import math
import numpy as np
np.seterr(divide='ignore',invalid='ignore')
from numpy import corrcoef
from features import Type

class Chart(object):
    bar=0
    line=1
    scatter=2
    pie=3
    chart=['bar','line','scatter','pie']

class View(object):
    def __init__(self,table,x_id,y_id,z_id,series_num,X,Y,chart):
        self.table=table
        self.fx=table.features[x_id]
        self.fy=table.features[y_id]
        self.x_name=self.fx.name
        self.y_name=self.fy.name
        self.z_id=z_id
        self.series_num=series_num
        self.X=X
        self.Y=Y
        self.chart=chart
        self.tuple_num=table.tuple_num
        self.score_l=0 #learning_to_rank score
        self.M=self.Q=self.W=self.score=0 # partial and div_ranking score
        self.getM()
        self.getQ()


#### function for learning to rank
    def getCorrelation_l(self,series_id):
        if self.fx.type==Type.categorical:
            return 0
        if self.fx.type==Type.temporal:
            data1=[i for i in range(self.tuple_num/self.series_num)]
        else:
            data1=self.X[series_id]
        data2=self.Y[series_id]
        log_data1=log_data2=[]
        if self.fx.type!=Type.temporal and self.fx.min>0:
            log_data1=map(math.log,data1)
        if self.fy.min>0:
            log_data2=map(math.log,data2)
        # linear
        result = abs(corrcoef(data1, data2)[0][1])

        # exponential
        if log_data2:
            r = abs(corrcoef(data1, log_data2)[0][1])
            if r > result:
                result = r

        # logarithm
        if log_data1:
            r = abs(corrcoef(log_data1, data2)[0][1])
            if r > result:
                result = r

        # power
        if log_data1 and log_data2:
            r = abs(corrcoef(log_data1, log_data2)[0][1])
            if r > result:
                result = r

        return result

    def output_score(self):
        correlation = max([self.getCorrelation_l(i) for i in range(self.series_num)])
        if self.fx.type == Type.temporal:
            return '1 qid:1 1:' + str(self.fx.type) + ' 2:' + str(self.fy.type) + ' 3:' + str(
                self.tuple_num) + ' 4:' + str(self.tuple_num) + ' 5:0 6:' + str(self.fy.min) + ' 7:0 8:' + str(
                self.fy.max) + ' 9:' + str(self.fx.distinct) + ' 10:' + str(self.fy.distinct) + ' 11:' + str(
                self.fx.ratio) + ' 12:' + str(self.fy.ratio) + ' 13:' + str(correlation) + ' 14:' + str(self.chart)
        else:
            return '1 qid:1 1:' + str(self.fx.type) + ' 2:' + str(self.fy.type) + ' 3:' + str(
                self.tuple_num) + ' 4:' + str(self.tuple_num) + ' 5:' + str(self.fx.min) + ' 6:' + str(
                self.fy.min) + ' 7:' + str(self.fx.max) + ' 8:' + str(self.fy.max) + ' 9:' + str(
                self.fx.distinct) + ' 10:' + str(self.fy.distinct) + ' 11:' + str(self.fx.ratio) + ' 12:' + str(
                self.fy.ratio) + ' 13:' + str(correlation) + ' 14:' + str(self.chart)

