import json
import numpy as np


class NumpyEncoder(json.JSONEncoder):
    """ Special json encoder for numpy types """
    def default(self, obj):
        if isinstance(obj, (np.int_, np.intc, np.intp, np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16,
                            np.uint32, np.uint64)):
            return int(obj)
        elif isinstance(obj, (np.float_, np.float16, np.float32, np.float64)):
            return float(obj)
        elif isinstance(obj, (np.ndarray,)): #### This is the fix
            return obj.tolist()
        return json.JSONEncoder.default(self, obj)


class RecolectorResultados:

    def __init__(self, resultados_experimento):
        self.diccionario_resultados = dict()
        self.diccionario_dataframes = dict()
        self.diccionario_mejores_f = dict()
        self.diccionario_indices_mejores_f = dict()
        self.diccionario_contadores_llamados_f = dict()
        self.diccionario_total_iteraciones = dict()

        for i in range(len(resultados_experimento)):
            resultados_experimento[i] = list(resultados_experimento[i])

        for lista_resultados in resultados_experimento:

            parametros_experimento = str(list(lista_resultados[0].values()))

            del(lista_resultados[0])

            if parametros_experimento in self.diccionario_resultados:
                self.diccionario_resultados[parametros_experimento].append(lista_resultados)
            else:
                self.diccionario_resultados[parametros_experimento] = [lista_resultados]

        self.__procesar_resultados__()

    def __procesar_resultados__(self):
        import pandas as pd
        for parametros_experimento, resultados_experimento in self.diccionario_resultados.items():
            diccionario_dataframe_un_parametro = dict()
            dataframe_prueba = resultados_experimento[0][1]
            lista_mejores_resultados = []
            lista_indices_mejores_resultados = []
            lista_contadores_f = []
            lista_total_iteraciones = []
            for columna in dataframe_prueba.columns.tolist():
                diccionario_dataframe_un_parametro[columna] = pd.DataFrame()
            for resultado in resultados_experimento:
                diccionario_resultados_finales = resultado[0]
                lista_mejores_resultados.append(diccionario_resultados_finales['f_mejor_solucion'])
                lista_indices_mejores_resultados.append(diccionario_resultados_finales['iteracion_mejor_solucion'])
                lista_contadores_f.append(diccionario_resultados_finales['total_llamados_funcion'])
                lista_total_iteraciones.append(diccionario_resultados_finales['total_iteraciones'])
                dataframe_resultado = resultado[1]
                for columna in dataframe_resultado.columns.tolist():
                    diccionario_dataframe_un_parametro[columna] = \
                        diccionario_dataframe_un_parametro[columna].append(dataframe_resultado[columna])
            self.diccionario_mejores_f[parametros_experimento] = lista_mejores_resultados
            self.diccionario_indices_mejores_f[parametros_experimento] = lista_indices_mejores_resultados
            self.diccionario_dataframes[parametros_experimento] = diccionario_dataframe_un_parametro
            self.diccionario_contadores_llamados_f[parametros_experimento] = lista_contadores_f
            self.diccionario_total_iteraciones[parametros_experimento] = lista_total_iteraciones

    def __str__(self):

        string = "Cantidad de bloque de parámetros: " + str(len(self.diccionario_resultados))

        for bloque, resultado in self.diccionario_resultados.items():
            string = string + "\nBloque: "+str(bloque) + "\n --------------------------------------------------- \n"\
                     + str(resultado)+"\n"

        for bloque, resultado in self.diccionario_dataframes.items():
            string = string + "\nBloque: " + str(bloque) + "\n --------------------------------------------------- \n" \
                     + str(resultado) + "\n"

        return string

    def generar_datos_todos_resultados(self, ruta, nombre_heuristica, fecha_hora):

        import json

        for parametro, resultados in self.diccionario_resultados.items():
            for i in range(len(resultados)):

                ruta_archivo_csv = str(nombre_heuristica) + "-" + str(parametro) + "-" + str(i) + "-" + str(fecha_hora) \
                                   + ".csv"
                ruta_archivo_json = str(nombre_heuristica) + "-" + str(parametro) + "-" + str(i) + "-" + str(fecha_hora)\
                                    + ".txt"

                nueva_ruta_csv = ruta / ruta_archivo_csv
                nueva_ruta_json = ruta / ruta_archivo_json

                resultados_dataframe = resultados[i][1]
                resultados_dataframe.to_csv(nueva_ruta_csv)
                resultados_json = resultados[i][0]
                with open(nueva_ruta_json, 'w') as outfile:
                    json.dump(resultados_json, outfile, cls=NumpyEncoder, indent=4)

    def generar_csv_resultados_procesados(self, ruta, nombre_heuristica, fecha_hora):

        for parametro, resultados in self.diccionario_dataframes.items():

            for nombre_parametro, dataframe in resultados.items():

                ruta_archivo = str(nombre_heuristica) + "-" + str(parametro) + "-" + str(nombre_parametro) +\
                               "-" + str(fecha_hora) + ".csv"
                nueva_ruta = ruta / ruta_archivo
                dataframe.to_csv(nueva_ruta)

    def obtener_nombre_parametros_dataframe(self):

        lista_nombre_parametros_dataframe = []

        for parametro, resultados in self.diccionario_dataframes.items():
            for nombre_parametro, dataframe in resultados.items():
                lista_nombre_parametros_dataframe.append(str(nombre_parametro))

        return lista_nombre_parametros_dataframe

    def obtener_promedio_tiempo_ejecucion_parametro(self, parametro):
        tiempo = 0
        for lista in self.diccionario_resultados[parametro]:
            diccionario_resultados = lista[0]
            tiempo = tiempo + diccionario_resultados['tiempo_ejecucion']
        promedio_tiempo_ejecucion = tiempo / len(self.diccionario_resultados)
        return promedio_tiempo_ejecucion
















