import functools
import os

import numpy as np
import pygmo as pg

from simulation import simulate, statistical
from solving import value_function_list
from util import constants as cs, param_type


class HumanCapitalSearchProblem(object):
    def fitness(self, params_nparray, gradient_eval=False):
        params_paramtype = param_type.transform_array_to_paramstype(params_nparray)
        optimal_value_functions = value_function_list.backwards_iterate(params_paramtype)
        # if display_plots:
        #     plotting.plot_valuefunctions(optimal_value_functions)
        panel_data = simulate.simulate_data(params_paramtype, optimal_value_functions)
        # if save_panel_data:
        #     np.savetxt('simulated_data.csv', panel_data.T, delimiter=',')
        simulated_coefficients, _, _, _ = statistical.calculate_coefficients(panel_data)
        criterion_value = calculate_criterion(
                simulated_coefficients, cs.data_coeffs, cs.weights)
        if not gradient_eval:
            

        return np.array([criterion_value])

    def get_name(self):
        return 'Human Capital Search Problem'

    def get_bounds(self):
        lowerbounds, upperbounds, _ = param_type.gen_initial_point()
        return (lowerbounds, upperbounds)

    def gradient(self, x):
        grad_fitness = functools.partial(self.fitness, gradient_eval=True)
        return pg.estimate_gradient(grad_fitness, x)

def calculate_criterion(simulate_coefficients,
                        data_coefficients, weights):
    cutoff = 155
    try:
        squared_coeff_diffs = (data_coefficients[:cutoff]
                       - simulate_coefficients[:cutoff])**2
        return np.sum(squared_coeff_diffs * weights[:cutoff]**2)
    except ValueError:
        return 1000000.0
    return None


def convert_hours_to_seconds(hours):
    seconds = 60 * 60 * hours
    return int(seconds)

if __name__ == '__main__':
    x0 = np.array([  3.50002199e-03,   6.51848176e-03,   1.51129690e-02,
         5.44408669e-01,   4.00993663e-01,   6.55844833e-02,
         6.07802957e+00,   1.60167206e+00,   5.01869425e+00,
         4.72961572e+00,   9.38466921e+00,   5.05588161e+00,
         8.19033636e+00,   2.75929445e+00,   2.85635433e+00,
         1.75737616e+00,   7.80585097e-01,   7.75955256e-01,
         7.84082645e-01,   7.84472240e-01,   7.88595353e-01,
         7.56837829e-01,   7.95899147e-01,   1.00607895e-01,
         9.54173933e-02,   1.01830970e-01,   2.35455817e-01,
         4.34618429e-01,   5.05177886e-01,   4.97754216e-01,
         1.33424724e+00,   1.33335481e+00,   5.14048248e-01,
         5.31256998e-01,   2.72639929e-02,   2.06973106e-01,
         7.44039604e-01,   3.35103286e-01,   8.64058736e-01,
         2.01314260e-01,   7.48161453e-01,   1.98923666e-01,
         7.49378943e-01,   1.96135026e-01,   7.52297629e-01,
         2.03848678e-01,   7.48561095e-01,   1.98618489e-01,
         9.17364498e-01,   1.97851509e-01,   7.42171336e-01,
         1.98302575e-01,   7.41711271e-01]) #array([  149.86359966])
    hc_problem = HumanCapitalSearchProblem()
    hc_problem.fitness(x0)

    np.set_printoptions(threshold=10000)
    np.random.seed(128)

    prob = pg.problem(hc_problem)
    pop = pg.population(prob=prob, size=0)
    pop.push_back(x0, f=[149.86359966])

    archi = pg.archipelago(pop=pop)
    methods = ["bobyqa", 'neldermead', 'praxis', "newuoa_bound", "sbplx"]
    algo_list = []
    for method in methods:
        nl= pg.nlopt(method)
        nl.stopval = 1e-8
        nl.ftol_rel = 1e-8
        nl.xtol_rel = 1e-8
        nl.xtol_abs = 1e-8
        nl.ftol_abs = 1e-8
        nl.maxtime = convert_hours_to_seconds(12)
        algo = pg.algorithm(nl)
        archi.push_back(algo=algo, pop=pop)
    print(archi)
    archi.evolve()

    import time
    while archi.status is pg.core._evolve_status.busy:
        time.sleep(120)
        print(archi)
    archi.wait_check()
    print(archi.get_champions_x())
    print(archi.get_champions_f())

print('within_val {0}:'.format(os.getpid()), repr(params_nparray), repr(np.array([criterion_value])))