import time
import numpy as np
from minecraft_pb2 import *
from utils_deap_tools import *
from utils_evolution_tools import *
from utils_coordinates import *
from utils_server_client import *
from deap import base, creator, tools, algorithms

SC = ServerClient()
DEFAULT_POINT = Coordinates(69,0,69)

def toy_example_evolution_deap(ngen=100, pop_size=20):
    utils = ToyExampleTools(SC)
    
    # Prepare for experiment
    length = 10
    experiment_space_shape, unit_space_shape, starting_coordinates, goal_coordinates = utils.redstone_line_experiment_setup(DEFAULT_POINT, pop_size,length=length)
    coordinates = [c + Coordinates(0,0,1) for c in starting_coordinates]
    
    # Create tools
    creator.create("FitnessMax", base.Fitness, weights=(1.0,))
    creator.create("Individual", np.ndarray, fitness=creator.FitnessMax, coordinates = Coordinates.zero(), s_coordinates = Coordinates.zero(), g_coordinates = Coordinates.zero())
    
    toolbox = base.Toolbox()
    toolbox.register("create_individual", NumpyTools.create_individual, utils.blocks, (unit_space_shape - Coordinates(0,0,3)).to_tuple(), p=[0.8,0.2])
    toolbox.register("individual", lambda ind_c :creator.Individual(ind_c()), toolbox.create_individual)
    toolbox.register("population", tools.initRepeat, list, toolbox.individual)
    toolbox.register("execute", lambda pop,idx : utils.execute([pop[i] for i in idx],toolbox=toolbox))
    toolbox.register("fitness", lambda ind: (utils.objective_func(ind),))
    toolbox.register("evaluate", toolbox.map, toolbox.fitness)
    toolbox.register("mate", NumpyTools.random_cross)
    toolbox.register("mutate", NumpyTools.flip_mutate, p=0.05, options=utils.blocks)
    toolbox.register("select", tools.selTournament, tournsize=2)
    
    toolbox.decorate("execute", utils.assign_coordinates(coordinates, starting_coordinates, goal_coordinates))
    
    # Setup stats
    stats = GeneralStatistics.create_default_stats()
    
    # Call Evolution Algorithm
    hof = tools.HallOfFame(5, similar=np.array_equal)
    pop = toolbox.population(n=pop_size)
    pop, logbook = Algorithms.ea_simple(pop, toolbox, 0.8, 0.1, ngen, stats=stats, halloffame=hof, verbose=True)
    
    # Final results
    best_individual_fit = tools.selBest(pop, 1)[0]
    print(np.unique(pop,axis=0))
    print("Best Individual:")
    print(f"By Fitness:\n{best_individual_fit}\nf={best_individual_fit.fitness.values[0]:.2f}")
    
    print("Hall Of Fame:")
    for i,best_ind in enumerate(hof):
        print(f"Indilidual {i}:\n{best_ind}\no={best_ind.fitness.values[0]}")
    
    # Display HOF
    toolbox.execute(hof,range(len(hof)))
    SC.ask_for_response("Send \"/response\" if u want to end showcase")
    SC.place_blocks_from_loc(DEFAULT_POINT, experiment_space_shape, AIR)

if __name__ == "__main__":
    toy_example_evolution_deap()