# -*- coding: utf-8 -*-

# this script is called by the participants
# in order to train and test a model in cross-validation setting
# This is the development script


from __future__ import division
from __future__ import unicode_literals
from data_reader import DataReader
from feature_extractor import FeatureExtractor
from trainer import NBTrainer, DTTrainer
import requests
import os, sys
import random

# creates path for log files, if not already existent
if not os.path.isdir("logs"):
    os.makedirs("logs")

if len(sys.argv) != 2:
    sys.exit('\nBeim Aufruf von train.py ist nur genau 1 Parameter zulässig.\n' +
             'Beispielaufruf:\n' +
             'python train.py ../data/Parzival_train.tsv\n')


# THIS IS WHERE THE INPUT IS DEFINED AND READ #

# calls a function from DataReader
# reads in the annotated corpus
#
# change the path to the desired input corpus here:
#corpus = DataReader("../data/Parzival_train.tsv").read_corpus()
# ... on Windows this looks slightly different:
#corpus = DataReader("..\data\Parzival_train.tsv").read_corpus()
# ... platform-independent:
#corpus = DataReader(os.path.join('..','data','Parzival_train.tsv')).read_corpus()

corpus = DataReader(sys.argv[1]).read_corpus()


# THIS IS WHERE THE FEATURES ARE EXTRACTED #

# The annotated corpus is sent to the FeatureExtractor -
# several feature extraction functions are called there
# In order to change which features are used:
# open feature_extractor.py and change the function calls
tokens_with_features = FeatureExtractor().extract_features(corpus)


# THIS IS WHERE THE MACHINE LEARNING STARTS #

# before the first training we don't have a best accuracy value,
# for comparing and storing the best acc so far, set to a starting value
acc_best = 0
# initialize accuracy sum (for calculating accuracy average)
acc_sum = 0
acc_base_sum = 0

# train in cross validation,
# test if the accuracy is higher then before,
# if yes, save the classifier and overwrite the accuracy

# determine crossvalidation set sizes
num_folds = 3
subset_size = int(len(tokens_with_features)/num_folds)

# for random partition of data: shift start of data 
#randstart = 0   ## TEST
randstart = random.randrange(subset_size)
shiftidx = list(range(randstart, len(tokens_with_features))) + list(range(randstart))

print("\n=== Classifier Training in " + str(num_folds) + "-fold crossvalidation setting ===")

# train one classifier for each crossvalidation fold
for n in range(num_folds):
    print("Train fold number " + str(n+1))
    # make crossvalidation set test and train
    testcv = [tokens_with_features[i] for i in shiftidx[n*subset_size:]][:subset_size]
    traincv = [tokens_with_features[i] for i in shiftidx[(n+1)*subset_size:]] \
        + [tokens_with_features[i] for i in shiftidx[:n*subset_size]]

    # THIS IS WHERE YOU CAN CHANGE THE ML ALGORITHM #

    # DTTrainer is the trainer for a Decision Tree classifier
    # NBTrainer is the trainer for a Naive Bayes classifier
    # change this line for another ML algorithm (remove the # in front of a line to uncomment)
    #
    trainer = DTTrainer(traincv)
    #trainer = NBTrainer(traincv)
    
    # calculate the accuracy of the resulting algorithm
    acc, acc_base = trainer.return_accuracy(testcv)
    
    print("  " + '{:<8.5f}'.format(acc) + "- accuracy of this classifier")
    print("  " + '{:<8.5f}'.format(acc_base) + "- baseline accuracy (all words labeled as non-entity)")

    #print(trainer.classifier)
    #print(trainer.classifier.pretty_format())
    #print(trainer.classifier.pseudocode())

    # prepare calculating average
    acc_sum += acc
    acc_base_sum += acc_base

    ## TEST
    #trainer.analyze_classifier(trainer.classifier, testcv)
    
    # if the accuracy is higher than for the previous classifiers, store the best classifier
    if acc > acc_best:
        # store the accuracy value as currently best accuracy
        acc_best = acc
        # best classifier is the current classifier
        classifier = trainer.classifier
        besttrainer = trainer
        # keep the test set that belonged to this crossvalidation fold
        testset = testcv

# accuracy average info
print("\n" + '{:<8.5f}'.format(acc_sum/num_folds) + " - average accuracy")
print('{:<8.5f}'.format(acc_base_sum/num_folds) + " - average baseline accuracy")

# give detailed analysis of the best classifier
besttrainer.analyze_classifier(classifier, testset)


