import numpy as np
from sklearn.cluster import MeanShift, estimate_bandwidth
from scipy.spatial.distance import cityblock
from sklearn.utils import check_array
from .base import BaseThresholder
from .thresh_utility import normalize, cut, gen_kde


class SHIFT(BaseThresholder):
    """SHIFT class for Mean Shift clustering thresholder.

       Use the mean shift clustering to evaluate a non-parametric means to
       threshold scores generated by the decision_scores where outliers
       are set to any value beyond the minimum labeled outlier

       Paramaters
       ----------

       Attributes
       ----------

       eval_: numpy array of binary labels of the training data. 0 stands
           for inliers and 1 for outliers/anomalies.

    """

    def __init__(self):

        super(SHIFT, self).__init__()

    def eval(self, decision):
        """Outlier/inlier evaluation process for decision scores.

        Parameters
        ----------
        decision : np.array or list of shape (n_samples)
                   which are the decision scores from a
                   outlier detection.
        
        Returns
        -------
        outlier_labels : numpy array of shape (n_samples,)
            For each observation, tells whether or not
            it should be considered as an outlier according to the
            fitted model. 0 stands for inliers and 1 for outliers.
        """

        decision = check_array(decision, ensure_2d=False)

        decision = normalize(decision)

        # Get quantile value for bandwidth estimation
        q = cityblock(decision, np.sort(decision))/np.sum(decision)
        
        if q>1.0: q=1.0

        decision = decision.reshape(-1,1)
        
        # Estimate bandwidth
        try:
            bw = estimate_bandwidth(decision, quantile=q)
        except ValueError:
            bw = estimate_bandwidth(decision)

        # Apply mean shift and get labels
        if bw!=0:
            ms = MeanShift(bandwidth=bw, cluster_all=False, max_iter=500)
        else:
            ms = MeanShift(bandwidth=None, cluster_all=False, max_iter=500)

        ms.fit(decision)
        labels = ms.labels_

        # Set limit to smallest outlier label
        idx = np.where(labels==-1)

        if len(idx[0])!=0:  
            limit = np.min(decision[idx])
        else:
            limit = 1.1

        self.thresh_ = limit 

        return cut(np.squeeze(decision), limit)

