'''
Created on Oct 27, 2016

@author: bcraenen
'''
import sys
import numpy as np
from root.main.ArffHandler import Arffhandler
from sklearn.ensemble.forest import RandomForestClassifier
from root.other import ForestHandler
from numpy import float32
from sklearn.metrics.classification import accuracy_score

class KnowledgeFragmentHandler(object):
    
    def calc_sort(self, inputs, output, fragments, inputsMask):
        if self.criterion == 'gini':
            for e in fragments:
                e.calc_gini_impurity(inputs, output, inputsMask)
                fragments.sort(key=lambda x: (-x.gini_impurity, x.impurity))
        if self.criterion == 'entropy':
            for e in fragments:
                e.calc_entropy_impurity(inputs, output, inputsMask)
                fragments.sort(key=lambda x: (x.entropy_impurity, x.impurity))
                
    def select_delete(self, fragments):
        fragment = fragments[len(fragments)-1]
        del fragments[len(fragments)-1]
        return fragment
    
    def update_mask(self, fragment, inputs, inputsMask):
        for index in range(len(inputs)):
            if inputsMask[index] == True:
                if inputs[index][fragment.feature] < fragment.threshold:
                    inputsMask[index] = False
    
    def __init__(self):
        '''
        Constructor
        '''
        self.max_depth = 10
        self.max_length = 3
        # possible values: 'gini', 'entropy'
        #self.criterion = 'gini'
        self.criterion = 'entropy'
    
if __name__ == "__main__":
    print "Load Dataset"
    Arffhandler = Arffhandler()
    Arffhandler.Load(sys.argv[1])
    Arffhandler.OneHotEncode()
    inputs = Arffhandler.inputs
    output = Arffhandler.output
    print "Setup and fit RandomForest"
    randomForest = RandomForestClassifier(n_estimators=30, \
                                          criterion="gini", \
                                          max_features="auto", \
                                          max_depth=3, \
                                          min_samples_split=2, \
                                          min_samples_leaf=1, \
                                          min_weight_fraction_leaf=0, \
                                          max_leaf_nodes=None, \
                                          bootstrap=True, \
                                          oob_score=True, \
                                          n_jobs=1, \
                                          random_state=None, \
                                          verbose=0, \
                                          warm_start=False, \
                                          class_weight=None)
    randomForest.fit(inputs, output)
    print "score: %s " % (randomForest.score(inputs, output))
    print "Extract and unify Knowledge Fragments"
    interpreter = ForestHandler()
    interpreter.extract_fragments(randomForest)
    interpreter.uniquify()
    fragments = interpreter.test_fragments
    #for e in fragments:
    #    print e
    print "Setup knowledge fragment handler"        
    handler = KnowledgeFragmentHandler()
    print "Greedily build KF matrix"
    matrix = []
    for depth in range(handler.max_depth):
        matrix.append([])
    for depth in range(handler.max_depth):
        inputsMask = np.ones(len(inputs), dtype=bool)
        for length in range(handler.max_length):
            print "length=%s, depth=%s" % (length, depth)
            handler.calc_sort(inputs, output, fragments, inputsMask)
            fragment = handler.select_delete(fragments)
            print "current=%s" % (fragment)
            matrix[depth].append(fragment)
            handler.update_mask(fragment, inputs, inputsMask)
    print "Calculate predict array"
    predict = np.zeros(len(inputs), dtype=float32)
    for index in range(len(inputs)):
        for depth in range(handler.max_depth):
            isPredict = True
            for length in range(handler.max_length):
                fragment = matrix[depth][length]
                if inputs[index][fragment.feature] < fragment.threshold:
                    isPredict = False
                    break
            if isPredict == True:
                predict[index] = 1.0
                break
    print "score: %s" % accuracy_score(output, predict, normalize=True, sample_weight=None)
            
            