import sys
import os
import numpy as np
import mechnet as mn
from multiprocessing import Pool
from functools import partial
sys.path.append("/home/lpyka/hierarchical_interface/")
from lib.push_data import push_data_to, copy_runfile_to 
from lib.my_plot_edges import plot_edges_mechnet_network 

os.environ["OMP_NUM_THREADS"] = "1"
os.environ["OPENBLAS_NUM_THREADS"] = "1"
os.environ["MKL_NUM_THREADS"] = "1"

def get_edge_list():
    edge_list = []
    pwd = os.getcwd()
    with open("7_composite_system/composite/edgelist_0_seed1_0", "r") as file:
        for line in file:
            line = line.rstrip("\n")
            line = line.split(" ")
            data_list = [int(entry) for entry in line]
            edge_list.append(data_list)
    return edge_list

def generate_coordinate_list(nx, ny, nz):
    coordinate_list = []
    for node in range(0,nx*ny*nz):
        x_coordinate = (node%(nx*ny))%nx 
        y_coordinte = (node%(nx*ny))//nx
        z_coordinate = node//(nx*ny)
        entry = [node, x_coordinate, y_coordinte, z_coordinate]
        coordinate_list.append(entry)
    return coordinate_list

def generate_thresholdlist(parameters):
    thresholdlist = []
    for _ in parameters.entries["edgelist"]:
        threshold = parameters.entries["thresholdrng"].return_weibull(parameters.entries["weibullparameter"])
        thresholdlist.append(threshold)
    return thresholdlist



def get_parametercollection(seed):
    edge_list = get_edge_list() 
    steps = 3
    nx = 2**steps
    ny = nx
    nz = 2*(2+steps) - 2
    coordinate_list = generate_coordinate_list(nx, ny, nz)
    par = mn.ParameterCollection()
    par.set_N(nx*ny*nz)
    par.set_edgelist(edge_list)
    par.set_coordinatelist(coordinate_list)
    Lx = nx - 1
    Ly = Lx
    Lz = nz - 1
    par.set_Lx(Lx)
    par.set_Ly(Ly)
    par.set_Lz(Lz)
    par.set_minDz(0.05)
    par.set_minzdirichlet({'displacement' : 0.0})
    par.set_maxDz(Lz - 0.05)
    par.set_maxzdirichlet({'displacement' : 1.0})
    par.set_thresholdrng(seed)
    par.set_structurerng(seed + 1_000_000)
    par.set_weibullparameter(4)
    thresholdlist = generate_thresholdlist(par)
    par.set_thresholdlist(thresholdlist)
    return par

def run_a_simulation_fuse(seed, targetdirectory):
    #setup a network constructor that will produce the desired network
    constructor = mn.arbgeo.EdgelistBasedConstructor()
    constructor = mn.arbgeo.NodePositionsListDecorator(constructor)
    #running a (fuse) simulation necessitates thresholds:
    constructor = mn.general.ListbasedThresholdDecorator(constructor)
    
    #setup a Parametercollection object containing all necessary parameters
    parametercollection = get_parametercollection(seed)
    
    #setup a boundary constructor that will produce the desired 
    #boundary conditions; both network and boundary constructors
    #use the decorator pattern (look up this design pattern for further
    #information);
    #in an arbitrary geometry, we do not know which nodes are at the top
    #or bottom, so we need definitions of boundary conditions 
    #based on their assigned positions
    boundaries = mn.general.EmptyBoundariesConstructor()
    boundaries = mn.arbgeo.MinZBoundaryDecorator(boundaries)
    boundaries = mn.arbgeo.MaxZBoundaryDecorator(boundaries)
    
    #construct network by calling the start_construction() method of the
    #network and passing the fitting parametercollection
    network = constructor.start_construction(parametercollection)
    
    #the network saves the associated Parametercollection object;
    #to add desired boundary conditions call the start_assign_boundaries()
    #method of the boundary constructor and pass the network
    bounded_network = boundaries.start_assign_boundaries(network)
    
    #define simulation parameters: maximum number of iterations niter,
    #the external 'voltage' (fuse simulations are a skalar simplification
    #of mechanics which are equvalent to a network of electrical fuses;
    #some of the naming schemes reflect this), accuracy (rounding to this
    #decimal within the simulation)
    dict_of_simulation_parameters = {"niter":1, "externalV": 1.0, "accuracy":12}
    #construct the desired simulation setup
    builder = mn.sim.FuseDirichletBuilder() #builds the system of equations Ku=f
    solver = mn.sim.PARDISOBindingsSolver() #solves system of equations
    intrescal = mn.sim.StartInterimResultsCalculator() #decorator pattern again:
    intrescal = mn.sim.FuseCurrentsVoltagesCalculator(intrescal) #calculate necessary interim results
    outgen = mn.sim.StartOutputGenerator() #decorator pattern: If desired, specify additional output
    applier = mn.sim.HottestFuseBreakApplier() #apply changes for each simulation step
    checker = mn.sim.NonInitialChecker(mn.sim.ConnectedPathChecker()) #check whether simulation reached ending condition (e.g.: network fully fractured)
    simulation = mn.sim.RunTillStopconditionSimulation(builder, solver, intrescal, outgen, applier, checker) #pass dsired parts to Simulation during construction
    #get necessary data about the network in form of a dictionary
    dict_of_network_data= bounded_network.get_network_data()
    #run the simulation by calling the start_simulation() method and passing 
    #dict_of_network_data and dict_of_simulation_parameters
    simdata = simulation.start_simulation(dict_of_network_data, dict_of_simulation_parameters)
    simdata_dict = simdata.get_simdata_dict()
    network_descriptor_dict = bounded_network.get_construction_data()
    mn.datman.save_simulation_output(network_descriptor_dict, simdata_dict, simulation.simulationname, simulation.simulationdoc,
                                     suffix=str(seed), overwritemode = False, targetdirectory=targetdirectory)
    print("done with sim of seed", seed)
    return
    

if __name__ == "__main__":
   # seeds = [i for i in range(1000, 1050)]
    seed = 1000
    crack_length = 0
    destination = f"/FASTTEMP/p7/lpyka/hierarchical_interface/7_composite_system/1_deterministic/a_{crack_length}/"
    run_a_simulation_fuse(seed=seed, targetdirectory=destination)
   # with Pool(50) as p:
   #     p.map(partial(run_a_simulation_fuse, targetdirectory=destination), seeds)
    copy_runfile_to(runfile_name=__file__,destination=destination)
    print(f"Done with crack {crack_length}")
    


