#!/usr/bin/env python3

import os

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.svm import SVR

import evaluation
import models
import parse
import preprocessing


def print_results(dataset, model, model_type, k):
    """Evaluate model, print result and model's parameters.

    :param dataset: dataset for evaluation
    :param model: model to be evaluated
    :param model_type: string describing model - "SVR", "NN", "ONES"
    :param k: model's input size
    """
    result = evaluation.evaluate(model, dataset, k, log=True)
    print("{} Mean: {}".format(model_type, result))

    try: #SVR parameters
        p = model.get_params()
        print("Best SVR parameters: C={}, epsilon={}, gamma={}, kernel={}"\
              .format(p['estimator__C'],
                      p['estimator__epsilon'],
                      p['estimator__gamma'],
                      p['estimator__kernel']))
    except:
        pass

    try: #Neural network parameters
        p = model.get_config()
        layers = len(p['layers'])//2
        units = p['layers'][0]['config']['units']//layers
        activation = p['layers'][0]['config']['activation']
        print("Best NN parameters: hidden_layers={}, neurons={}, activation={}"\
              .format(layers, units, activation))
    except:
        pass

    print()


def main():
    """Run the experiment.

    The entire machine learning process from preprocessing datasets to
    training models to evaluation.
    """

    print("Initializing..")

    np.random.seed(1)
    k=4 #number of records per one measurement to consider

    # Filepaths
    cz_cells_path  = "cells/230.csv"
    sk_cells_path  = "cells/231.csv"
    my_data_path   = "data/my_data.csv"
    brno_data_path = "data/cell_data.csv"
    sk_data_path   = "data/sk_cell_data.csv"

    # Create folder for temporary files.
    if not os.path.exists("./tmp/"):
        os.makedirs("tmp")

    # Load cell tower databases, also available at https://opencellid.org/
    cz_cells = pd.read_csv(cz_cells_path)
    sk_cells = pd.read_csv(sk_cells_path)

    # Parse raw data and load them into pandas DataFrames.
    my_data   = pd.read_csv(parse.parse(my_data_path))
    brno_data = pd.read_csv(parse.parse(brno_data_path))
    sk_data   = pd.read_csv(parse.parse(sk_data_path))

    # Separate data into three distinct regions - city center, residential and
    # village.
    brno_data["meas_id"] = brno_data.meas_id + my_data.meas_id.max()
    center_data = pd.concat([my_data[my_data.meas_lat < 49.1980511], brno_data])

    residential_data = my_data[my_data.meas_lat > 49.2002033]

    village_data = sk_data

    # Preprocess data to prepare them for training.
    print("Preparing datasets..")
    center      = preprocessing.process(center_data, cz_cells, k)
    center1     = preprocessing.process(center_data, cz_cells, k=1)
    residential = preprocessing.process(residential_data, cz_cells, k)
    village     = preprocessing.process(village_data, sk_cells, k)

    # Split datasets into training and testing subsets.
    center_train, center_test     = train_test_split(center, test_size=0.2)
    center1_train, center1_test   = train_test_split(center1, test_size=0.2)
    resid_train, resid_test       = train_test_split(residential, test_size=0.2)

    # Do a randomized search of hyperparameters for an SVR and a Neural Network
    # model. Train and evaluate the best performing one on different
    # combinations of datasets. Compare with a naive model.

    naive_model = models.Ones()

    print("Training on city center subset..")
    svr_center = models.train_svr(center_train, k)
    nn_center = models.train_nn(center_train, k)
    print("Evaluating on city center..")
    print_results(center_test, svr_center, "SVR", k)
    print_results(center_test, nn_center, "NN", k)
    print_results(center_test, naive_model, "ONE", k)


    print("Training on city center..")
    svr_center_all = models.train_svr(center, k)
    nn_center_all = models.train_nn(center, k)
    print("Evaluating on residential..")
    print_results(residential, svr_center_all, "SVR", k)
    print_results(residential, nn_center_all, "NN", k)
    print_results(residential, naive_model, "ONE", k)
    print("Evaluating on village..")
    print_results(village, svr_center_all, "SVR", k)
    print_results(village, nn_center_all, "NN", k)
    print_results(village, naive_model, "ONE", k)


    print("Training on residential subset..")
    svr_resid = models.train_svr(resid_train, k)
    nn_resid = models.train_nn(resid_train, k)
    print("Evaluating on residential..")
    print_results(resid_test, svr_resid, "SVR", k)
    print_results(resid_test, nn_resid, "NN", k)
    print_results(resid_test, naive_model, "ONE", k)


    print("Training on residential..")
    svr_resid_all = models.train_svr(residential, k)
    nn_resid_all = models.train_nn(residential, k)
    print("Evaluating on city center..")
    print_results(center, svr_resid_all, "SVR", k)
    print_results(center, nn_resid_all, "NN", k)
    print_results(center, naive_model, "ONE", k)


    print("Training on city center (k=1 closest)..")
    svr_center_1 = models.train_svr(center1_train, k=1)
    nn_center_1 = models.train_nn(center1_train, k=1)
    print("Evaluating on city center..")
    print_results(center_test, svr_center_1, "SVR", k=1)
    print_results(center_test, nn_center_1, "NN", k=1)
    print_results(center_test, naive_model, "ONE", k=1)


if __name__ == "__main__":
    main()
