import pygame
import speech_recognition as sr
from openai import OpenAI
from pathlib import Path
import os
import io
import soundfile as sf
import sounddevice as sd
import random
import csv
import threading
import signal
import sys

pygame.mixer.init()

# Set your OpenAI API key here
api_key = 'sk-proj-wwWaxim1Qt13uqzSS0xjT3BlbkFJK0rZvx78AJiWG3Ot7d3S'
client = OpenAI(api_key=api_key)

# Function to read text file
def read_text_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        return file.read()

# Function to read CSV file
def read_csv_file(file_path):
    content = ""
    try:
        with open(file_path, mode='r', encoding='utf-8') as file:
            reader = csv.reader(file)
            for row in reader:
                content += ' '.join(row) + ' '
    except Exception as e:
        print(f"Error reading CSV file: {e}")
    return content

# Function to recognize speech from the microphone
def recognize_speech():
    recognizer = sr.Recognizer()
    with sr.Microphone() as source:
        print("Listening...")
        audio = recognizer.listen(source)
        try:
            print("Recognizing...")
            text = recognizer.recognize_google(audio, language='en-US')
            audio_files = ["ty.mp3", "th.mp3", "sure.mp3", "sure1.mp3"]
            # Select a random audio file
            random_audio = random.choice(audio_files)
            
            # Load and play the selected random audio
            pygame.mixer.music.load(random_audio)
            pygame.mixer.music.play()
            print(f"You said: {text}")
            return text
        except sr.UnknownValueError:
            print("Sorry, I did not understand that.")
            return None
        except sr.RequestError:
            print("Sorry, there was an error with the speech recognition service.")
            return None

# Function to create messages for OpenAI API
def create_messages(question, file_content):
    return [
        {"role": "system", "content": "Your name is Futurebot. You were created by Sooraj and team who develops innovative projects in IoT future lab at Vodafone. You work with Tim, Sooraj, and Priya along with other team members Laura, Sven, Thomas, and Stephie. You are from T-E-T-I team. Your manager is Teja. You are a lab tour guide who explains and answers about IoT use cases in Vodafone. You have to create and complete explanations and answers in a meaningful way under 150 tokens. Do not say greetings. Do not say any calculations. Directly say the result."},
        {"role": "user", "content": f"{file_content}\n\nQ: {question}\nA:"}
    ]

# Function to get a response from OpenAI
def get_response_from_openai(messages):
    stream = client.chat.completions.create(
        model="gpt-4o",
        max_tokens=150,
        temperature=0.5,
        messages=messages,
        stream=True,
    )
    for chunk in stream:
        if chunk.choices[0].delta.content is not None:
            yield chunk.choices[0].delta.content

# Function to generate and play speech in chunks
def generate_speech(text):
    if text.strip():  # Only generate speech if the text is not empty
        spoken_response = client.audio.speech.create(
            model="tts-1",
            voice="alloy",
            input=text
        )

        buffer = io.BytesIO()
        for chunk in spoken_response.iter_bytes(chunk_size=4096):
            buffer.write(chunk)
        buffer.seek(0)

        with sf.SoundFile(buffer, 'r') as sound_file:
            data = sound_file.read(dtype='int16')
            sd.play(data, sound_file.samplerate)
            sd.wait()

# Main function to handle user query
def chatbot(question, text_file_path, csv_file_path):
    text_content = read_text_file(text_file_path)
    csv_content = read_csv_file(csv_file_path)
    combined_content = text_content + ' ' + csv_content
    messages = create_messages(question, combined_content)
    response_generator = get_response_from_openai(messages)
    print("Answer: ", end="")
    accumulated_response = ""
    for response_chunk in response_generator:
        accumulated_response += response_chunk
        if '.' in response_chunk or len(accumulated_response) > 500:  # Check for sentence end or length
            print(accumulated_response, end="", flush=True)
            generate_speech(accumulated_response)
            accumulated_response = ""  # Reset accumulated response for the next chunk
    if accumulated_response:  # Generate speech for any remaining text
        print(accumulated_response, end="", flush=True)
        generate_speech(accumulated_response)

# Function to detect wake word
def detect_wake_word(stop_event):
    recognizer = sr.Recognizer()
    wake_word = "hello futurebot"
    with sr.Microphone() as source:
        while not stop_event.is_set():
            print("Waiting for wake word...")
            audio = recognizer.listen(source)
            try:
                text = recognizer.recognize_google(audio, language='en-US').lower()
                if wake_word in text:
                    print("Wake word detected!")
                    question = recognize_speech()
                    if question:
                        chatbot(question, text_file_path, csv_file_path)
                    else:
                        print("Sorry, I didn't get that. Please ask again.")
            except sr.UnknownValueError:
                continue
            except sr.RequestError:
                print("Sorry, there was an error with the speech recognition service.")
                continue

def signal_handler(signal, frame):
    print("Exiting program...")
    stop_event.set()
    sys.exit(0)

if __name__ == "__main__":
    text_file_path = 'Allinone.txt'  # Path to your text file
    csv_file_path = 'device_data.csv'  # Path to your CSV file

    # Create an event to stop the wake word detection loop
    stop_event = threading.Event()

    # Set up signal handler for graceful exit
    signal.signal(signal.SIGINT, signal_handler)

    # Run wake word detection in a separate thread
    wake_word_thread = threading.Thread(target=detect_wake_word, args=(stop_event,))
    wake_word_thread.daemon = True
    wake_word_thread.start()

    # Keep the main thread alive
    wake_word_thread.join()
