# -*- coding: utf-8 -*-
""" Shapelet Transform Classifier
wrapper implementation of a shapelet transform classifier pipeline that
simply performs a (configurable) shapelet transform
then builds (by default) a random forest. This is a stripped down version
for basic usage

"""

__author__ = "Tony Bagnall"
__all__ = ["ShapeletTransformClassifier"]

import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.pipeline import Pipeline
from sklearn.utils.multiclass import class_distribution
from sktime.classification.base import BaseClassifier
from sktime.transformations.panel.shapelets import ContractedShapeletTransform
from sktime.utils.validation.panel import check_X, check_X_y


class ShapeletTransformClassifier(BaseClassifier):
    """Shapelet Transform Classifier
        Basic implementation along the lines of [1,2]

    Parameters
    ____________
    TO DO

    Attributes
    ----------
    TO DO

    Notes
    _____

    ..[1] Jon Hills wt al., "Classification of time series by
    shapelet transformation",
        Data Mining and Knowledge Discovery, 28(4), 851--881, 2014
    https://link.springer.com/article/10.1007/s10618-013-0322-1
    ..[2] A. Bostrom and A. Bagnall, "Binary Shapelet Transform
    for Multiclass Time Series Classification",
    Transactions on Large-Scale Data and Knowledge Centered
      Systems, 32, 2017
    https://link.springer.com/chapter/10.1007/978-3-319-22729-0_20
    Java Version
    https://github.com/uea-machine-learning/tsml/blob/master/src/main/
    java/tsml/classifiers/shapelet_based/ShapeletTransformClassifier.java


    """

    def __init__(self, time_contract_in_mins=300, n_estimators=500, random_state=None):
        self.time_contract_in_mins = time_contract_in_mins
        self.n_estimators = n_estimators
        self.random_state = random_state

        #        self.shapelet_transform=ContractedShapeletTransform(
        #        time_limit_in_mins=self.time_contract_in_mins, verbose=shouty)
        #        self.classifier=RandomForestClassifier(
        #        n_estimators=self.n_estimators,criterion="entropy")
        #        self.st_X=None;
        super(ShapeletTransformClassifier, self).__init__()

    def fit(self, X, y):
        """Perform a shapelet transform then builds a random forest.
        Contract default for ST is 5 hours
        ----------
        X : array-like or sparse matrix of shape = [n_instances,
        series_length] or shape = [n_instances,n_columns]
            The training input samples.  If a Pandas data frame is passed it
            must have a single column (i.e. univariate
            classification. RISE has no bespoke method for multivariate
            classification as yet.
        y : array-like, shape =  [n_instances]    The class labels.

        Returns
        -------
        self : object
        """
        X, y = check_X_y(X, y, enforce_univariate=True)

        # if y is a pd.series then convert to array.
        if isinstance(y, pd.Series):
            y = y.to_numpy()

        # generate pipeline in fit so that random state can be propogated properly.
        self.classifier_ = Pipeline(
            [
                (
                    "st",
                    ContractedShapeletTransform(
                        time_contract_in_mins=self.time_contract_in_mins,
                        verbose=False,
                        random_state=self.random_state,
                    ),
                ),
                (
                    "rf",
                    RandomForestClassifier(
                        n_estimators=self.n_estimators, random_state=self.random_state
                    ),
                ),
            ]
        )

        self.n_classes_ = np.unique(y).shape[0]
        self.classes_ = class_distribution(np.asarray(y).reshape(-1, 1))[0][0]

        self.classifier_.fit(X, y)

        self._is_fitted = True
        return self

    def predict(self, X):
        """
        Find predictions for all cases in X. Built on top of predict_proba
        Parameters
        ----------
        X : array-like or sparse matrix of shape = [n_samps, num_atts] or a
        data frame.
        If a Pandas data frame is passed,

        Returns
        -------
        output : array of shape = [n_samples]
        """
        X = check_X(X, enforce_univariate=True)
        self.check_is_fitted()

        return self.classifier_.predict(X)

    def predict_proba(self, X):
        """
        Find probability estimates for each class for all cases in X.
        Parameters
        ----------
        X : array-like or sparse matrix of shape = [n_instances, n_columns]
            The training input samples.  If a Pandas data frame is passed,

        Returns
        -------
        output : array of shape = [n_samples, num_classes] of probabilities
        """
        X = check_X(X, enforce_univariate=True)
        self.check_is_fitted()

        return self.classifier_.predict_proba(X)