#### function for partial order and diversified ranking 
    def getCorrelation(self,series_id):
        if self.fx.type==Type.temporal:
            data1=[i for i in range(self.tuple_num/self.series_num)]
        else:
            data1=self.X[series_id]
        data2=self.Y[series_id]
        log_data1=log_data2=[]
        if self.fx.type!=Type.temporal and self.fx.min>0:
            log_data1=map(math.log,data1)
        if self.fy.minmin>0:
            log_data2=map(math.log,data2)
        
        # linear
        try:
            result = abs(corrcoef(data1, data2)[0][1])
        except Exception as e:
            # print "linear"
            result = 0;
        else:
            pass
        

        # exponential
        if log_data2:
            try:
                r = abs(corrcoef(data1, log_data2)[0][1])
                if r > result:
                    result = r
            except Exception as e:
                # print "exponential"
                result = 0;
            else:
                pass
            

        # logarithm
        if log_data1:
            try:
                r = abs(corrcoef(log_data1, data2)[0][1])
                if r > result:
                    result = r
            except Exception as e:
                # print "logarithm"
                result = 0;
            else:
                pass
            

        # power
        if log_data1 and log_data2:
            try:
                r = abs(corrcoef(log_data1, log_data2)[0][1])
                if r > result:
                    result = r
            except Exception as e:
                # print "power"
                result = 0;
            else:
                pass

        return result

    def getM(self):
        if self.chart==Chart.pie:
            if self.tuple_num==1:
                self.M=0
            elif 2<=self.tuple_num<=10:
                sumY=sum(self.Y[0])
                self.M=reduce(lambda x,y:x+y,map(lambda y:-(1.0*y/sumY)*math.log(1.0*y/sumY),self.Y[0]))
            elif self.tuple_num>10:
                sumY=sum(self.Y[0])
                self.M=reduce(lambda x,y:x+y,map(lambda y:-(1.0*y/sumY)*math.log(1.0*y/sumY),self.Y[0]))*10.0/self.tuple_num
        elif self.chart==Chart.bar:
            if self.tuple_num==1:
                self.M=0
            elif 2<=self.tuple_num<=20:
                self.M=(max(self.Y[0])-min(self.Y[0]))/(sum(self.Y[0])/float(self.tuple_num/self.series_num))
            else:
                self.M=10.0/self.tuple_num*((max(self.Y[0])-min(self.Y[0]))/(sum(self.Y[0])/float(self.tuple_num/self.series_num)))
        elif self.chart==Chart.scatter:
            if self.series_num==1:
                self.M=self.getCorrelation(0)
            else:
                self.M=max([self.getCorrelation(i) for i in range(self.series_num)])
        else:
            if self.series_num==1:
                if self.getCorrelation(0)>0.3:
                    self.M=1
                else:
                    self.M=0
            else:
                if max([self.getCorrelation(i) for i in range(self.series_num)])>0.3:
                    self.M=1
                else:
                    self.M=0

    def getQ(self):
        self.Q=1
        if self.chart==Chart.bar or self.chart==Chart.pie:
            self.Q=1-1.0*(self.tuple_num/self.series_num)/self.table.instance.tuple_num


#### function for all
    def output(self,order):
        classify = str([])
        if self.series_num > 1:
            classify = str([v[0] for v in self.table.classes]).replace("u'", '\'').decode("unicode-escape").replace("'",'"')
        x_data = str(self.X)
        if self.fx.type == Type.numerical:
            x_data = str(self.X).replace("'", '"').replace('L', '')
        elif self.fx.type == Type.categorical:
            x_data = str(self.X).replace("u'", '\'').decode("unicode-escape").replace("'", '"')
        else:
            len_x = len(self.X)
            x_data = '[' + reduce(lambda s1, s2: s1 + s2, [str(map(str, self.X[i])) for i in range(len_x)]).replace("'",'"') + ']'
        y_data = str(self.Y)
        if self.fy.type == Type.numerical:
            y_data = y_data.replace('L', '')
        data ='{"order1":' + str(order) + ',"order2":' + str(1) +  ',"describe":"' + self.table.describe + '","x_name":"' + self.fx.name + '","y_name":"' + self.fy.name + '","chart":"' + Chart.chart[self.chart] + '","classify":' + classify + ',"x_data":' + x_data + ',"y_data":' + y_data + '}'
        #data = 'score:' + str(round(self.score, 2)) + '\tM:' + str(round(self.M, 2)) + '\tQ:' + str(round(self.Q, 2)) + '\tW:' + str(round(self.W, 2)) + '{"order":' + str(order) + ',"describe":"' + self.table.describe + '","x_name":"' + self.fx.name + '","y_name":"' + self.fy.name + '","chart":"' + Chart.chart[self.chart] + '","classify":' + classify + ',"x_data":' + x_data + ',"y_data":' + y_data + '}'
        return data

