from .xaisuiteFoundation import*
import collections

class InsightGenerator:
  '''
  Class to generate insights based on explanation results. This was first released in version 2.0.0 and is a work in progress.

  :param collections.OrderedDict explanations: Local explanation results, as generated by the `ModelTrainer.getExplanationsFor()` function
  '''
  def __init__(self, explanations:collections.OrderedDict):
    self.explanations = explanations
    self.explainers = list(self.explanations.keys())
    self.explainers.remove('predict')
    self.num_instances = len(self.explanations.get(self.explainers[0] if len(self.explainers) != 0 else "No local explanations").get_explanations())

  def calculateExplainerSimilarity(self, explainer1:str, explainer2:str) -> float:
    '''
    Calculates explainer similarity based on the average Shreyan distance. 

    :param str explainer1: The name of the first explainer. Ex. "shap", "lime"
    :param str explainer2: The name of the second explainer
    '''

    sum = 0
    for i in range (0, self.num_instances):
      sum += self.getShreyanDistance(self.explanations.get(explainer1).get_explanations()[i].get("features"), self.explanations.get(explainer2).get_explanations()[i].get("features"))
    return sum / self.num_instances
    

  def getShreyanDistance(self, vec1:list, vec2:list) -> float:
    '''
    Calculate the distance between two ordered vectors. 1 is the max distance, 0 is no distance

    :param list vec1: The pattern vector
    :param list vec2: The disorder vector
    :returns float shreyan_distance: The Shreyan distance between the two ordered vectors
    '''

    assert (len(vec1) == len(vec2)), "Cannot find the Shreyan distance when ordered vectors are of different lengths."

    x = len(vec1)
    max = self.__sigma(0, int((x/2) - 1), lambda i: ((x - 2*i - 1) * (2*x - 4*i - 1) / (x**2))) #This is the maximum possible Shreyan distance between two ordered vectors of size x

    enumerated_elements = {index + 1:value for index, value in enumerate(vec1)}

    key_list = list(enumerated_elements.keys())
    val_list = list(enumerated_elements.values())
 
    enumerated_vec2 = []
    for element in vec2:
      enumerated_vec2.append(key_list[val_list.index(element)])

    shreyan_distance = 0.0

    for j in range(1, x+1):
      shreyan_distance += (x - j) * abs(j - enumerated_vec2[j])
    
    shreyan_distance /= (x**2)
    shreyan_distance /= max
    shreyan_distance = 1 - shreyan_distance
    return shreyan_distance    

  def __sigma(self, start, end, expression):
    return sum(expression(i) for i in range(start, end+1))

  

    
    
    
      

