import redis
import RPi.GPIO as GPIO
import asyncio
import websockets
import sounddevice as sd
import numpy as np
import time
import re

def transcribe(audio_chunks):
    pass # (todo)

def say(text):
    # This should immediatly stop if button is pressed (if GPIO.input(18))
    pass # (todo)

# Connect to button
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# Set the duration and sample rate for the mic
chunk_duration = 0.5  # seconds
sample_rate = 44100  # Hz

# Set up Redis connection
r = redis.Redis(host='localhost', port=6379, db=0)

# Set up websocket connection
websocket = websockets.connect('ws://localhost:8765')

# This is so we only say() full sentences
accumulated_text = ""
def is_full_sentence(text):
    return text.endswith(('.', '!', '?'))
def split_into_sentences(text):
    return re.split(r'(?<=[.!?])\s+', text)

async def send_to_websocket(message):
    async with websocket as ws:
        await ws.send(message)

async def check_websocket():
    async with websocket as ws:
        message = await ws.recv()
        return message

def main():
    while True:

        # If the button is pushed down
        if not GPIO.input(18):

            # Tell websocket and core that the user is speaking
            send_to_websocket({"role": "user", "type": "message", "start": True}) # Standard start flag, required per streaming LMC protocol (https://docs.openinterpreter.com/guides/streaming-response)
            r.rpush('to_core', {"role": "user", "type": "message", "content": "stop"}) # Custom stop message. Core is not streaming LMC (it's static LMC) so doesn't require that ^ flag

            # Record audio from the microphone in chunks
            audio_chunks = []

            # Continue recording until the button is released
            while not GPIO.input(18):
                chunk = sd.rec(int(chunk_duration * sample_rate), samplerate=sample_rate, channels=2)
                sd.wait()  # Wait until recording is finished
                audio_chunks.append(chunk)

            # Transcribe
            text = transcribe(audio_chunks)

            message = {"role": "user", "type": "message", "content": text, "time": time.time()}

            # Send message to core and websocket
            r.rpush('to_core', message)
            send_to_websocket(message)

            # Send user message end flag to websocket, required per streaming LMC protocol
            send_to_websocket({"role": "user", "type": "message", "end": True})
        
        # Send out anything in the to_interface queue
        chunk = r.lpop('to_interface')
        if chunk:
            send_to_websocket(chunk)
            accumulated_text += chunk["content"]
            
            # Speak full sentences out loud
            sentences = split_into_sentences(accumulated_text)
            if is_full_sentence(sentences[-1]):
                for sentence in sentences:
                    say(sentence)
                accumulated_text = ""
            else:
                for sentence in sentences[:-1]:
                    say(sentence)
                accumulated_text = sentences[-1]
        else:
            say(accumulated_text)
            accumulated_text = ""

        message = check_websocket()
        if message:
            r.rpush('to_core', message)

if __name__ == "__main__":
    main()