import json
from multiprocessing.connection import Client
import pickle
import random
import socket
import sys

from message import *  # Can change this after testing done
from timers import ClientTimer, setTimer


class LockClient():
    def __init__(self) -> None:
        with open("config.json", encoding='utf-8') as f:
            json_dict = json.loads(f.read())
        server_addresses = json_dict["servers"]
        self.server_addresses = [(server_addresses[i], int(server_addresses[i + 1])) for i in range(0, len(server_addresses), 2)]

        # create a UDP socket
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.sock.bind(('', 0))
        self.client_addr = self.sock.getsockname()
        print(f"Starting client {self.client_addr}", file=sys.stdout)
        random.seed(0)
        # For client timer to know whether to resend message
        self.req_num = 0

    def execute(self):
        prompt_str = "Enter a command (e.g. lock 1 / unlock 1 / exit): "
        input_str = input(prompt_str)  # Input would be "lock 2" / "unlock 1", etc or "exit"
        try:
            while input_str != "exit":
                lock, val = input_str.split(" ", 2)
                paxos_req = PaxosRequest(self.client_addr, LockCommand(lock, val, self.req_num))
                data = pickle.dumps(paxos_req)

                server_address = random.choice(self.server_addresses)
                print(f"Sending command {paxos_req} to {server_address}", file=sys.stdout)
                self.sock.sendto(data, server_address)

                setTimer(ClientTimer(paxos_req, self.req_num), ClientTimer.CLIENT_RETRY_MILLIS, self.onClientTimer)

                # receive data back from the server
                res = pickle.loads(self.sock.recv(1024))
                while res.cmd.req_num != self.req_num:
                    res = pickle.loads(self.sock.recv(1024))

                self.req_num += 1

                print(f"Received response: {res}", file=sys.stdout)

                if res.value == True:
                    print("Command successfully ran", file=sys.stdout)
                else:
                    print("Command failed to run", file=sys.stdout)

                input_str = input(prompt_str)
        finally:
            # Close socket
            self.sock.close()

    def onClientTimer(self, client_timer: ClientTimer):
        # print(f"{client_timer}: Callback", file=sys.stdout)
        if self.req_num <= client_timer.req_num:
            server_address = random.choice(self.server_addresses)
            print(f"Resending command {client_timer.paxos_req} to {server_address}", file=sys.stdout)
            data = pickle.dumps(client_timer.paxos_req)
            self.sock.sendto(data, server_address)
            setTimer(client_timer, ClientTimer.CLIENT_RETRY_MILLIS, self.onClientTimer)


if __name__ == "__main__":
    client = LockClient()
    client.execute()