import time
from ctypes import c_long, create_string_buffer, addressof
from logging import Logger
from threading import Condition, Thread

from systools import sys_log_exception
from typing import Optional


class SitefServiceFinder(object):
    def __init__(
        self,
        sleep_time,     # type: int
        sitef_ip_list,  # type: []
        store_id,       # type: str
        pdv_tef,        # type: int
        logger,         # type: Logger
        dll_path        # type: str
    ):
        # type: (...) -> None

        from ctypes import WinDLL
        self.logger = logger
        self.pdv_tef = pdv_tef
        self.store_id = store_id
        self.sitef_ip_list = sitef_ip_list
        self.sleep_time = float(sleep_time)

        self.search_thread = None  # type: Thread
        self.search_thread_condition = Condition()
        self.search_thread_running = False
        self.id_terminal = "SW%06d" % int(99)

        self.available_sitefs = []
        self.next_sitef_index = 0
        self.available_sitef_lock = Condition()

        self.sitef_dll = WinDLL(dll_path)
        self.configura_sitef_interativo = self.sitef_dll["ConfiguraIntSiTefInterativo"]
        self.inicia_sitef_interativo = self.sitef_dll["IniciaFuncaoSiTefInterativo"]
        self.continua_sitef_interativo = self.sitef_dll["ContinuaFuncaoSiTefInterativo"]
        self.finaliza_sitef_interativo = self.sitef_dll["FinalizaFuncaoSiTefInterativo"]

        self.search_sitef_modules()
        # self.start_search_thread()

    def _should_not_check_ip(self):
        # type: () -> bool
        return self.sleep_time <= 0

    def find_sitef_service(self):
        # type: () -> Optional[str]
        self.logger.debug("Iniciando => find_sitef_service")
        if len(self.available_sitefs) == 0:
            if self._should_not_check_ip():
                self.logger.debug("Nenhum IP foi encontrado na lista, recarrega lista de IPs")
                self.available_sitefs = self.sitef_ip_list[:]
                return self.available_sitefs[0]
            self.logger.debug("Nenhum IP foi encontrado na lista, retornamos vazio")
            return None
        else:
            self.logger.debug("Retornando IP => {0}".format(self.available_sitefs[0]))
            return self.available_sitefs[0]

    def sitef_unavailable(self, ip_sitef):
        # type: (str) -> None
        self.logger.debug("Iniciando => sitef_unavailable")
        self.logger.debug("Removendo IP da lista de IPs validos. IP => {0}".format(ip_sitef))
        if ip_sitef in self.available_sitefs:
            self.available_sitefs.remove(ip_sitef)

    def lock_sitef(self):
        self.available_sitef_lock.acquire()

    def release_sitef(self):
        self.available_sitef_lock.release()

    def wakeup_search(self):
        if self.search_thread_running:
            with self.search_thread_condition:
                self.search_thread_condition.notifyAll()

    def search_sitef_modules(self):
        if self._should_not_check_ip():
            self.available_sitefs = self.sitef_ip_list[:]
            return
        with self.available_sitef_lock:
            self.logger.debug("Iniciando => search_sitef_modules")
            self.logger.debug("Quantidade de IPs para validacao => {0}".format(len(self.sitef_ip_list)))
            for ip in self.sitef_ip_list:
                self.logger.debug("Validando IP => {0}".format(ip))
                try:
                    data_fiscal = time.strftime("%Y%m%d")  # '20171017'
                    hora_fiscal = time.strftime("%H%M%S")  # '162130'

                    ret = self.process(str(self.pdv_tef), data_fiscal, hora_fiscal, ip)

                    if ret:
                        self.logger.debug("Sucesso na validacao do IP => {0}".format(ip))
                        if ip not in self.available_sitefs:
                            self.available_sitefs.append(ip)
                    else:
                        self.logger.error("Falha na validacao do IP => {0}".format(ip))
                        if ip in self.available_sitefs:
                            self.sitef_unavailable(ip)

                except Exception:
                    self.logger.exception("Erro validando IP SiTEF")
        self.logger.debug("Finalizado => search_sitef_modules")

    def start_search_thread(self):
        self.search_thread_running = True
        self.search_thread = Thread(target=self._search_thread_loop)
        self.search_thread.daemon = True
        self.search_thread.name = "search_sitef_modules"
        self.search_thread.start()

    def stop_search_thread(self):
        if self._should_not_check_ip():
            return
        if self.search_thread_running:
            with self.search_thread_condition:
                self.search_thread_running = False
                self.search_thread_condition.notifyAll()

    def _search_thread_loop(self):
        if self._should_not_check_ip():
            return
        while self.search_thread_running:
            self.search_sitef_modules()
            with self.search_thread_condition:
                self.search_thread_condition.wait(self.sleep_time)

    def process(self, pos_id, data_fiscal, hora_fiscal, ip_sitef=None):
        if not ip_sitef:
            ip_sitef = self.find_sitef_service()
        time_limit = (time.time() + 5.0)
        while (time.time() < time_limit) and (not self.sitef_dll["VerificaPresencaPinPad"]()):
            time.sleep(0.2)

        id_terminal = "SW99%04d" % int(pos_id)
        self.logger.debug("TestSitefProcessor - IP Sitef: {0}, ID Loja: {1}, ID Terminal: {2}".format(
            ip_sitef,
            self.store_id,
            id_terminal
        ))

        ret = -12
        while ret == -12:
            ret = self.configura_sitef_interativo(str(ip_sitef), str(self.store_id), id_terminal, 0)

        if ret == 0:
            ret = self.inicia_sitef_interativo(111, "", '{}'.format(pos_id), data_fiscal, hora_fiscal, "", "")
            if ret == 10000:
                comando = c_long(0)
                tipo_campo = c_long(0)
                tam_minimo = c_long(0)
                tam_maximo = c_long(0)
                tam_buffer = 32768
                output_buffer = create_string_buffer('\000' * tam_buffer)

                while ret == 10000:
                    ret = self.continua_sitef_interativo(addressof(comando), addressof(tipo_campo), addressof(tam_minimo), addressof(tam_maximo), output_buffer, tam_buffer, 0)

                    if ret != 10000:
                        break

                    try:
                        if comando.value == 20:
                            output_buffer.value = "0"
                            time.sleep(0.5)

                    except Exception as ex:
                        sys_log_exception("Exception Trapped on SiTef Processor %" + str(ex))

                response = "Teste Realizado com Sucesso" if ret == 0 else "Falha no Teste: Erro %s" % str(ret)
                self.logger.debug("TestSitefProcessor - Resposta: {}".format(response))

                self.finaliza_sitef_interativo("0", '{}'.format(pos_id), data_fiscal, hora_fiscal, None)
                return True if ret == 0 else False

        self.logger.debug("TestSitefProcessor - Erro Configurando SiTef Interativo")
        return False
