import random
from threading import Timer
from Packet.Packet import Packet
from Packet.ReceivedPacket import ReceivedPacket
from Packet.PacketPimHello import PacketPimHello
from Packet.PacketPimHeader import PacketPimHeader
from Interface import Interface
import Main
from utils import HELLO_HOLD_TIME_TIMEOUT


class Hello:
    TYPE = 0
    TRIGGERED_HELLO_DELAY = 16  # TODO: configure via external file??

    def __init__(self):
        Main.add_protocol(Hello.TYPE, self)

        self.thread = Timer(Hello.TRIGGERED_HELLO_DELAY, self.send_handle)
        self.thread.start()

    def send_handle(self):
        for (_, interface) in list(Main.interfaces.items()):
            self.packet_send_handle(interface)

        # reschedule timer
        self.thread = Timer(Hello.TRIGGERED_HELLO_DELAY, self.send_handle)
        self.thread.start()

    def packet_send_handle(self, interface: Interface):
        pim_payload = PacketPimHello()
        pim_payload.add_option(1, 3.5 * Hello.TRIGGERED_HELLO_DELAY)
        pim_payload.add_option(20, interface.generation_id)
        ph = PacketPimHeader(pim_payload)
        packet = Packet(payload=ph)
        interface.send(packet.bytes())

    def force_send(self, interface: Interface):
        # When PIM is enabled on an interface or when a router first starts, the Hello Timer (HT)
        # MUST be set to random value between 0 and Triggered_Hello_Delay
        hello_timer = random.uniform(0, Hello.TRIGGERED_HELLO_DELAY)
        Timer(hello_timer, self.packet_send_handle, args=[interface]).start()

    def force_send_remove(self, interface: Interface):
        pim_payload = PacketPimHello()
        pim_payload.add_option(1, HELLO_HOLD_TIME_TIMEOUT)
        pim_payload.add_option(20, interface.generation_id)
        ph = PacketPimHeader(pim_payload)
        packet = Packet(payload=ph)
        interface.send(packet.bytes())

    # receive handler
    def receive_handle(self, packet: ReceivedPacket):
        ip = packet.ip_header.ip_src
        print("ip = ", ip)
        options = packet.payload.payload.get_options()
        if Main.get_neighbor(ip) is None:
            # Unknown Neighbor
            if (1 in options) and (20 in options):
                try:
                    Main.add_neighbor(packet.interface, ip, options[20], options[1])
                    print("non neighbor and options inside")
                except Exception:
                    # Received Neighbor with Timeout
                    print("non neighbor and options inside but neighbor timedout")
                    pass
                return
            print("non neighbor and required options not inside")
        else:
            # Already know Neighbor
            print("neighbor conhecido")
            neighbor = Main.get_neighbor(ip)
            neighbor.heartbeat()
            if 1 in options and neighbor.hello_hold_time != options[1]:
                print("keep alive period diferente")
                neighbor.set_hello_hold_time(options[1])
            if 20 in options and neighbor.generation_id != options[20]:
                print("neighbor reiniciado")
                neighbor.remove()
                Main.add_neighbor(packet.interface, ip, options[20], options[1])