import threading
import time

from core.aochat import server_packets
from core.aochat.client_packets import CharacterLookup
from core.decorators import instance


@instance()
class CharacterService:
    def __init__(self):
        self.name_to_id = {}
        self.id_to_name = {}
        self.waiting_for_response = set()
        self.notify_on_receive = {}

    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.db = registry.get_instance("db")

    def pre_start(self):
        self.bot.register_packet_handler(server_packets.CharacterLookup.id, self.update)
        self.bot.register_packet_handler(server_packets.CharacterName.id, self.update)

    def start(self):
        self.db.shared.exec("CREATE TABLE IF NOT EXISTS `all_orgs` (`org_id` INT(11) NOT NULL, "
                            "`org_name` VARCHAR(64) NOT NULL COLLATE 'utf8mb4_general_ci', "
                            "`member_count` INT(11) NULL DEFAULT NULL, "
                            "`faction` VARCHAR(16) NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci', "
                            "`last_seen` INT(11) NOT NULL, "
                            "PRIMARY KEY (`org_id`) USING BTREE, "
                            "INDEX `org_name` (`org_name`) USING BTREE)")
        self.db.create_view("all_orgs")

    def _wait_for_char_id(self, char_name):
        # char_name must be .capitalize()'ed

        packet = self.bot.iterate(1)
        while packet and char_name not in self.name_to_id:
            packet = self.bot.iterate(1)

        return self.name_to_id.get(char_name, None)

    # FeatureFlags.THREADING
    def _wait_for_char_id_threading(self, char_name):
        # char_name must be .capitalize()'ed

        event = self.notify_on_receive.get(char_name, None)
        if event is None:
            event = threading.Event()
            self.notify_on_receive[char_name] = event

        if char_name not in self.name_to_id:
            event.wait(10)

        return self.name_to_id.get(char_name, None)

    def resolve_char_to_id(self, char):
        if isinstance(char, int):
            return char
        elif char.isdigit():
            return int(char)
        else:
            char_name = char.capitalize()
            if char_name in self.name_to_id:
                return self.name_to_id[char_name]
            else:
                self._send_lookup_if_needed(char_name)
                return self._wait_for_char_id(char_name)

    def resolve_char_to_name(self, char, default=None):
        if isinstance(char, int) or char.isdigit():
            char_name = self.get_char_name(char)
            return char_name if char_name else default
        else:
            return char

    def get_char_name(self, char_id):
        return self.id_to_name.get(char_id, None)

    def update(self, conn, packet):
        self.waiting_for_response.discard(packet.name)

        if packet.char_id == 4294967295:
            self.name_to_id[packet.name] = None
        else:
            self.id_to_name[packet.char_id] = packet.name
            self.name_to_id[packet.name] = packet.char_id
            # self._update_name_history(packet.name, packet.char_id)

    def _update_name_history(self, char_name, char_id):
        params = [char_name, char_id, int(time.time())]
        self.db.exec("INSERT IGNORE INTO name_history (name, char_id, created_at) VALUES (?, ?, ?)", params)

    def _send_lookup_if_needed(self, char_name):
        # char_name must be .capitalize()'ed
        if char_name not in self.name_to_id and char_name not in self.waiting_for_response:
            self.waiting_for_response.add(char_name)
            self.bot.send_packet(CharacterLookup(char_name))
