import speech_recognition as sr
import pygame
import os
import json
import nltk
import time
import webbrowser
from datetime import datetime, date
import tkinter as tk
from gtts import gTTS
import sounddevice as sd
import random
import torch
from transformers import Wav2Vec2ForCTC, Wav2Vec2Processor, BertTokenizer, BertModel
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
from nltk.sentiment.vader import SentimentIntensityAnalyzer
import platform
import sys

# Έλεγχος συμβατότητας λειτουργικού συστήματος
if platform.system() != "Windows":
    print("Το πρόγραμμα είναι συμβατό μόνο με τα Windows.")
    sys.exit(1)

# Έλεγχος για CUDA
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# Φόρτωση Wav2Vec2 μοντέλου και processor
processor = Wav2Vec2Processor.from_pretrained("facebook/wav2vec2-base-960h")
model = Wav2Vec2ForCTC.from_pretrained("facebook/wav2vec2-base-960h").to(device)

# Φόρτωση των υπαρχόντων ερωτήσεων-απαντήσεων και ιστορικού συνομιλίας
def load_qa_pairs_and_history():
    qa_file = "qa_data.json"
    history_file = "conversation_history.json"

    qa_pairs = {}
    conversation_history = []

    if os.path.exists(qa_file):
        with open(qa_file, 'r', encoding='utf-8') as f:
            qa_pairs = json.load(f)

    if os.path.exists(history_file):
        with open(history_file, 'r', encoding='utf-8') as f:
            conversation_history = json.load(f)

    return qa_pairs, conversation_history

qa_pairs, conversation_history = load_qa_pairs_and_history()

# Συνάρτηση για ακρόαση και αναγνώριση ομιλίας
def listen():
    recognizer = sr.Recognizer()
    with sr.Microphone() as source:
        print("Ακούω...")
        recognizer.adjust_for_ambient_noise(source)
        audio = recognizer.listen(source)

    try:
        print("Αναγνώριση...")
        text = recognizer.recognize_google(audio, language="el-GR")
        print(f"Αναγνωρίστηκε: {text}")
        return text.lower()
    except sr.UnknownValueError:
        print("Δεν αναγνωρίστηκε η ομιλία")
        return ""
    except sr.RequestError as e:
        print(f"Σφάλμα στην υπηρεσία αναγνώρισης ομιλίας: {e}")
        return ""

# Συνάρτηση για αποθήκευση ερωτήσεων-απαντήσεων και ιστορικού συνομιλίας
def save_qa_pairs_and_history(qa_pairs, conversation_history):
    qa_file = "qa_data.json"
    history_file = "conversation_history.json"

    with open(qa_file, 'w', encoding='utf-8') as f:
        json.dump(qa_pairs, f, ensure_ascii=False, indent=4)

    with open(history_file, 'w', encoding='utf-8') as f:
        json.dump(conversation_history, f, ensure_ascii=False, indent=4)

# Συνάρτηση για ανάλυση και εξαγωγή χαρακτηριστικών από κείμενο
def analyze_text(text):
    tokens = word_tokenize(text.lower())
    tokens = [word for word in tokens if word.isalpha()]
    tokens = [word for word in tokens if word not in stopwords.words('greek')]
    lemmatizer = nltk.WordNetLemmatizer()
    tokens = [lemmatizer.lemmatize(word) for word in tokens]
    preprocessed_text = ' '.join(tokens)

    sia = SentimentIntensityAnalyzer()
    sentiment = sia.polarity_scores(text)['compound']

    return preprocessed_text, sentiment

# Συνάρτηση για παροχή προσαρμοσμένων απαντήσεων με βάση το ιστορικό συνομιλίας
def get_response_with_history(question, conversation_history):
    preprocessed_question, sentiment = analyze_text(question)

    relevant_history = [entry for entry in conversation_history if preprocessed_question in entry['question']]

    if relevant_history:
        if sentiment > 0:
            tone = "θετική"
        elif sentiment < 0:
            tone = "αρνητική"
        else:
            tone = "ουδέτερη"

        response = f"Η προηγούμενη απάντησή σας ήταν {tone}."
    else:
        response = get_response_with_bert(question)

    return response

