import os
import socket
from random import randint

from src import Constants
from src.Constants import Network
from src.networking import NetworkPackets, Actions
from src.networking.Client import Client
from src.utils.DH_Encryption import Encryption
from src.utils.Enum import Enum


class SessionManager:
    """
    This class is responsible for dealing with any flow of net msgs.
    """

    def __init__(self):
        address = (Network.SERVER_IP, Network.SERVER_PORT)
        self.client = Client(str(socket.gethostname()), address)
        self.val = self.client.connect()
        if not self.val:
            Network.IS_ONLINE = False

    def go_crypto(self):
        msg = NetworkPackets.split(self.client.receive())
        g = int(msg[1])
        n = int(msg[2])
        g_pow_a_mod_n = int(msg[3])
        crypto = Encryption(g, n)
        crypto.get_full_key(g_pow_a_mod_n)
        self.client.send(NetworkPackets.assemble(NetworkPackets.NetLogicIncomes.CONNECT.value,
                                                 str(crypto.get_partial_key())))
        self.client.crypto = crypto

    def gen_id(self) -> str:
        num = str(randint(1, 9999))
        num = num.zfill(4)
        return num

    def open_id_file(self):
        try:
            open(Constants.Files.ID, 'r+').close()
        except FileNotFoundError:
            open(Constants.Files.ID, 'x').close()
        finally:
            file = open(Constants.Files.ID, 'r+')
            return file

    def sync(self):
        """
        This function contains the full process of the sync phase.
        """
        if Network.IS_ONLINE:
            self.go_crypto()
            num = ""
            file = self.open_id_file()

            if os.path.getsize(Constants.Files.ID) == 0:  # Empty
                is_valid = False
                while not is_valid:
                    num = self.gen_id()
                    self.client.send(NetworkPackets.assemble("COMPUTER", "ID_VAL", num))
                    msg = NetworkPackets.split(self.client.receive())
                    is_valid = msg[0] == NetworkPackets.NetLogicIncomes.VALID.value

                file.write(num)

            else:
                is_valid = False
                num = file.read()
                while not is_valid:
                    self.client.send(NetworkPackets.assemble("COMPUTER", "ID_VAL", num))
                    msg = NetworkPackets.split(self.client.receive())
                    is_valid = msg[0] == NetworkPackets.NetLogicIncomes.VALID.value
                    if not is_valid:
                        num = self.gen_id()

                if num != file.read():
                    file.close()
                    os.remove(Constants.Files.ID)
                    file = self.open_id_file()
                    file.write(num)

            file.close()

    def manage(self, incoming: str):
        """
        This functions deals with the execution of the required operations.
        :param incoming: Raw net msg.
        """
        if Network.IS_ONLINE:
            incoming = NetworkPackets.split(incoming)[0]
            if incoming in Operation.list():

                if incoming == Operation.VOL_UP.value:
                    Actions.vol_up()
                elif incoming == Operation.VOL_DOWN.value:
                    Actions.vol_down()
                elif incoming == Operation.PAUSE_PLAY_TOGGLE.value:
                    Actions.play_pause()
                elif incoming == Operation.SKIP.value:
                    Actions.next_song()
                elif incoming == Operation.PREV.value:
                    Actions.prev_song()
                elif incoming == Operation.MUTE.value:
                    Actions.mute()
                elif incoming == Operation.OFF.value:
                    Actions.shut_down()
                elif incoming == Operation.SLEEP.value:
                    Actions.sleep()
                elif incoming == Operation.RESTART.value:
                    Actions.restart()
                elif incoming == Operation.LOCK.value:
                    Actions.lock()
                elif incoming == Operation.LOG_OUT.value:
                    Actions.log_out()
                elif incoming == Operation.MAGIC_BTN.value:
                    Actions.run_file()
                elif incoming == Operation.USAGE.value:
                    self.client.send(NetworkPackets.assemble(arr=Actions.COMPUTER.get_use_as_str_arr()))

                elif incoming == Operation.DISCONNECT.value:
                    self.client.send(NetworkPackets.assemble(Operation.DISCONNECT.value))
                    return Operation.DISCONNECT

            elif incoming in NetworkPackets.NetLogicIncomes.list():
                if incoming == NetworkPackets.NetLogicIncomes.PAIRED.value:
                    Constants.Network.IS_PAIRING = True
                    self.client.send(NetworkPackets.assemble(arr=Actions.COMPUTER.get_specs_as_str_arr()))
                elif incoming == NetworkPackets.NetLogicIncomes.INVALID:
                    pass


