import numpy as np
import simpy

from classes.Client import *
from classes.Net import *
from classes.Utilities import *
from classes.Publisher import *
from datetime import datetime

def setup_env(conf):
    env = simpy.Environment()
    env.stop_sim_event = env.event()
    env.phase_transitions = [False, False]
    env.stop_first_phase = env.event()
    env.message_ctr = 0
    env.total_messages_sent = 0
    env.total_messages_received = 0
    env.finished = False
    env.entropy = np.zeros(int(conf["misc"]["num_target_packets"]))
    return env

def flush_logs(loggers):
    for l in loggers:
        for h in l.handlers:
            h.flush()

def get_loggers(log_dir, conf):

    packet_logger = setup_logger('simulation.packet', os.path.join(log_dir, 'packet_log.csv'))
    packet_logger.info(StructuredMessage(metadata=("Type", "CurrentTime", "ClientID", "PacketID", "PacketType", "MessageID", "PacketTimeQueued", "PacketTimeSent", "PacketTimeDelivered", "TotalFragments", "PrOthers", "PrSenderA", "PrSenderB", "RealSenderLabel", "Route", "PoolSizes")))

    message_logger = setup_logger('simulation.messages', os.path.join(log_dir, 'message_log.csv'))
    message_logger.info(StructuredMessage(metadata=("Type", "CurrentTime", "ClientID", "MessageID", "NumPackets", "MsgTimeQueued", "MsgTimeSent", "MsgTimeDelivered", "MsgTransitTime", "MsgSize", "MsgRealSender","MsgRealSenderID")))

    return (packet_logger, message_logger, None)

def run(log_dir, conf):
    # Setup environment
    env = setup_env(conf)
    # Initialize components
    type = conf["network"]["topology"]
    loggers = get_loggers(log_dir, conf)
    net = Network(env, type, conf, loggers)
    publisher = Publisher(env, conf, net, loggers)
    env.process(publisher.set_start_logs())
    clients = net.clients
    print("Number of active clients: ", len(clients))
    for c in clients:
        env.process(c.start_no_dummy())
    print("---------" + str(datetime.now()) + "---------")
    print("> Running the system for %s ticks to prepare it for measurment." % (100))
    print("> Finished the preparation")
    for p in net.mixnodes:
        p.mixlogging = True
    # Add a registration request to each client's queue
    for c in clients:
        msg = Message.random(conf=c.conf, net=c.net, sender=c, dest=publisher)  # New Message
        c.schedule_message(msg)
        env.message_ctr += 1
    env.finished = True
    time_started = env.now
    env.run(until=env.stop_first_phase)  # Run until publisher receives all requests
    print("> Publisher received all messages.")
    gr_start = env.now
    print("> Start sending out confirmation.")
    for c in clients:
        msg = Message(conf=conf, net=net, payload="GID", real_sender=publisher, dest=c)
        publisher.schedule_message(msg)
        env.message_ctr += 1
        env.process(c.set_start_logs())
    while len(publisher.pkt_buffer_out) > 0:  # Flush all the replies into the network
        tmp_pkt = publisher.pkt_buffer_out.pop(0)
        publisher.send_packet(tmp_pkt)
        env.total_messages_sent += 1
    env.run(until=env.stop_sim_event) # Run until all clients received confirmation
    time_finished = env.now
    print("> Total Simulation Time [in ticks]: " + str(time_finished - time_started))
    flush_logs(loggers[:2])
    return gr_start