# Μετατροπή κειμένου σε ομιλία και αναπαραγωγή
def speak(text):
    try:
        tts = gTTS(text=text, lang='el')
        filename = "voice.mp3"
        tts.save(filename)

        pygame.mixer.init()
        pygame.mixer.music.load(filename)
        pygame.mixer.music.play()

        while pygame.mixer.music.get_busy():
            pygame.time.Clock().tick(20)

        pygame.mixer.music.stop()
        pygame.mixer.quit()

        os.remove(filename)

    except Exception as e:
        print(f"Σφάλμα κατά την αναπαραγωγή ομιλίας: {e}. Παρακαλώ ελέγξτε τη ρύθμιση ήχου και προσπαθήστε ξανά.")

# Συνάρτηση για ηχογράφηση και αποθήκευση σημειώσεων
def record_note():
    RATE = 44100
    CHANNELS = 1
    RECORD_SECONDS = 10

    speak("Παρακαλώ, πείτε μου τον τίτλο της σημείωσης.")
    title = listen()
    if not title:
        speak("Δεν κατάλαβα τον τίτλο. Χρησιμοποιώ προεπιλεγμένο τίτλο.")
        title = "Σημείωση"

    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    WAVE_OUTPUT_FILENAME = f"{title}_{timestamp}.wav"

    speak(f"Ξεκινάω την ηχογράφηση για {RECORD_SECONDS} δευτερόλεπτα. Πείτε 'τέλος' όταν θέλετε να σταματήσετε νωρίτερα.")
    recording = sd.rec(int(RECORD_SECONDS * RATE), samplerate=RATE, channels=CHANNELS)
    sd.wait()

    speak("Η ηχογράφηση ολοκληρώθηκε.")
    
    wavfile.write(WAVE_OUTPUT_FILENAME, RATE, recording)
    
    speak(f"Η σημείωση αποθηκεύτηκε ως {WAVE_OUTPUT_FILENAME}")

# Λήψη απάντησης με χρήση BERT
def get_response_with_bert(question):
    preprocessed_question = preprocess_text(question)

    bert_model_name = "bert-base-uncased"
    bert_tokenizer = BertTokenizer.from_pretrained(bert_model_name)
    bert_model = BertModel.from_pretrained(bert_model_name)

    bert_inputs = bert_tokenizer(preprocessed_question, return_tensors="pt")
    bert_outputs = bert_model(**bert_inputs)
    last_hidden_state = bert_outputs.last_hidden_state

    question_embedding = torch.mean(last_hidden_state, dim=1).squeeze(0).detach().numpy()

    tfidf_vectorizer = TfidfVectorizer()
    questions = list(qa_pairs.keys())
    tfidf_matrix = tfidf_vectorizer.fit_transform(questions)
    tfidf_question = tfidf_vectorizer.transform([preprocessed_question])
    similarity_scores = cosine_similarity(tfidf_question, tfidf_matrix)

    similar_indices = similarity_scores[0].argsort()[-2:][::-1]
    similar_questions = [questions[index] for index in similar_indices]

    if similar_questions:
        return qa_pairs[similar_questions[0]]
    else:
        return "Δεν έχω ακόμα μάθει την απάντηση σε αυτή την ερώτηση. Μπορείτε να με διδάξετε!"

