import asyncio
import datetime
import logging
import sys
import time
from asyncio import Server, Queue
from cgi import print_exception

from grpc import aio

from py_speech_service import speech_service_pb2_grpc, speech_service_pb2
from py_speech_service.speaker import Speaker, SpeechSettings
from py_speech_service.speech_recognition import SpeechRecognition


class GrpcServer:

    speaker: Speaker
    speech_recognition: SpeechRecognition
    server: Server
    shutdown_event = asyncio.Event()
    response_queue = Queue()

    def __init__(self):
        self.speaker = Speaker()
        self.speech_recognition = SpeechRecognition()

    async def start(self):
        server = aio.server()
        self.server = server
        speech_service_pb2_grpc.add_SpeechServiceServicer_to_server(self, server)
        port = server.add_insecure_port("[::]:0")
        await server.start()
        logging.info("Listening to gRPC connections on port " + str(port))
        print("grpc_port=" + str(port))
        sys.stdout.flush()
        self.speaker.start()
        await self.shutdown_event.wait()
        self.speaker.shutdown()
        if self.speech_recognition:
            self.speech_recognition.shutdown()
        await server.stop(5)
        time.sleep(1)

    async def StartSpeechService(self, request_iterator, context):
        self.speaker.set_grpc_response_queue(self.response_queue)
        self.speech_recognition.set_grpc_response_queue(self.response_queue)
        asyncio.create_task(self.process_queue(context))

        async for request in request_iterator:
            try:
                if request.HasField("start_speech_recognition"):
                    logging.info(
                        "Received gRPC start_speech_recognition request: " + str(request.start_speech_recognition))
                    print("Received gRPC start_speech_recognition request")

                    vosk_model = request.start_speech_recognition.vosk_model if hasattr(request.start_speech_recognition,
                        "vosk_model") else None
                    grammar_file = request.start_speech_recognition.grammar_file if hasattr(request.start_speech_recognition,
                        "grammar_file") else None
                    required_confidence = request.start_speech_recognition.required_confidence if hasattr(request.start_speech_recognition,
                        "required_confidence") else 80

                    self.speech_recognition.set_vosk_details(vosk_model, grammar_file, required_confidence)
                    asyncio.create_task(self.speech_recognition.start_speech_recognition(context))

                elif request.HasField("set_speech_defaults"):
                    logging.info(
                        "Received gRPC set_speech_defaults request: " + str(request.set_speech_defaults))
                    print("Received gRPC set_speech_defaults request")

                    speech_settings = SpeechSettings(request.set_speech_defaults.speech_settings)
                    alt_speech_settings = SpeechSettings(request.set_speech_defaults.alt_voice_settings) if request.set_speech_defaults.HasField(
                        "alt_voice_settings") else request.set_speech_defaults.speech_settings
                    self.speaker.default_settings = SpeechSettings(speech_settings)
                    self.speaker.alt_voice_settings = alt_speech_settings
                elif request.HasField("speak"):
                    logging.info(
                        "Received gRPC speak request: " + str(request.speak))
                    print("Received gRPC speak request")

                    speech_settings = SpeechSettings(request.speak.speech_settings) if request.speak.HasField("speech_settings") else None
                    await self.speaker.speak(request.speak.message, speech_settings)
                elif request.HasField("stop_speaking"):
                    logging.info(
                        "Received gRPC stop_speaking request")
                    print("Received gRPC stop_speaking request")

                    self.speaker.stop_speaking()
                elif request.HasField("shutdown"):
                    logging.info(
                        "Received gRPC shutdown request")
                    print("Received gRPC shutdown request")
                    break
                elif request.HasField("ping"):
                    print("Received gRPC ping")
                    logging.info(
                        "Received gRPC ping")
                    response = speech_service_pb2.SpeechServiceResponse()
                    #response.ping = speech_service_pb2.PingResponse()
                    response.ping.time = str(datetime.datetime.now())
                    await context.write(response)
            except Exception as e:
                logging.error(e)
                print_exception(value = e)
                error_details = speech_service_pb2.SpeechServiceError()
                error_details.original_request = request
                error_details.message = repr(e)
                response = speech_service_pb2.SpeechServiceResponse()
                response.error = error_details
                await self.response_queue.put(response)
                logging.error(e)

        self.shutdown_event.set()

    async def process_queue(self, context):
        while not self.shutdown_event.is_set():
            response = await self.response_queue.get()
            await context.write(response)


