"""
Coming...
"""

__author__ = "CodingLive"
__email__ = "rootcode@duck.com"
__status__ = "planning"

import numpy as np
from sklearn.metrics import accuracy_score
import argparse
import pandas as pd
from sklearn.datasets import load_breast_cancer, load_iris
from sklearn.tree import DecisionTreeClassifier as SklearnDecisionTreeClassifier
from sklearn.tree import export_graphviz
from sklearn.utils import Bunch
from sklearn import datasets
from sklearn.model_selection import train_test_split
from io import StringIO  # for StringIO


def gini_imp(y):
    if isinstance(y, pd.Series):
        p = y.value_counts() / y.shape[0]
        gini = 1 - np.sum(p ** 2)
        return (gini)
    else:
        raise ("Must be pandas object")


def entropy_imp(y):
    if isinstance(y, pd.Series):
        a = y.value_counts() / y.shape[0]
        entropy = np.sum(-a * np.log2(a + 1e-9))
        return entropy
    else:
        raise ("Must be pandas object")


def best_criterion(y):
    gini = gini_imp(y)
    ent = entropy_imp(y)

    if ent > gini:
        criterion = "entropy"
    else:
        criterion = "gini"
    return criterion


def best_depth(X, y, criterion="gini"):
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)
    l = 0
    d = 0
    #max_depth = 1

    for i in range(1, 500):
        max_depth = i

        clf2 = SklearnDecisionTreeClassifier(criterion=criterion, max_depth=max_depth)
        clf2.fit(X_train, y_train)

        pred2 = clf2.predict(X_test)
        acc = accuracy_score(pred2, y_test)
        if acc > l:
            l = acc
            d = max_depth
    return d


def best_splitter(X, y, depths, criterion="gini"):
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)

    # best
    clf2 = SklearnDecisionTreeClassifier(criterion=criterion, max_depth=depths, splitter="best")
    clf2.fit(X_train, y_train)

    pred2 = clf2.predict(X_test)
    acc = accuracy_score(pred2, y_test)

    # random
    clf2 = SklearnDecisionTreeClassifier(criterion=criterion, max_depth=depths, splitter="random")
    clf2.fit(X_train, y_train)

    pred2 = clf2.predict(X_test)
    acc2 = accuracy_score(pred2, y_test)
    #

    if acc > acc2:
        return "best"
    else:
        return "random"


def best_criterion_norm(X, y):
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)

    a = 0
    b = 0

    for i in range(1, 101):
        # gini
        clf2 = SklearnDecisionTreeClassifier(criterion="gini", max_depth=i)
        clf2.fit(X_train, y_train)

        pred2 = clf2.predict(X_test)
        acc = accuracy_score(pred2, y_test)

        # entropy
        clf2 = SklearnDecisionTreeClassifier(criterion="entropy", max_depth=i)
        clf2.fit(X_train, y_train)

        pred2 = clf2.predict(X_test)
        acc2 = accuracy_score(pred2, y_test)

        if acc > acc2:
            a += 1
        else:
            b += 1
    if a > b:
        return "gini"
    else:
        return "entropy"


def best_state(X, y, depth, criterion="gini"):
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)
    state = 0
    ac = 0
    for i in range(100):
        clf = SklearnDecisionTreeClassifier(criterion=criterion, max_depth=depth, random_state=i)
        clf.fit(X_train, y_train)
        pred = clf.predict(X_test)
        acc = accuracy_score(pred, y_test)

        if acc > ac:
            state = i
            ac = acc
    # print(acc)
    # print(state)
    return state


def best_all(X, y):
    if isinstance(y, pd.Series):
        # Getting the best criterion
        criterion = best_criterion(y)

        # Getting best depth value
        depth = best_depth(X, y, criterion=criterion)

        # Getting best splitter
        split = best_splitter(X, y, depth, criterion=criterion)

        return criterion, depth, split
    else:
        # Getting the best criterion
        criterion = best_criterion_norm(X, y)

        # Getting best depth value
        depth = best_depth(X, y, criterion=criterion)

        # Getting best splitter
        split = best_splitter(X, y, depth, criterion=criterion)

        return criterion, depth, split