# Συνάρτηση για να ανοίξει το παράθυρο αποθήκης
def open_warehouse():
    warehouse_window = tk.Toplevel()
    warehouse_window.title("Αποθήκη Νοσοκομείου")
    warehouse_window.geometry("1200x1200")

    # Δημιουργία πλαισίου διαλόγου
    dialog_frame = tk.Frame(warehouse_window, height=600)
    dialog_frame.pack(fill=tk.X)

    dialog_label = tk.Label(dialog_frame, text="Πλαίσιο Διαλόγου", font=("Arial", 16))
    dialog_label.pack(pady=10)

    dialog_text = tk.Text(dialog_frame, height=25, width=150)
    dialog_text.pack(padx=10, pady=10)

    # Δημιουργία πλαισίου για τα κουμπιά
    button_frame = tk.Frame(warehouse_window)
    button_frame.pack(side=tk.BOTTOM, padx=10, pady=10)

    # Συνάρτηση για εκκαθάριση
    def clear_dialogue():
        dialog_text.delete(1.0, tk.END)

    # Δημιουργία κουμπιού «Clear»
    clear_button = tk.Button(button_frame, text="Clear", command=clear_dialogue, fg="red")
    clear_button.pack(side=tk.BOTTOM, anchor='se', padx=10, pady=10)

    # Δημιουργία πλαισίου για τα κουμπιά Ραφιών
    rack_button_frame = tk.Frame(button_frame)
    rack_button_frame.pack(side=tk.TOP, padx=10, pady=10)

    # Συνάρτηση για αναζήτηση φαρμάκων
    def search_drugs():
        try:
            with open('farmaka.json', 'r', encoding='utf-8') as f:
                drugs = json.load(f)
        except FileNotFoundError:
            tk.messagebox.showerror("Σφάλμα", "Το αρχείο farmaka.json δεν βρέθηκε!")
            return

        # Δημιουργεί νέο παράθυρο για την αναζήτηση
        search_window = tk.Toplevel()
        search_window.title("Αναζήτηση Φαρμάκων")
        
        tk.Label(search_window, text="Εισάγετε κωδικό ή όνομα φαρμάκου:").pack()

        search_entry = tk.Entry(search_window)
        search_entry.pack(pady=5)

        def perform_search():
            search_query = search_entry.get().strip()
            if not search_query:
                return
            
            # Έλεγχος στις στήλες του DataFrame
            results = [drug for drug in drugs if 
                       drug['Κωδικός'] == search_query or 
                       search_query in drug['Όνομα']]
            
            # Αν υπάρχουν αποτελέσματα
            if results:
                result_window = tk.Toplevel(search_window)
                result_window.title("Αποτελέσματα Αναζήτησης")

                result_text = tk.Text(result_window, wrap='word')
                result_text.pack()

                for drug in results:
                    result_text.insert(tk.END, f"Κατηγορία: {drug['Κατηγορία']}, Όνομα: {drug['Όνομα']}, "
                                                f"Κωδικός: {drug['Κωδικός']}, Ημερομηνία Λήξης: {drug['Ημερομηνία Λήξης']}, "
                                                f"Αποθέματα: {drug['Αποθέματα']}, Παρατηρήσεις: {drug['Παρατηρήσεις']}\n")
            else:
                tk.messagebox.showinfo("Αναζήτηση", "Δεν βρέθηκαν αποτελέσματα.")

        tk.Button(search_window, text="Αναζήτηση", command=perform_search).pack(pady=5)

    # Κουμπί "Φαρμακα"
    def button_action(rack_number):
        if rack_number == 1:  # 1 για Φάρμακα
            search_drugs()
        else:
            dialog_text.insert(tk.END, f"Εκτελείται η ενέργεια για Ράφι {rack_number}\n")

    # Δημιουργία κουμπιών Ραφιών (10 στην πρώτη σειρά και 10 στη δεύτερη)
    for i in range(1, 21):
        row = (i - 1) // 10  # Σειρά
        column = (i - 1) % 10  # Στήλη
        button = tk.Button(rack_button_frame, text=("Φάρμακα" if i == 1 else f"Ράφι {i}"),
                           command=lambda rack=i: button_action(rack))
        button.grid(row=row, column=column, padx=5, pady=5)

    warehouse_window.mainloop()

# Κύρια λειτουργία του εικονικού βοηθού
def main():
    speak("Γεια σας! Είμαι η Μία, ο εικονικός σας βοηθός. Πώς μπορώ να σας βοηθήσω σήμερα;")

    while True:
        command = listen()
        if command:
            if "άνοιξε αποθήκη" in command:
                open_warehouse()
            elif "έξοδος" in command or "σταμάτα" in command:
                speak("Αντίο! Ήταν χαρά μου που σας βοήθησα.")
                break
            # Άλλες εντολές...

if __name__ == "__main__":
    # Κατέβασμα πακέτων NLTK, αν δεν έχουν ήδη
    nltk.download('punkt')
    nltk.download('wordnet')
    nltk.download('stopwords')
    nltk.download('vader_lexicon')

    main()                     Όταν πατάω το κουμπί φάρμακα θέλω να μου βγάζεις τα αποτελέσματα από το αρχείο jason στο πλαίσιο διαλόγου και ταυτόχρονα να μου θα μου δίνει τη δυνατότητα να προσθέσω άλλη μία σειρά με τις ανάλογες μετανάλογο χαρακτηριστικά για το επόμενο φάρμακο δεν έχω ένα κουμπί να πατάω προσθήκη δεν έχω τη δυνατότητα να προσθέσω άλλη μία σειρά με το επόμενο φάρμακο