import os
import ast
import csv
import numpy as np
import multiprocessing

from classes.concept import Concept
from classes.feature_vector import FeatureVector


def knn(training = os.path.abspath(os.path.join(__file__, "..", "training.csv")), testing = os.path.abspath(os.path.join(__file__, "..", "testing.csv")), k_param = 3):
    from classes.learner import Learner

    testing_data = []
    with open(testing, 'r') as csv_file:
        reader = csv.reader(csv_file, delimiter=";")
        next(reader)
        for row in reader:
            testing_data.append((row[1], row[2]))

    learner = Learner(k_paramater=k_param)
    learner.learn(training)

    cnt = 0

    for test_feature_vector in testing_data:
        distances = learner.classify(ast.literal_eval(test_feature_vector[1]))
        cnt += learner.analyse(distances, Concept.identify_by_str(test_feature_vector[0].split('.')[1]))

    return [cnt, len(testing_data) - cnt, len(testing_data)] # richtig, falsch, amount of testing data


def decision_tree(training = os.path.abspath(os.path.join(__file__, "..", "training.csv")), testing = os.path.abspath(os.path.join(__file__, "..", "testing.csv")), cycles = 3):
    from classes.decision_tree import DecisionTree

    training_data = []
    with open(training, 'r') as csv_file:
        reader = csv.reader(csv_file, delimiter=";")
        next(reader)
        for row in reader:
            fv = FeatureVector(concept = Concept.identify_by_str(row[1].split(".")[1]), features_list = ast.literal_eval(row[2]), loaded = True)
            training_data.append(fv)

    tree = DecisionTree(children={}, cycles=cycles)
    tree.learn(training_data)

    cnt_data = 0
    cnt_correct = 0
    with open(testing, 'r') as csv_file:
        reader = csv.reader(csv_file, delimiter=";")
        next(reader)
        for row in reader:
            cnt_data += 1
            fv = FeatureVector(concept = Concept.identify_by_str(row[1].split(".")[1]), features_list = ast.literal_eval(row[2]), loaded = True)
            classified_concept = tree.classify(fv)
            cnt_correct += 1 if fv.concept == classified_concept else 0
            # print(f"{fv.concept} was classified as {classified_concept}")

    # print(f"classified {cnt_correct}/{cnt_data} correctly  ({round(cnt_correct/cnt_data*100, 3)}%)")
    return [cnt_correct, cnt_data - cnt_correct, cnt_data]  # richtig, falsch, amount of testing data

def neural_network(training = os.path.abspath(os.path.join(__file__, "..", "training.csv")), testing = os.path.abspath(os.path.join(__file__, "..", "testing.csv")), num_training_vectors = 100):
    from classes.neural.network import NeuralNetwork
    from classes.neural.neuron import Neuron

    training_data = []
    with open(training, 'r') as csv_file:
        reader = csv.reader(csv_file, delimiter=";")
        next(reader)
        i = 0
        for row in reader:
            if i > num_training_vectors:
                break
            i += 1

            float_list = ast.literal_eval(row[3])

            fv = FeatureVector(concept = Concept.identify_by_str(row[2].split(".")[1]), features_list = [element * 0.01 for element in float_list], loaded = True)
            training_data.append(fv)
    
    while len(training_data) < num_training_vectors:
        training_data += training_data
    training_data = training_data[:num_training_vectors+1]

    nn = NeuralNetwork()
    nn.learn(training_data)

    cnt_data = 0
    cnt_correct = 0
    with open(testing, 'r') as csv_file:
        reader = csv.reader(csv_file, delimiter=";")
        next(reader)
        for row in reader:
            cnt_data += 1
            fv = FeatureVector(concept = Concept.identify_by_str(row[2].split(".")[1]), features_list = ast.literal_eval(row[3]), loaded = True)
            classified_concept, _ = nn.classify(fv)
            cnt_correct += 1 if fv.concept == classified_concept else 0
            # print(f"{fv.concept} was classified as {classified_concept}")

    #print(f"classified {cnt_correct}/{cnt_data} correctly  ({round(cnt_correct/cnt_data*100, 3)}%)")
    return [cnt_correct, cnt_data - cnt_correct, cnt_data]  # richtig, falsch, amount of testing data

def repeat_n_times(n: int, num_training_vectors: int):
    for _ in range(n):
        cnt_correct = 0
        cnt_data = 0
        result = neural_network(num_training_vectors=num_training_vectors)
        cnt_correct += result[0]
        cnt_data += result[2]
    print(f"{num_training_vectors}: classified {cnt_correct}/{cnt_data} correctly  ({round(cnt_correct/cnt_data*100, 3)}%)")

if __name__ == "__main__":
    for num_training_vectors in range(100, 1000, 100):
        multiprocessing.Process(target=repeat_n_times, args=(100, num_training_vectors, )).start()
