#!/usr/bin/python3

import customtkinter as ctk
import tkinter as tk
import tkinter.messagebox as messagebox
import random
import os
import sys
import csv
from datetime import datetime
from transliterate import translit
from tkinter import filedialog, PhotoImage
import platform
import json 

def resource_path(relative_path):
    base_path = os.path.abspath(".")
    full_path = os.path.join(base_path, relative_path)  
    if not os.path.exists(full_path):
        raise FileNotFoundError(f"Resource not found: {full_path}")    
    return full_path

class QuizApp(ctk.CTk):

    def __init__(self):
        super().__init__()

        self.title("Учебный модуль") 
        self.geometry("600x650") 
        self.configure(bg="#333333") 
        self.protocol("WM_DELETE_WINDOW", self.on_closing) 
        self.icon_image = PhotoImage(file="/usr/share/pixmaps/ctwf.png")
        self.tk.call('wm', 'iconphoto', self._w, self.icon_image)
        self.topics = self._load_json_data('/usr/share/cttestB/quiz_topics.json', {})
        self.theory_content = []
        self.name_frame = ctk.CTkFrame(self, fg_color="#333333") 
        self.name_frame.pack(pady=20, fill="both", expand=True) 
        self.name_label = ctk.CTkLabel(self.name_frame, text="Введите ваше имя:", text_color="white", fg_color="#333333") 
        self.name_label.pack(pady=10) 
        self.name_entry = ctk.CTkEntry(self.name_frame, fg_color="#333333", text_color="white")
        self.name_entry.pack(pady=10)
        self.name_entry.focus_set()
        self.name_entry.bind("<Return>", lambda event: self.submit_name_button.invoke())
        self.submit_name_button = ctk.CTkButton(self.name_frame, text="Подтвердить", command=self.set_user_name) 
        self.submit_name_button.pack(pady=10) 
        self.mode_frame = ctk.CTkFrame(self, fg_color="#333333") 
        self.title_label = ctk.CTkLabel(self.mode_frame, text="Выберите режим:", text_color="white", fg_color="#333333")         
        self.theory_button = ctk.CTkButton(self.mode_frame, text="Теория", command=self.show_theory) 
        self.study_button = ctk.CTkButton(self.mode_frame, text="Обучение", command=self.study_mode)
        self.training_button = ctk.CTkButton(self.mode_frame, text="Тренировка", command=self.training_mode) 
        self.testing_button = ctk.CTkButton(self.mode_frame, text="Экзамен", command=self.testing_mode) 
        self.error_exercise_button = ctk.CTkButton(self.mode_frame, text="Повторение", command=self.start_error_exercise) 
        self.history_button = ctk.CTkButton(self.mode_frame, text="История", command=self.show_history)
        self.selected_topic = None
        self.after(100, self.name_entry.focus_set)
        self.topics = self.load_topics()
        self.theory_content = []
        self.selected_topics = []
        self.mode = None
        self.user_answers = {}
        self.correct_answers = 0
    
    def load_topics(self):
        path = resource_path('/usr/share/cttestB/quiz_topics.json')
        try:
            with open(path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            messagebox.showerror("Error", f"Quiz data load failed: {e}")
            return {}
    
    def _load_json_data(self, filename, default_value):
        try:
            path = resource_path(filename)
            with open(path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            messagebox.showerror("Error", f"Failed to load {filename}: {str(e)}")
            return default_value

    def load_theory_content(self):
        self.theory_content = self._load_json_data('/usr/share/cttestB/theory_content.json', [])
        self.update_theory_text()    
        
    
    def get_history_file_path(user_name):
        home_directory = os.path.expanduser("~")
        file_name = f"{user_name}_quiz_results.csv"
        return os.path.join(home_directory, file_name)
 

    def on_closing(self):
        self.quit()

    def training_mode(self):
        self.mode = "training"
        self.title("Тренировка")
        self.show_topic_selection()

    def testing_mode(self):
        self.mode = "testing"
        self.title("Экзамен")
        self.show_topic_selection()
    
    def show_main_menu(self):
        self.name_frame.pack_forget()
        self.mode_frame.pack(pady=20, fill="both", expand=True)
        self.title("Меню")        
        self.title_label.pack(pady=10)
        self.theory_button.pack(pady=10)
        self.study_button.pack(pady=10)
        self.training_button.pack(pady=10)
        self.testing_button.pack(pady=10)
        self.error_exercise_button.pack(pady=10)
        self.history_button.pack(pady=10)

    def set_user_name(self):
        try:
            user_name_russian = self.name_entry.get()           
            if not user_name_russian.strip():
                messagebox.showerror("Error", "Имя не может быть пустым. Пожалуйста, введите ваше имя.")
                return
            if not user_name_russian.isalpha():
                messagebox.showerror("Error", "Имя может содержать только буквы. Пожалуйста, введите корректное имя.")
                return
            self.user_name = translit(user_name_russian, 'ru', reversed=True)
            self.show_main_menu()
        except Exception as e:
            print(f"Error during transliteration: {e}")
            messagebox.showerror("Error", f"Ошибка при транслитерации имени: {e}")

    def show_topic_selection(self):
        for widget in self.mode_frame.winfo_children():
            widget.destroy()

        self.topic_label = ctk.CTkLabel(self.mode_frame, text="Выберите одну или несколько тем", text_color="white", fg_color="#333333")
        self.topic_label.pack(pady=10)

        self.topic_listbox = tk.Listbox(self.mode_frame, selectmode="multiple", bg="#333333", fg="white")
        for topic in self.topics.keys():
            self.topic_listbox.insert("end", topic)
        self.topic_listbox.pack(pady=10)
        self.topic_listbox.focus_set()
        self.select_button = ctk.CTkButton(self.mode_frame, text="Выбрать", command=self.set_topics)
        self.select_button.pack(pady=10)
    
        self.back_button = ctk.CTkButton(self.mode_frame, text="Назад", command=self.show_main_screen)
        self.back_button.pack(pady=10)
        self.topic_listbox.bind("<Return>", lambda event: self.select_button.invoke())

    def set_topics(self):
        self.selected_topics = [self.topic_listbox.get(i) for i in self.topic_listbox.curselection()]
    
        if not self.selected_topics:
            messagebox.showwarning("Предупреждение", "Пожалуйста, выберите хотя бы одну тему.")
        else:
            self.show_question_screen()

    def show_question_screen(self):
        for widget in self.mode_frame.winfo_children():
            widget.destroy()

        self.question_label = ctk.CTkLabel(self.mode_frame, text="Укажите количество вопросов", text_color="white",
                                           fg_color="#333333")
        self.question_label.pack(pady=10)

        self.question_entry = ctk.CTkEntry(self.mode_frame, fg_color="#333333", text_color="white")
        self.question_entry.pack(pady=10)
        self.question_entry.focus_set()
        self.start_button = ctk.CTkButton(self.mode_frame, text="Начать", command=self.start_quiz)
        self.start_button.pack(pady=10)
        
        self.back_button = ctk.CTkButton(self.mode_frame, text="Назад", command=self.show_topic_selection) 
        self.back_button.pack(pady=10)
        self.question_entry.bind("<Return>", lambda event: self.start_button.invoke())
        
    def start_quiz(self):
        try:
            self.num_questions = int(self.question_entry.get())
            if self.num_questions <= 0:
                messagebox.showerror("Ошибка", "Введите корректное число вопросов больше нуля.")
                return
            total_questions = sum(len(self.topics[topic]) for topic in self.selected_topics)
            if self.num_questions > total_questions:
                self.num_questions = total_questions
            if self.num_questions == 0:
                messagebox.showinfo("Ошибка", "По этой теме нет вопросов")
                return
        except ValueError:
            messagebox.showerror("Ошибка", "Введите корректное число вопросов больше нуля.")
            return
        
        self.current_question = 0
        self.user_answers = {}
        self.correct_answers = 0
        self.all_questions = [(q, self.topics[topic][q]) for topic in self.selected_topics for q in self.topics[topic]]
        random.shuffle(self.all_questions)
        self.show_next_question()
        
    def study_mode(self):
        self.mode = "study"
        self.title("Обучение")
        self.show_study_topic_selection()
        
    def show_study_topic_selection(self):
        for widget in self.mode_frame.winfo_children():
            widget.destroy()

        self.topic_label = ctk.CTkLabel(self.mode_frame, text="Выберите тему", text_color="white", fg_color="#333333")
        self.topic_label.pack(pady=10)

        self.topic_listbox = tk.Listbox(self.mode_frame, selectmode="single", bg="#333333", fg="white")
        for topic in self.topics.keys():
            self.topic_listbox.insert("end", topic)
        self.topic_listbox.pack(pady=10)
        self.topic_listbox.focus_set()
        self.select_button = ctk.CTkButton(self.mode_frame, text="Выбрать", command=self.set_study_topic)
        self.select_button.pack(pady=10)

        self.back_button = ctk.CTkButton(self.mode_frame, text="Назад", command=self.show_main_screen)
        self.back_button.pack(pady=10)
        self.topic_listbox.bind("<Return>", lambda event: self.select_button.invoke())

    
    def show_next_question(self):
        if self.current_question < self.num_questions:
            question, data = self.all_questions[self.current_question]
            self.current_question += 1

            for widget in self.mode_frame.winfo_children():
                widget.destroy()

            self.question_label = ctk.CTkLabel(self.mode_frame, text=question, text_color="white", fg_color="#333333")
            self.question_label.pack(pady=10)

            self.answer_entry = ctk.CTkEntry(self.mode_frame, fg_color="#333333", text_color="white")
            self.answer_entry.pack(pady=10)
            self.answer_entry.focus_set()
            self.answer_entry.bind("<Return>", lambda event: self.submit_button.invoke())
            self.submit_button = ctk.CTkButton(self.mode_frame, text="Далее",
                                               command=lambda: self.check_answer(question, data))
            self.submit_button.pack(pady=10)
            self.main_menu_button = ctk.CTkButton(self.mode_frame, text="Меню", command=self.show_main_screen)
            self.main_menu_button.pack(pady=10)            
        else:
            self.show_results()

    def set_study_topic(self):
        selected_indices = self.topic_listbox.curselection()
        if not selected_indices:
            messagebox.showwarning("Предупреждение", "Пожалуйста, выберите тему.")
            return
    
        self.selected_topic = self.topic_listbox.get(selected_indices[0])
        self.num_questions = len(self.topics[self.selected_topic])
        self.current_question = 0
        self.user_answers = {}
        self.correct_answers = 0
        self.all_questions = [(q, self.topics[self.selected_topic][q]) for q in self.topics[self.selected_topic]]
        self.show_next_question()
    
    def check_answer(self, question, data):
        user_answer = self.answer_entry.get().strip().lower()
        correct_answer = data["answer"].strip().lower()
        explanation = data["explanation"]

        if self.mode in ["training", "study"] and user_answer != correct_answer:
            messagebox.showinfo("Неверно", f"Правильный ответ: {correct_answer}\nОбъяснение: {explanation}")

        if self.mode in ["testing"] and user_answer == correct_answer:
            self.correct_answers += 1

        self.user_answers[question] = {
            'user_answer': user_answer,
            'correct_answer': correct_answer,
            'explanation': explanation,
            'correct': user_answer == correct_answer
        }       
        
        if not self.user_answers[question]['correct']:
            self.log_error(question, correct_answer, explanation)
        
        self.show_next_question()


    def log_error(self, question, correct_answer, explanation):
        home_directory = os.path.expanduser("~")  
        error_file_path = os.path.join(home_directory, f"{self.user_name}_errors.csv")

        existing_errors = set()

        if os.path.exists(error_file_path):
            try:
                with open(error_file_path, mode='r', encoding='utf-8') as file:
                    reader = csv.reader(file)
                    for row in reader:
                        if row:
                            existing_errors.add(row[0])
            except Exception as e:
                messagebox.showerror("Ошибка", f"Не удалось загрузить ошибки: {str(e)}")

        if question not in existing_errors:
            try:
                with open(error_file_path, mode='a', newline='', encoding='utf-8') as file:
                    writer = csv.writer(file)
                    writer.writerow([question, correct_answer, explanation])
            except Exception as e:
                messagebox.showerror("Ошибка", f"Не удалось сохранить ошибки: {str(e)}")


    def start_error_exercise(self):
        self.title("Повторение")
        self.selected_topics = []
        self.mode = "error_exercise"
        self.all_questions = self.load_errors()
        if not self.all_questions:            
            return
        self.num_questions = len(self.all_questions)
        random.shuffle(self.all_questions)
        self.current_question = 0
        self.user_answers = {}
        self.correct_answers = 0
        self.show_next_question()
        
    def load_errors(self):
        home_directory = os.path.expanduser("~")  
        error_file_path = os.path.join(home_directory, f"{self.user_name}_errors.csv")
        errors = []

        try:
            with open(error_file_path, mode='r', encoding='utf-8') as file:
                reader = csv.reader(file)
                for row in reader:
                    if len(row) == 3:  
                        question, correct_answer, explanation = row
                        errors.append((question, {'answer': correct_answer, 'explanation': explanation}))
        except FileNotFoundError:
            messagebox.showinfo("Ошибка", "Нет вопросов для упражнений на основе ошибок")
        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось загрузить ошибки: {str(e)}")
        
        return errors


    def show_results(self):
        for widget in self.mode_frame.winfo_children():
            widget.destroy()

        result_text = ""

        if self.mode == "testing":
            if self.num_questions > 0:
                percentage_score = (self.correct_answers / self.num_questions) * 100
                result_text = f"Ваш результат: {percentage_score:.2f}%\n\n"

                if percentage_score <= 50:
                    result_text += "Плохо\n"
                elif percentage_score <= 66:
                    result_text += "Удовлетворительно\n"
                elif percentage_score <= 80:
                    result_text += "Хорошо\n"
                else:
                    result_text += "Замечательно\n"

                    if self.correct_answers == self.num_questions:
                        result_text += "\nЛучше не бывает\n"
            else:
                result_text = "Нет ответов."

            self.save_results(percentage_score)
        else:  
            all_correct = True
            result_text = ""
            for question, data in self.user_answers.items():
                if not data['correct']:
                    result_text += (
                        f"Вопрос: {question}\n"
                        f"Ваш ответ: {data['user_answer']}\n"
                        f"Правильный ответ: {data['correct_answer']}\n"
                        f"Комментарий: {data['explanation']}\n\n"
                    )
                    all_correct = False

            if all_correct:
                result_text = "Все ответы правильные! Отличная работа!"

            self.save_results()

        self.history_text = tk.Text(self.mode_frame, bg="#333333", fg="white", wrap="word")
        self.history_text.pack(pady=10, fill="both", expand=True)
        self.history_text.insert("end", result_text)
        self.history_text.config(state="disabled")  

        self.scrollbar = tk.Scrollbar(self.mode_frame, command=self.history_text.yview)
        self.scrollbar.pack(side="right", fill="y")
        self.history_text.config(yscrollcommand=self.scrollbar.set)

        self.back_button = ctk.CTkButton(self.mode_frame, text="Меню", command=self.show_main_screen)
        self.back_button.pack(pady=10)


    
    def show_main_screen(self):
        for widget in self.mode_frame.winfo_children():
            widget.destroy()
        self.title("Меню")
        self.title_label = ctk.CTkLabel(self.mode_frame, text="Выберите режим", text_color="white", fg_color="#333333")
        self.title_label.pack(pady=10)

        self.theory_button = ctk.CTkButton(self.mode_frame, text="Теория", command=self.show_theory)
        self.theory_button.pack(pady=10)
        
        self.study_button = ctk.CTkButton(self.mode_frame, text="Обучение", command=self.study_mode)
        self.study_button.pack(pady=10)

        self.training_button = ctk.CTkButton(self.mode_frame, text="Тренировка", command=self.training_mode)
        self.training_button.pack(pady=10)

        self.testing_button = ctk.CTkButton(self.mode_frame, text="Экзамен", command=self.testing_mode)
        self.testing_button.pack(pady=10)

        self.error_exercise_button = ctk.CTkButton(self.mode_frame, text="Повторение",
                                                   command=self.start_error_exercise)
        self.error_exercise_button.pack(pady=10)

        self.history_button = ctk.CTkButton(self.mode_frame, text="История", command=self.show_history)
        self.history_button.pack(pady=10)

    def save_results(self, percentage_score=None):
        date_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        home_directory = os.path.expanduser("~")
        file_path = os.path.join(home_directory, f"{self.user_name}_quiz_results.csv")

        try:
            with open(file_path, mode='a', newline='', encoding='utf-8') as file:
                writer = csv.writer(file)
                if file.tell() == 0:  
                    writer.writerow(["Date", "Mode", "Topics", "Score (%)", "User Answers"])

                answers_summary = "; ".join([
                    f"{question}: Ваш ответ: {data['user_answer']}" +
                    (f", Правильный ответ: {data['correct_answer']}" if not data['correct'] else "")
                    for question, data in self.user_answers.items()
                ])

                if self.mode == "testing":
                    mode_display = "тестирование"
                elif self.mode == "training":
                    mode_display = "тренировка"
                else:
                    mode_display = "обучение"

                if self.mode == "testing" and percentage_score is not None:
                    writer.writerow([date_str, mode_display, ", ".join(self.selected_topics), f"{percentage_score:.2f}", answers_summary])
                elif self.mode == "training":
                    writer.writerow([date_str, mode_display, ", ".join(self.selected_topics), "N/A", answers_summary])
        except Exception as e:
            messagebox.showerror("Ошибка", f"Не удалось сохранить результаты: {str(e)}")

    def update_theory_text(self, event=None):
        search_query = self.search_var.get().strip().lower()
        filtered_content = [f"{key.strip()} — {value.strip()}\n" for key, value in self.theory_content if search_query in key.lower()]

        self.theory_text.config(state="normal")
        self.theory_text.delete("1.0", "end")
        self.theory_text.insert("end", "".join(filtered_content))
        self.theory_text.config(state="disabled")

    def show_theory(self):
        for widget in self.mode_frame.winfo_children():
            widget.destroy()

        self.title("Теория")

        self.search_var = tk.StringVar()
        self.search_entry = ctk.CTkEntry(self.mode_frame, textvariable=self.search_var, fg_color="#333333", text_color="white")
        self.search_entry.pack(pady=10)
        self.search_entry.bind("<KeyRelease>", self.update_theory_text)

        self.theory_text = tk.Text(self.mode_frame, wrap="word", bg="#333333", fg="white")
        self.theory_text.pack(expand=True, fill="both")

        self.scrollbar = tk.Scrollbar(self.mode_frame, command=self.theory_text.yview)
        self.scrollbar.pack(side="right", fill="y")

        self.theory_text.config(yscrollcommand=self.scrollbar.set)

        self.back_button = ctk.CTkButton(self.mode_frame, text="Меню", command=self.show_main_screen)
        self.back_button.pack(pady=10)

        self.load_theory_content()


    def show_history(self):
        for widget in self.mode_frame.winfo_children():
            widget.destroy()
        
        self.title("Результаты")
        self.history_label = ctk.CTkLabel(
            self.mode_frame, 
            text="История тренировок и экзаменов", 
            text_color="white", 
            fg_color="#333333"
        )
        self.history_label.pack(pady=10)

        self.history_text = tk.Text(self.mode_frame, bg="#333333", fg="white", wrap="word")
        self.history_text.pack(pady=10, fill="both", expand=True)

        home_directory = os.path.expanduser("~")
        file_path = os.path.join(home_directory, f"{self.user_name}_quiz_results.csv")

        try:
            with open(file_path, mode='r', encoding='utf-8') as file:
                reader = csv.reader(file)
                next(reader)  
                for row in reader:
                    answers = row[4].split("; ")
                    answers_formatted = "\n".join(answers)
                    self.history_text.insert(
                        "end",
                        f"Дата: {row[0]}\nРежим: {row[1]}\nТемы: {row[2]}\nОценка (%): {row[3]}\nОтветы:\n{answers_formatted}\n\n"
                    )
        except FileNotFoundError:
            self.history_text.insert("end", "Нет записей.")
        except Exception as e:
            self.history_text.insert("end", f"Ошибка: {str(e)}")

        self.back_button = ctk.CTkButton(self.mode_frame, text="Меню", command=self.show_main_screen)
        self.back_button.pack(pady=10)

if __name__ == "__main__":
    app = QuizApp()
    app.mainloop()
