# coding: utf-8
import socket

from . import tea, utils, settings


def _make_keys(secret):
    # Implementation of the diffie-helman algorithm using funcom values
    # as described here: http://aodevs.com/forums/index.php?topic=42.0

    # funcom - prime base
    dh_p = int("<KEY>"
               "a778703b418408249440b2c1edd28853e240d8a7e49540b76d120d3b1ad2878b1b99490eb4a2a5e84caa8a91cecbdb1aa7c816e"
               "<KEY>", 16)
    # funcom - server public key
    dh_f = int("<KEY>"
               "<KEY>"
               "<KEY>", 16)
    # funcom - generator
    dh_g = int("5", 16)

    # client secret
    dh_x = int(secret, 16)

    # dh_g = dh_g^dhx mod dh_p
    # client public key
    dh_g = pow(dh_g, dh_x, dh_p)

    # dh_k = dh_f^dhx mod dh_p ( dh_f^dhx = (dh_g^funcom hemlig nyckel)^dhx
    # shared secret used to encrypt
    dh_k = pow(dh_f, dh_x, dh_p)

    # right pad with zeroes
    return ('%016x' % dh_g)[:16], ('%016x' % dh_k)[:16]


def _make_login_token(username, password, seed):
    prefix = utils.random_hex(8)

    login_info = "|".join([username, seed, password])

    # base-4 length of login_info
    login_info_length = utils.int2str(len(login_info), base=4)

    # authentication token
    token = prefix + login_info_length + login_info

    # pad with spaces until length is a multiple of 8
    while len(token) % 8:
        token += ' '

    return token


class AOClient(object):
    def __init__(self):
        self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.private_key, self.public_key = _make_keys(utils.random_hex(32))
        self.server_seed = None

    def __enter__(self):
        self.connect()
        print('Connected to %s. Using seed %s.' % (settings.HOST, self.server_seed))
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()

    def connect(self):
        if self.server_seed is not None:
            return  # Already connected

        remote_ip = socket.gethostbyname(settings.HOST)
        self._sock.connect((remote_ip, settings.PORT))
        self.server_seed = self._sock.recv(1024).decode()[6:]

    def close(self):
        self._sock.shutdown(1)
        self._sock.close()
        self.server_seed = None

    def authenticate(self, username, password):
        token = _make_login_token(username, password, self.server_seed)
        cipher = tea.encrypt(token, self.private_key)
        message = "{}-{}".format(self.public_key, cipher).encode()
        self._sock.send(message)