class Operation(Enum):
    """
    All the operations that can be asked to execute.
    """
    VOL_UP = "VOL_UP"
    VOL_DOWN = "VOL_DOWN"
    PAUSE_PLAY_TOGGLE = "PTT"
    SKIP = "SKIP"
    PREV = "PREV"
    MUTE = "MUTE"
    OFF = "OFF"
    SLEEP = "SLEEP"
    RESTART = "RESTRT"
    LOCK = "LCK"
    LOG_OUT = "LGOT"
    DISCONNECT = "DISCON"
    MAGIC_BTN = "MAGIC"
    SPECS_INFO = "SPECS"
    USAGE = "USE"

[((30, 17, 30, 33), 'src.utils.DH_Encryption.Encryption', 'Encryption', ({(30, 28, 30, 29): 'g', (30, 31, 30, 32): 'n'}, {}), '(g, n)', False, 'from src.utils.DH_Encryption import Encryption\n'), ((37, 18, 37, 34), 'random.randint', 'randint', ({(37, 26, 37, 27): '1', (37, 29, 37, 33): '9999'}, {}), '(1, 9999)', False, 'from random import randint\n'), ((20, 33, 20, 53), 'socket.gethostname', 'socket.gethostname', ({}, {}), '()', False, 'import socket\n'), ((59, 15, 59, 50), 'os.path.getsize', 'os.path.getsize', ({(59, 31, 59, 49): 'Constants.Files.ID'}, {}), '(Constants.Files.ID)', False, 'import os\n'), ((93, 23, 93, 53), 'src.networking.NetworkPackets.split', 'NetworkPackets.split', ({(93, 44, 93, 52): 'incoming'}, {}), '(incoming)', False, 'from src.networking import NetworkPackets, Actions\n'), ((81, 20, 81, 49), 'os.remove', 'os.remove', ({(81, 30, 81, 48): 'Constants.Files.ID'}, {}), '(Constants.Files.ID)', False, 'import os\n'), ((97, 20, 97, 36), 'src.networking.Actions.vol_up', 'Actions.vol_up', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((127, 29, 127, 66), 'src.networking.NetworkPackets.NetLogicIncomes.list', 'NetworkPackets.NetLogicIncomes.list', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((63, 37, 63, 87), 'src.networking.NetworkPackets.assemble', 'NetworkPackets.assemble', ({(63, 61, 63, 71): '"""COMPUTER"""', (63, 73, 63, 81): '"""ID_VAL"""', (63, 83, 63, 86): 'num'}, {}), "('COMPUTER', 'ID_VAL', num)", False, 'from src.networking import NetworkPackets, Actions\n'), ((73, 37, 73, 87), 'src.networking.NetworkPackets.assemble', 'NetworkPackets.assemble', ({(73, 61, 73, 71): '"""COMPUTER"""', (73, 73, 73, 81): '"""ID_VAL"""', (73, 83, 73, 86): 'num'}, {}), "('COMPUTER', 'ID_VAL', num)", False, 'from src.networking import NetworkPackets, Actions\n'), ((99, 20, 99, 38), 'src.networking.Actions.vol_down', 'Actions.vol_down', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((101, 20, 101, 40), 'src.networking.Actions.play_pause', 'Actions.play_pause', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((103, 20, 103, 39), 'src.networking.Actions.next_song', 'Actions.next_song', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((105, 20, 105, 39), 'src.networking.Actions.prev_song', 'Actions.prev_song', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((130, 65, 130, 104), 'src.networking.Actions.COMPUTER.get_specs_as_str_arr', 'Actions.COMPUTER.get_specs_as_str_arr', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((107, 20, 107, 34), 'src.networking.Actions.mute', 'Actions.mute', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((109, 20, 109, 39), 'src.networking.Actions.shut_down', 'Actions.shut_down', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((111, 20, 111, 35), 'src.networking.Actions.sleep', 'Actions.sleep', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((113, 20, 113, 37), 'src.networking.Actions.restart', 'Actions.restart', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((115, 20, 115, 34), 'src.networking.Actions.lock', 'Actions.lock', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((117, 20, 117, 37), 'src.networking.Actions.log_out', 'Actions.log_out', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((119, 20, 119, 38), 'src.networking.Actions.run_file', 'Actions.run_file', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n'), ((124, 37, 124, 88), 'src.networking.NetworkPackets.assemble', 'NetworkPackets.assemble', ({(124, 61, 124, 87): 'Operation.DISCONNECT.value'}, {}), '(Operation.DISCONNECT.value)', False, 'from src.networking import NetworkPackets, Actions\n'), ((121, 65, 121, 102), 'src.networking.Actions.COMPUTER.get_use_as_str_arr', 'Actions.COMPUTER.get_use_as_str_arr', ({}, {}), '()', False, 'from src.networking import NetworkPackets, Actions\n')]