class WakewordUploadThread(Thread):
    def __init__(self, host: str, port: int, zipPath: str):
        super().__init__()
        self._logger = Logger(prepend='[HotwordUploadThread]')

        self.setDaemon(True)

        self._host = host
        self._port = port
        self._zipPath = Path(zipPath)

    def run(self):
        try:
            wakewordName = self._zipPath.stem

            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
                sock.bind((self._host, self._port))
                self._logger.logInfo('Waiting for a device to connect')
                sock.listen()

                conn, addr = sock.accept()
                self._logger.logInfo(
                    f'New device connected with address **{addr}**')

                with self._zipPath.open(mode='rb') as f:
                    data = f.read(1024)
                    while data:
                        conn.send(data)
                        data = f.read(1024)

                self._logger.logInfo(
                    f'Waiting on a feedback from **{addr[0]}**')
                conn.settimeout(20)
                try:
                    while True:
                        answer = conn.recv(1024).decode()
                        if not answer:
                            raise Exception(
                                'The device closed the connection before confirming...'
                            )
                        if answer == '0':
                            self._logger.logInfo(
                                f'Wakeword **{wakewordName}** upload to **{addr[0]}** success'
                            )
                            break
                        elif answer == '-1':
                            raise Exception(
                                'The device failed downloading the hotword')
                        elif answer == '-2':
                            raise Exception(
                                'The device failed installing the hotword')
                except timeout:
                    self._logger.logWarning(
                        'The device did not confirm the operation as successfull in time. The hotword installation might have failed'
                    )
        except Exception as e:
            self._logger.logError(f'Error uploading wakeword: {e}')
class HotwordDownloadThread(Thread):
    def __init__(self, host: str, port: int, hotwordName: str):
        super().__init__()
        self._logger = Logger(prepend='[HotwordDownloadThread]')
        self._host = host
        self._port = int(port)
        self._hotwordName = hotwordName
        self.setDaemon(True)

    def run(self):
        sock = None
        try:
            self._logger.logInfo('Cleaning up')

            rootPath = Path(SuperManager.getInstance().commons.rootDir(),
                            'hotwords')
            hotwordPath = rootPath / f'{self._hotwordName}'
            zipPath = hotwordPath.with_suffix('.zip')

            if zipPath.exists():
                zipPath.unlink()

            if hotwordPath.exists():
                shutil.rmtree(hotwordPath, ignore_errors=True)

            self._logger.logInfo(
                f'Connecting to **{self._host}_{self._port}**')
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.connect((self._host, self._port))

            self._logger.logInfo(
                f'Receiving hotword package: **{self._hotwordName}**')
            sock.settimeout(2)
            try:
                with zipPath.open('wb') as f:
                    while True:
                        data = sock.recv(1024)

                        if not data:
                            break

                        f.write(data)
            except socket.timeout:
                sock.settimeout(None)

        except Exception as e:
            self._logger.logError(f'Error downloading hotword: {e}')
            if sock:
                sock.send(b'-1')
                sock.close()
            return

        try:
            self._logger.logInfo('New hotword received, unpacking...')
            shutil.unpack_archive(filename=zipPath, extract_dir=hotwordPath)

            conf = SuperManager.getInstance(
            ).configManager.getSnipsConfiguration(parent='snips-hotword',
                                                  key='model',
                                                  createIfNotExist=True)
            if not isinstance(conf, list):
                conf = list()

            addSnips = True
            regex = re.compile(f'.*/{hotwordPath}=[0-9.]+$')
            copy = conf.copy()
            for i, hotword in enumerate(copy):
                if hotword.find('/snips_hotword='):
                    addSnips = False
                elif regex.match(hotword):
                    conf.pop(i)

            if addSnips:
                conf.append(str(rootPath / 'snips_hotword=0.53'))

            conf.append(f'{hotwordPath}=0.52')
            SuperManager.getInstance().configManager.updateSnipsConfiguration(
                parent='snips-hotword',
                key='model',
                value=conf,
                createIfNotExist=True)
            subprocess.run(['sudo', 'systemctl', 'restart', 'snips-satellite'])

            sock.send(b'0')
            self._logger.logInfo(
                f'Sucessfully installed new hotword **{self._hotwordName}**')
        except Exception as e:
            self._logger.logError(
                f'Error while unpacking and installing hotword: {e}')
            sock.send(b'-2')
        finally:
            sock.close()
            os.remove(zipPath)