#!/usr/bin/env python3
# Created by esl4kids.by

import wx
import os
import sys
import json
import csv
import random
from datetime import datetime, timedelta
from transliterate import translit

APP_TITLE = "Учебный модуль"
WINDOW_SIZE = (800, 750)
MIN_WINDOW_SIZE = (800, 750)
BG_COLOR = "#333333"
DEFAULT_FONT_FAMILY = "Arial"

PRIMARY_COLOR = "#3a7ebf"
HOVER_COLOR = "#325882"
SECONDARY_COLOR = "#2d5b8a"
SECONDARY_HOVER_COLOR = "#1e3f63"
BUTTON_BG_COLOR = "#444444"
BUTTON_TEXT_COLOR = "white"
ACCENT_COLOR_KNOW = "#2e7d32"
ACCENT_COLOR_NOT_SURE = "#ed6c02"
ACCENT_COLOR_NOT_KNOW = "#d32f2f"

QUIZ_TOPICS_A_PATH = '/usr/share/cttest/quiz_topicsA.json'
QUIZ_TOPICS_B_PATH = '/usr/share/cttest/quiz_topicsB.json'
THEORY_CONTENT_PATH = '/usr/share/cttest/theory_content.json'
APP_ICON_PATH = '/usr/share/icons/hicolor/48x48/apps/cttest.png'

def get_resource_path(relative_path):
    if os.path.isabs(relative_path):
        return relative_path

    base_path = os.path.dirname(os.path.abspath(sys.argv[0]))
    full_path = os.path.join(base_path, relative_path)

    if not os.path.exists(full_path):
        wx.MessageBox(f"Ресурс не найден: {full_path}", "Ошибка", wx.OK | wx.ICON_ERROR)
        raise FileNotFoundError(f"Resource not found: {full_path}")
    return full_path

class QuizFileManager:
    def __init__(self, user_name):
        self.user_name = user_name
        self.home_dir = os.path.expanduser("~")

    def _get_results_filename(self, part):
        return os.path.join(self.home_dir, f"{self.user_name}_quiz_results_part{part}.csv")

    def _get_errors_filename(self, part):
        return os.path.join(self.home_dir, f"{self.user_name}_errors_part{part}.csv")

    def load_json_data(self, filepath, default_value):
        try:
            path = get_resource_path(filepath)
            with open(path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except FileNotFoundError:
            wx.MessageBox(f"Файл не найден: {filepath}. Использование значений по умолчанию.", "Предупреждение", wx.OK | wx.ICON_WARNING)
            return default_value
        except json.JSONDecodeError:
            wx.MessageBox(f"Ошибка декодирования JSON в файле: {filepath}. Проверьте формат.", "Ошибка", wx.OK | wx.ICON_ERROR)
            return default_value
        except Exception as e:
            wx.MessageBox(f"Не удалось загрузить {filepath}: {str(e)}", "Ошибка", wx.OK | wx.ICON_ERROR)
            return default_value

    def save_results(self, user_answers, current_part, mode, selected_topics, selected_topic=None, percentage_score=None):
        file_path = self._get_results_filename(current_part)
        mistaken_answers = []
        for question, data in user_answers.items():
            if not data['correct']:
                mistaken_answers.append(f"{question}: Ваш ответ: {data['user_answer']}, Правильный ответ: {data['correct_answer']}")

        if not mistaken_answers:
            return

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

                mode_display_map = {
                    "testing": "тестирование",
                    "training": "тренировка",
                    "study": "обучение",
                    "error_exercise": "повторение"
                }
                mode_display = mode_display_map.get(mode, "N/A")
                part_display = "A" if current_part == "A" else "B"
                answers_summary = "; ".join(mistaken_answers)
                topics_summary = ", ".join(selected_topics) if selected_topics else selected_topic

                row_data = [
                    datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                    part_display,
                    mode_display,
                    topics_summary,
                    f"{percentage_score:.2f}" if mode == "testing" and percentage_score is not None else "N/A",
                    answers_summary
                ]
                writer.writerow(row_data)
        except Exception as e:
            wx.MessageBox(f"Не удалось сохранить результаты: {str(e)}", "Ошибка", wx.OK | wx.ICON_ERROR)

    def load_errors(self, part):
        file_path = self._get_errors_filename(part)
        errors = []
        try:
            with open(file_path, mode='r', encoding='utf-8') as file:
                reader = csv.reader(file)
                for row in reader:
                    if len(row) >= 4:
                        question = row[0]
                        correct_answer = row[1]
                        explanation = row[2]
                        q_type = row[3] if len(row) > 3 else 'text'
                        correct_count = int(row[4]) if len(row) > 4 else 0

                        question_data = {
                            'answer': correct_answer,
                            'explanation': explanation,
                            'type': q_type,
                            'correct_count': correct_count
                        }
                        if q_type == 'mcq' and len(row) > 5:
                            question_data['options'] = row[5:]
                        errors.append((question, question_data))
        except FileNotFoundError:
            wx.MessageBox(f"Нет ошибок для повторения в части {part}", "Информация", wx.OK | wx.ICON_INFORMATION)
        except Exception as e:
            wx.MessageBox(f"Не удалось загрузить ошибки: {str(e)}", "Ошибка", wx.OK | wx.ICON_ERROR)
        return errors

    def log_error(self, current_part, question, data):
        error_file_path = self._get_errors_filename(current_part)

        existing_errors = {}
        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[row[0]] = {
                                'answer': row[1],
                                'explanation': row[2],
                                'type': row[3],
                                'correct_count': int(row[4]) if len(row) > 4 else 0,
                                'options': row[5:] if len(row) > 5 else []
                            }
            except Exception as e:
                wx.MessageBox("Не удалось загрузить существующие ошибки: " + str(e), "Ошибка", wx.OK | wx.ICON_ERROR)

        if question not in existing_errors:
            try:
                with open(error_file_path, mode='a', newline='', encoding='utf-8') as file:
                    writer = csv.writer(file)
                    row = [
                        question,
                        data["answer"],
                        data.get("explanation", ""),
                        data.get("type", "text"),
                        0
                    ]
                    if data.get("type") == "mcq" and "options" in data:
                        row.extend(data["options"])
                    writer.writerow(row)
            except Exception as e:
                wx.MessageBox("Не удалось сохранить ошибку: " + str(e), "Ошибка", wx.OK | wx.ICON_ERROR)

    def update_error_counter(self, current_part, question, is_correct):
        error_file_path = self._get_errors_filename(current_part)
        temp_file_path = error_file_path + ".tmp"

        updated_rows = []
        question_removed = False

        try:
            with open(error_file_path, mode='r', encoding='utf-8') as infile, \
                 open(temp_file_path, mode='w', newline='', encoding='utf-8') as outfile:

                reader = csv.reader(infile)
                writer = csv.writer(outfile)

                for row in reader:
                    if not row:
                        continue

                    if row[0] == question:
                        current_count = int(row[4]) if len(row) > 4 else 0
                        if is_correct:
                            new_count = current_count + 1
                            if new_count >= 3:
                                question_removed = True
                                continue
                            else:
                                if len(row) > 4:
                                    row[4] = str(new_count)
                                else:
                                    row.append(str(new_count))
                                writer.writerow(row)
                        else:
                            if len(row) > 4:
                                row[4] = "0"
                            else:
                                row.append("0")
                            writer.writerow(row)
                    else:
                        writer.writerow(row)

            os.replace(temp_file_path, error_file_path)
            return question_removed

        except Exception as e:
            wx.MessageBox("Не удалось обновить счетчик ошибок: " + str(e), "Ошибка", wx.OK | wx.ICON_ERROR)
            if os.path.exists(temp_file_path):
                os.remove(temp_file_path)
            return False

    def init_user_theory_file(self, user_theory_path, part_theory):
        if not os.path.exists(user_theory_path):
            user_theory = []
            for term, definition in part_theory:
                user_theory.append({
                    "term": term,
                    "definition": definition,
                    "status": "nd",
                    "next_review": None
                })
            with open(user_theory_path, 'w', encoding='utf-8') as f:
                json.dump(user_theory, f, ensure_ascii=False, indent=2)

    def load_user_theory(self, user_theory_path):
        try:
            with open(user_theory_path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except (FileNotFoundError, json.JSONDecodeError) as e:
            wx.MessageBox("Не удалось загрузить данные для повторения: " + str(e) + ". Создание нового файла.", "Ошибка", wx.OK | wx.ICON_ERROR)
            return []

    def save_user_theory_data(self, user_theory_path, all_data):
        try:
            with open(user_theory_path, 'w', encoding='utf-8') as f:
                json.dump(all_data, f, ensure_ascii=False, indent=2)
        except Exception as e:
            wx.MessageBox("Не удалось сохранить данные для повторения: " + str(e), "Ошибка", wx.OK | wx.ICON_ERROR)

class QuizFrame(wx.Frame):
    def __init__(self, parent, title):
        super().__init__(parent, title=title, size=WINDOW_SIZE, style=wx.DEFAULT_FRAME_STYLE | wx.RESIZE_BORDER)

        self.SetMinSize(MIN_WINDOW_SIZE)
        self.SetBackgroundColour(wx.Colour(BG_COLOR))
        self.Bind(wx.EVT_CLOSE, self.OnClose)

        self._load_app_icon()
        self._initialize_attributes()

        self.name_panel = wx.Panel(self)
        self.part_panel = wx.Panel(self)
        self.mode_panel = wx.Panel(self)

        self.name_panel.SetBackgroundColour(wx.Colour(BG_COLOR))
        self.part_panel.SetBackgroundColour(wx.Colour(BG_COLOR))
        self.mode_panel.SetBackgroundColour(wx.Colour(BG_COLOR))

        self._create_name_frame()

        self.file_manager = None
        self._load_initial_quiz_data()

        self.part_panel.Hide()
        self.mode_panel.Hide()

        self.main_sizer = wx.BoxSizer(wx.VERTICAL)
        self.main_sizer.Add(self.name_panel, 1, wx.EXPAND | wx.ALL, 0)
        self.main_sizer.Add(self.part_panel, 1, wx.EXPAND | wx.ALL, 0)
        self.main_sizer.Add(self.mode_panel, 1, wx.EXPAND | wx.ALL, 0)
        self.SetSizer(self.main_sizer)
        self.Layout()

    def _load_app_icon(self):
        try:
            icon_path = get_resource_path(APP_ICON_PATH)
            icon = wx.Icon(icon_path, wx.BITMAP_TYPE_PNG)
            self.SetIcon(icon)
        except Exception as e:
            print(f"Could not load icon: {e}")
            wx.MessageBox(f"Не удалось загрузить иконку приложения: {e}", "Ошибка", wx.OK | wx.ICON_ERROR)

    def _initialize_attributes(self):
        self.current_part = None
        self.part_a_topics = {}
        self.part_b_topics = {}
        self.part_theory = []
        self.topics = {}
        self.theory_content = []
        self.selected_topics = []
        self.selected_topic = None
        self.mode = None
        self.user_answers = {}
        self.correct_answers = 0
        self.all_questions = []
        self.current_question_index = 0
        self.num_questions = 0
        self.user_name = ""
        self.user_theory_file_path = None
        self.last_selected_button = None
        self.option_buttons = []
        self.current_review_words = []
        self.current_review_index = 0

    def _create_name_frame(self):
        panel = self.name_panel

        sizer = wx.BoxSizer(wx.VERTICAL)


        font_large_bold = wx.Font(24, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, DEFAULT_FONT_FAMILY)
        font_medium = wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_small = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)

        sizer.Add(wx.StaticText(panel, label=""), 0, wx.ALIGN_CENTER | wx.TOP, 20)

        name_label = wx.StaticText(panel, label="Введите ваше имя:")
        name_label.SetForegroundColour(wx.Colour("white"))
        name_label.SetFont(font_medium)
        sizer.Add(name_label, 0, wx.ALIGN_CENTER | wx.TOP, 10)

        self.name_entry = wx.TextCtrl(panel, size=(300, 40), style=wx.TE_PROCESS_ENTER)
        self.name_entry.SetBackgroundColour(wx.Colour(BUTTON_BG_COLOR))
        self.name_entry.SetForegroundColour(wx.Colour("white"))
        self.name_entry.SetFont(font_small)
        sizer.Add(self.name_entry, 0, wx.ALIGN_CENTER | wx.TOP, 10)
        self.name_entry.Bind(wx.EVT_TEXT_ENTER, self._on_submit_name)

        submit_button = wx.Button(panel, label="Подтвердить", size=(200, 40))
        submit_button.SetFont(font_small)
        submit_button.SetBackgroundColour(wx.Colour(PRIMARY_COLOR))
        submit_button.SetForegroundColour(wx.Colour(BUTTON_TEXT_COLOR))
        submit_button.Bind(wx.EVT_BUTTON, self._on_submit_name)
        sizer.Add(submit_button, 0, wx.ALIGN_CENTER | wx.TOP, 20)

        panel.SetSizer(sizer)
        sizer.Layout()
        self.name_entry.SetFocus()

    def _load_initial_quiz_data(self):
        self.file_manager = QuizFileManager("")
        try:
            self.part_a_topics = self.file_manager.load_json_data(QUIZ_TOPICS_A_PATH, {})
            self.part_b_topics = self.file_manager.load_json_data(QUIZ_TOPICS_B_PATH, {})
            self.part_theory = self.file_manager.load_json_data(THEORY_CONTENT_PATH, [])

            for topic in self.part_a_topics.values():
                for q_data in topic.values():
                    q_data.setdefault('type', 'mcq')
            for topic in self.part_b_topics.values():
                for q_data in topic.values():
                    q_data.setdefault('type', 'text')
        except Exception as e:
            wx.MessageBox(f"Не удалось загрузить основные данные викторины: {str(e)}", "Ошибка", wx.OK | wx.ICON_ERROR)

    def OnClose(self, event):
        if wx.MessageBox("Вы уверены, что хотите выйти?", "Выход", wx.OK | wx.CANCEL | wx.ICON_QUESTION) == wx.OK:
            self.Destroy()
        else:
            event.Veto()

    def _on_submit_name(self, event):
        user_name_russian = self.name_entry.GetValue().strip()
        if not user_name_russian:
            wx.MessageBox("Имя не может быть пустым.", "Ошибка", wx.OK | wx.ICON_ERROR)
            return
        if not all(c.isalpha() or c.isspace() for c in user_name_russian):
            wx.MessageBox("Имя может содержать только буквы и пробелы.", "Ошибка", wx.OK | wx.ICON_ERROR)
            return

        self.user_name = translit(user_name_russian, 'ru', reversed=True)
        self.file_manager = QuizFileManager(self.user_name)
        self.user_theory_file_path = os.path.join(self.file_manager.home_dir, f"{self.user_name}_theory.json")
        self.file_manager.init_user_theory_file(self.user_theory_file_path, self.part_theory)
        self._show_part_selection()

    def _clear_and_pack_frame(self, frame_to_pack):
        for panel in [self.name_panel, self.part_panel, self.mode_panel]:
            if panel != frame_to_pack:
                panel.Hide()

        for child in frame_to_pack.GetChildren():
            child.Destroy()

        frame_to_pack.Show()
        self.Layout()

    def _show_part_selection(self):
        self._clear_and_pack_frame(self.part_panel)
        panel = self.part_panel
        sizer = wx.BoxSizer(wx.VERTICAL)
        font_large = wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, DEFAULT_FONT_FAMILY)
        font_button = wx.Font(15, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)

        welcome_label = wx.StaticText(panel, label=f"Привет, {self.name_entry.GetValue()}!")
        welcome_label.SetForegroundColour(wx.Colour("white"))
        welcome_label.SetFont(font_large)
        sizer.Add(welcome_label, 0, wx.ALIGN_CENTER | wx.TOP, 20)

        part_label = wx.StaticText(panel, label="Выберите часть:")
        part_label.SetForegroundColour(wx.Colour("white"))
        part_label.SetFont(font_large)
        sizer.Add(part_label, 0, wx.ALIGN_CENTER | wx.TOP, 20)

        button_a = wx.Button(panel, label="Часть А", size=(250, 50))
        button_a.SetFont(font_button)
        button_a.SetBackgroundColour(wx.Colour(PRIMARY_COLOR))
        button_a.SetForegroundColour(wx.Colour(BUTTON_TEXT_COLOR))
        button_a.Bind(wx.EVT_BUTTON, lambda evt: self._set_current_part('A'))
        sizer.Add(button_a, 0, wx.ALIGN_CENTER | wx.TOP, 15)

        button_b = wx.Button(panel, label="Часть Б", size=(250, 50))
        button_b.SetFont(font_button)
        button_b.SetBackgroundColour(wx.Colour(PRIMARY_COLOR))
        button_b.SetForegroundColour(wx.Colour(BUTTON_TEXT_COLOR))
        button_b.Bind(wx.EVT_BUTTON, lambda evt: self._set_current_part('B'))
        sizer.Add(button_b, 0, wx.ALIGN_CENTER | wx.TOP, 15)

        theory_button = wx.Button(panel, label="Теория", size=(250, 50))
        theory_button.SetFont(font_button)
        theory_button.SetBackgroundColour(wx.Colour(SECONDARY_COLOR))
        theory_button.SetForegroundColour(wx.Colour(BUTTON_TEXT_COLOR))
        theory_button.Bind(wx.EVT_BUTTON, self._show_theory_mode)
        sizer.Add(theory_button, 0, wx.ALIGN_CENTER | wx.TOP, 15)

        spaced_repetition_button = wx.Button(panel, label="Повторение слов", size=(250, 50))
        spaced_repetition_button.SetFont(font_button)
        spaced_repetition_button.SetBackgroundColour(wx.Colour(SECONDARY_COLOR))
        spaced_repetition_button.SetForegroundColour(wx.Colour(BUTTON_TEXT_COLOR))
        spaced_repetition_button.Bind(wx.EVT_BUTTON, self._show_spaced_repetition)
        sizer.Add(spaced_repetition_button, 0, wx.ALIGN_CENTER | wx.TOP, 15)

        panel.SetSizer(sizer)
        panel.Layout()
        self.Layout()

    def _show_theory_mode(self, event):
        self.current_part = None
        self._show_theory()
        pass

    def _set_current_part(self, part):
        self.current_part = part
        self.topics = self.part_a_topics if part == 'A' else self.part_b_topics
        self.theory_content = self.part_theory
        self.SetTitle(f"Часть {part}")
        self._show_main_menu()

    def _show_main_menu(self):
        self._clear_and_pack_frame(self.mode_panel)
        panel = self.mode_panel
        sizer = wx.BoxSizer(wx.VERTICAL)
        font_large = wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, DEFAULT_FONT_FAMILY)
        font_medium = wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_button = wx.Font(15, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_back_button = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)

        def create_mode_button(parent, label, handler):
            btn = wx.Button(parent, label=label, size=(250, 45))
            btn.SetFont(font_button)
            btn.SetBackgroundColour(wx.Colour(SECONDARY_COLOR))
            btn.SetForegroundColour(wx.Colour(BUTTON_TEXT_COLOR))
            btn.Bind(wx.EVT_BUTTON, handler)
            return btn

        part_type_text = "Множественный выбор" if self.current_part == 'A' else "Заполнить пропуски"
        type_label = wx.StaticText(panel, label=part_type_text)
        type_label.SetForegroundColour(wx.Colour("white"))
        type_label.SetFont(font_large)
        sizer.Add(type_label, 0, wx.ALIGN_CENTER | wx.TOP, 10)

        mode_label = wx.StaticText(panel, label="Выберите режим:")
        mode_label.SetForegroundColour(wx.Colour("white"))
        mode_label.SetFont(font_medium)
        sizer.Add(mode_label, 0, wx.ALIGN_CENTER | wx.TOP, 20)

        sizer.Add(create_mode_button(panel, "Обучение", self._set_mode_and_show_topics("study")), 0, wx.ALIGN_CENTER | wx.TOP, 12)
        sizer.Add(create_mode_button(panel, "Тренировка", self._set_mode_and_show_topics("training")), 0, wx.ALIGN_CENTER | wx.TOP, 12)
        sizer.Add(create_mode_button(panel, "Экзамен", self._set_mode_and_show_topics("testing")), 0, wx.ALIGN_CENTER | wx.TOP, 12)
        sizer.Add(create_mode_button(panel, "Повторение", self._start_error_exercise), 0, wx.ALIGN_CENTER | wx.TOP, 12)
        sizer.Add(create_mode_button(panel, "История", self._show_history), 0, wx.ALIGN_CENTER | wx.TOP, 12)

        back_button = wx.Button(panel, label="Назад", size=(200, 40))
        back_button.SetFont(font_back_button)
        back_button.SetBackgroundColour(wx.Colour("#555555"))
        back_button.SetForegroundColour(wx.Colour("white"))
        back_button.Bind(wx.EVT_BUTTON, lambda evt: self._show_part_selection())
        sizer.Add(back_button, 0, wx.ALIGN_CENTER | wx.TOP, 30)

        panel.SetSizer(sizer)
        panel.Layout()
        self.Layout()

    def _set_mode_and_show_topics(self, mode):
        def handler(event):
            self.mode = mode
            self.SetTitle(f"{mode.capitalize()} - Часть {'A' if self.current_part == 'A' else 'B'}")
            if mode == "study":
                self._show_study_topic_selection()
            else:
                self._show_quiz_topic_selection()
        return handler

    def _show_quiz_topic_selection(self):
        self._clear_and_pack_frame(self.mode_panel)
        panel = self.mode_panel
        sizer = wx.BoxSizer(wx.VERTICAL)
        font_label = wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_listbox = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_button = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)

        label = wx.StaticText(panel, label="Выберите одну или несколько тем:")
        label.SetForegroundColour(wx.Colour("white"))
        label.SetFont(font_label)
        sizer.Add(label, 0, wx.ALIGN_CENTER | wx.TOP, 15)

        self.topic_listbox = wx.ListBox(
            panel,
            style=wx.LB_EXTENDED | wx.LB_NEEDED_SB,
            size=(400, 200)
        )
        self.topic_listbox.SetBackgroundColour(wx.Colour(BUTTON_BG_COLOR))
        self.topic_listbox.SetForegroundColour(wx.Colour("white"))
        self.topic_listbox.SetFont(font_listbox)

        sorted_topics = sorted(self.topics.keys())
        self.topic_listbox.Set(sorted_topics)

        sizer.Add(self.topic_listbox, 1, wx.EXPAND | wx.ALL, 10)

        button_sizer = wx.BoxSizer(wx.HORIZONTAL)

        select_button = wx.Button(panel, label="Выбрать", size=(150, 40))
        select_button.SetFont(font_button)
        select_button.SetBackgroundColour(wx.Colour(PRIMARY_COLOR))
        select_button.SetForegroundColour(wx.Colour(BUTTON_TEXT_COLOR))
        select_button.Bind(wx.EVT_BUTTON, self._set_quiz_topics)
        button_sizer.Add(select_button, 0, wx.ALL, 5)

        self.topic_listbox.Bind(wx.EVT_KEY_DOWN, self._on_listbox_key_down)

        back_button = wx.Button(panel, label="Назад", size=(150, 40))
        back_button.SetFont(font_button)
        back_button.SetBackgroundColour(wx.Colour("#555555"))
        back_button.SetForegroundColour(wx.Colour("white"))
        back_button.Bind(wx.EVT_BUTTON, lambda evt: self._show_main_menu())
        button_sizer.Add(back_button, 0, wx.ALL, 5)

        sizer.Add(button_sizer, 0, wx.ALIGN_CENTER | wx.TOP, 20)

        panel.SetSizer(sizer)
        panel.Layout()
        self.Layout()
        self.topic_listbox.SetFocus()

    def _on_listbox_key_down(self, event):
        if event.GetKeyCode() == wx.WXK_RETURN:
            self._set_quiz_topics(None)
        event.Skip()

    def _set_quiz_topics(self, event):
        selected_indices = self.topic_listbox.GetSelections()
        self.selected_topics = [self.topic_listbox.GetString(i) for i in selected_indices]

        if not self.selected_topics:
            wx.MessageBox("Пожалуйста, выберите хотя бы одну тему.", "Предупреждение", wx.OK | wx.ICON_WARNING)
            return
        else:
            self._show_question_count_screen()

    def _show_question_count_screen(self):
        self._clear_and_pack_frame(self.mode_panel)
        panel = self.mode_panel
        sizer = wx.BoxSizer(wx.VERTICAL)
        font_label = wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_entry = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_button = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)

        label = wx.StaticText(panel, label="Укажите количество вопросов:")
        label.SetForegroundColour(wx.Colour("white"))
        label.SetFont(font_label)
        sizer.Add(label, 0, wx.ALIGN_CENTER | wx.TOP, 20)

        self.question_entry = wx.TextCtrl(panel, size=(200, 40), style=wx.TE_PROCESS_ENTER)
        self.question_entry.SetBackgroundColour(wx.Colour(BUTTON_BG_COLOR))
        self.question_entry.SetForegroundColour(wx.Colour("white"))
        self.question_entry.SetFont(font_entry)
        sizer.Add(self.question_entry, 0, wx.ALIGN_CENTER | wx.TOP, 10)
        self.question_entry.SetFocus()

        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        start_button = wx.Button(panel, label="Начать", size=(150, 40))
        start_button.SetFont(font_button)
        start_button.SetBackgroundColour(wx.Colour(PRIMARY_COLOR))
        start_button.SetForegroundColour(wx.Colour(BUTTON_TEXT_COLOR))
        start_button.Bind(wx.EVT_BUTTON, self._start_quiz_session)
        button_sizer.Add(start_button, 0, wx.ALL, 5)
        self.question_entry.Bind(wx.EVT_TEXT_ENTER, self._start_quiz_session)

        back_button = wx.Button(panel, label="Назад", size=(150, 40))
        back_button.SetFont(font_button)
        back_button.SetBackgroundColour(wx.Colour("#555555"))
        back_button.SetForegroundColour(wx.Colour("white"))
        back_button.Bind(wx.EVT_BUTTON, lambda evt: self._show_quiz_topic_selection())
        button_sizer.Add(back_button, 0, wx.ALL, 5)

        sizer.Add(button_sizer, 0, wx.ALIGN_CENTER | wx.TOP, 20)

        panel.SetSizer(sizer)
        panel.Layout()
        self.Layout()

    def _start_quiz_session(self, event):
        try:
            self.num_questions = int(self.question_entry.GetValue())
            if self.num_questions <= 0:
                wx.MessageBox("Введите число больше нуля.", "Ошибка", wx.OK | wx.ICON_ERROR)
                return

            self.all_questions = []
            for topic in self.selected_topics:
                for q, data in self.topics[topic].items():
                    self.all_questions.append((q, data))

            total_available_questions = len(self.all_questions)

            if self.num_questions > total_available_questions:
                self.num_questions = total_available_questions
                wx.MessageBox(f"Доступно только {total_available_questions} вопросов. Будет использовано максимальное количество.", "Информация", wx.OK | wx.ICON_INFORMATION)

            if self.num_questions == 0:
                wx.MessageBox("По выбранным темам нет вопросов.", "Ошибка", wx.OK | wx.ICON_ERROR)
                return

            random.shuffle(self.all_questions)
            self.all_questions = self.all_questions[:self.num_questions]

            self.current_question_index = 0
            self.user_answers = {}
            self.correct_answers = 0
            self._show_next_question()

        except ValueError:
            wx.MessageBox("Пожалуйста, введите число.", "Ошибка", wx.OK | wx.ICON_ERROR)
            return

    def _show_study_topic_selection(self):
        self._clear_and_pack_frame(self.mode_panel)
        panel = self.mode_panel
        sizer = wx.BoxSizer(wx.VERTICAL)
        font_label = wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_listbox = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_button = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)

        label = wx.StaticText(panel, label="Выберите тему для изучения:")
        label.SetForegroundColour(wx.Colour("white"))
        label.SetFont(font_label)
        sizer.Add(label, 0, wx.ALIGN_CENTER | wx.TOP, 15)

        self.topic_listbox = wx.ListBox(
            panel,
            style=wx.LB_SINGLE | wx.LB_NEEDED_SB,
            size=(400, 200)
        )
        self.topic_listbox.SetBackgroundColour(wx.Colour(BUTTON_BG_COLOR))
        self.topic_listbox.SetForegroundColour(wx.Colour("white"))
        self.topic_listbox.SetFont(font_listbox)

        sorted_topics = sorted(self.topics.keys())
        self.topic_listbox.Set(sorted_topics)

        sizer.Add(self.topic_listbox, 1, wx.EXPAND | wx.ALL, 10)

        button_sizer = wx.BoxSizer(wx.HORIZONTAL)

        select_button = wx.Button(panel, label="Выбрать", size=(150, 40))
        select_button.SetFont(font_button)
        select_button.SetBackgroundColour(wx.Colour(PRIMARY_COLOR))
        select_button.SetForegroundColour(wx.Colour(BUTTON_TEXT_COLOR))
        select_button.Bind(wx.EVT_BUTTON, self._set_study_topic)
        button_sizer.Add(select_button, 0, wx.ALL, 5)
        self.topic_listbox.Bind(wx.EVT_KEY_DOWN, self._on_study_listbox_key_down)

        back_button = wx.Button(panel, label="Назад", size=(150, 40))
        back_button.SetFont(font_button)
        back_button.SetBackgroundColour(wx.Colour("#555555"))
        back_button.SetForegroundColour(wx.Colour("white"))
        back_button.Bind(wx.EVT_BUTTON, lambda evt: self._show_main_menu())
        button_sizer.Add(back_button, 0, wx.ALL, 5)

        sizer.Add(button_sizer, 0, wx.ALIGN_CENTER | wx.TOP, 20)

        panel.SetSizer(sizer)
        panel.Layout()
        self.Layout()
        self.topic_listbox.SetFocus()

    def _on_study_listbox_key_down(self, event):
        if event.GetKeyCode() == wx.WXK_RETURN:
            self._set_study_topic(None)
        event.Skip()

    def _set_study_topic(self, event):
        selected_index = self.topic_listbox.GetSelection()
        if selected_index == wx.NOT_FOUND:
            wx.MessageBox("Пожалуйста, выберите тему.", "Предупреждение", wx.OK | wx.ICON_WARNING)
            return

        self.selected_topic = self.topic_listbox.GetString(selected_index)
        self.all_questions = [(q, self.topics[self.selected_topic][q]) for q in self.topics[self.selected_topic]]
        self.num_questions = len(self.all_questions)
        self.current_question_index = 0
        self.user_answers = {}
        self.correct_answers = 0
        self._show_next_question()

    def _show_next_question(self):
        self.option_buttons = []
        self.last_selected_button = None

        if self.current_question_index < self.num_questions:
            question_text, original_data = self.all_questions[self.current_question_index]
            self.current_original_question_data = original_data
            self.current_question_index += 1

            question_data = original_data.copy()

            if question_data.get('type') == 'mcq' and 'options' in question_data:
                shuffled_options = question_data['options'].copy()
                random.shuffle(shuffled_options)
                question_data['shuffled_options'] = shuffled_options

            self._clear_and_pack_frame(self.mode_panel)
            panel = self.mode_panel
            sizer = wx.BoxSizer(wx.VERTICAL)

            font_question_count = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
            font_question_text = wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
            font_option_button = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
            font_entry = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
            font_button = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)

            q_count_label = wx.StaticText(panel, label=f"Вопрос {self.current_question_index} из {self.num_questions}")
            q_count_label.SetForegroundColour(wx.Colour("white"))
            q_count_label.SetFont(font_question_count)
            sizer.Add(q_count_label, 0, wx.ALIGN_CENTER | wx.TOP, 10)

            question_label = wx.StaticText(panel, label=question_text, style=wx.ST_NO_AUTORESIZE)
            question_label.SetForegroundColour(wx.Colour("white"))
            question_label.SetFont(font_question_text)
            question_label.Wrap(700)
            sizer.Add(question_label, 0, wx.ALIGN_CENTER | wx.TOP | wx.LEFT | wx.RIGHT, 20)

            if question_data.get('type') == 'mcq' and 'shuffled_options' in question_data:
                options_vbox = wx.BoxSizer(wx.VERTICAL)

                for i, option in enumerate(question_data['shuffled_options']):
                    btn = wx.Button(
                        panel, label=option,
                        size=(600, 40),
                        style=wx.ALIGN_LEFT
                    )
                    btn.SetFont(font_option_button)
                    btn.SetBackgroundColour(wx.Colour(PRIMARY_COLOR if i % 2 == 0 else SECONDARY_COLOR))
                    btn.SetForegroundColour(wx.Colour(BUTTON_TEXT_COLOR))
                    btn.Bind(wx.EVT_BUTTON, lambda evt, o=option: self._select_mcq_answer(o))
                    options_vbox.Add(btn, 0, wx.EXPAND | wx.LEFT | wx.RIGHT | wx.BOTTOM, 5)
                    self.option_buttons.append(btn)

                sizer.Add(options_vbox, 1, wx.EXPAND | wx.ALL, 10)

                self.answer_entry = wx.TextCtrl(panel, style=wx.TE_READONLY)
                self.answer_entry.Hide()

            else:
                self.answer_entry = wx.TextCtrl(
                    panel,
                    size=(500, 40),
                    style=wx.TE_PROCESS_ENTER
                )
                self.answer_entry.SetBackgroundColour(wx.Colour(BUTTON_BG_COLOR))
                self.answer_entry.SetForegroundColour(wx.Colour("white"))
                sizer.Add(self.answer_entry, 0, wx.ALIGN_CENTER | wx.TOP, 20)
                self.answer_entry.SetFocus()
                self.answer_entry.Bind(wx.EVT_TEXT_ENTER, lambda evt: self._check_current_answer(question_text, question_data))

            button_sizer = wx.BoxSizer(wx.HORIZONTAL)

            self.submit_button = wx.Button(panel, label="Далее", size=(150, 40))
            self.submit_button.SetFont(font_button)
            self.submit_button.SetBackgroundColour(wx.Colour(PRIMARY_COLOR))
            self.submit_button.SetForegroundColour(wx.Colour(BUTTON_TEXT_COLOR))
            self.submit_button.Bind(wx.EVT_BUTTON, lambda evt: self._check_current_answer(question_text, question_data))
            button_sizer.Add(self.submit_button, 0, wx.ALL, 5)

            menu_button = wx.Button(panel, label="Меню", size=(150, 40))
            menu_button.SetFont(font_button)
            menu_button.SetBackgroundColour(wx.Colour("#555555"))
            menu_button.SetForegroundColour(wx.Colour("white"))
            menu_button.Bind(wx.EVT_BUTTON, lambda evt: self._show_main_menu())
            button_sizer.Add(menu_button, 0, wx.ALL, 5)

            sizer.Add(button_sizer, 0, wx.ALIGN_CENTER | wx.TOP, 20)

            panel.SetSizer(sizer)
            panel.Layout()
            self.Layout()

        else:
            self._show_results()

    def _select_mcq_answer(self, selected_option):
        self.answer_entry.SetValue(selected_option)

        if self.last_selected_button:
            try:
                btn_index = self.option_buttons.index(self.last_selected_button)
                self.last_selected_button.SetBackgroundColour(wx.Colour(PRIMARY_COLOR if btn_index % 2 == 0 else SECONDARY_COLOR))
                self.last_selected_button.SetLabel(self.last_selected_button.GetLabel())
            except ValueError:
                pass

        for btn in self.option_buttons:
            if btn.GetLabel() == selected_option:
                btn.SetBackgroundColour(wx.Colour("#1a3b5a"))
                btn.SetLabel(btn.GetLabel())
                self.last_selected_button = btn
                break

        if self.mode == "testing":
            question_text, question_data = self.all_questions[self.current_question_index - 1]
            self._check_current_answer(question_text, question_data)

    def _check_current_answer(self, question_text, question_data):
            user_answer = self.answer_entry.GetValue().strip().lower()
            correct_answer = question_data["answer"].strip().lower()
            explanation = question_data.get("explanation", "")
            is_correct = (user_answer == correct_answer)

            if self.mode in ["training", "study"] and not is_correct:
                wx.MessageBox(f"Правильный ответ: {correct_answer}\n\nОбъяснение: {explanation}", "Неверно", wx.OK | wx.ICON_INFORMATION)

            if self.mode == "testing" and is_correct:
                self.correct_answers += 1

            self.user_answers[question_text] = {
                'user_answer': user_answer,
                'correct_answer': correct_answer,
                'explanation': explanation,
                'correct': is_correct
            }

            if self.mode == "error_exercise":
                question_removed = self.file_manager.update_error_counter(self.current_part, question_text, is_correct)
                if question_removed and self.current_question_index > len(self.all_questions) - 1:
                    self._show_results()
                    return
            elif not is_correct:
                self.file_manager.log_error(self.current_part, question_text, self.current_original_question_data)

            wx.CallAfter(self._show_next_question)

    def _start_error_exercise(self, event):
            self.SetTitle(f"Повторение ошибок - Часть {'A' if self.current_part == 'A' else 'B'}")
            self.selected_topics = []
            self.mode = "error_exercise"

            self.all_questions = self.file_manager.load_errors(self.current_part)

            if not self.all_questions:
                self._show_main_menu()
                return

            self.num_questions = len(self.all_questions)
            random.shuffle(self.all_questions)
            self.current_question_index = 0
            self.user_answers = {}
            self.correct_answers = 0
            self._show_next_question()

    def _show_results(self):
        self._clear_and_pack_frame(self.mode_panel)
        panel = self.mode_panel
        sizer = wx.BoxSizer(wx.VERTICAL)

        result_text_content = ""
        font_result_text = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_button = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)

        if self.mode == "testing":
            if self.num_questions > 0:
                percentage_score = (self.correct_answers / self.num_questions) * 100
                result_text_content = f"Ваш результат: {percentage_score:.2f}%\n\n"
                if percentage_score <= 50: result_text_content += "Плохо\n"
                elif percentage_score <= 66: result_text_content += "Удовлетворительно\n"
                elif percentage_score <= 80: result_text_content += "Хорошо\n"
                else: result_text_content += "Замечательно\n"
                if self.correct_answers == self.num_questions: result_text_content += "\nЛучше не бывает!\n"
                self.file_manager.save_results(self.user_answers, self.current_part, self.mode, self.selected_topics, percentage_score=percentage_score)
            else:
                result_text_content = "Нет ответов."
        else:
            all_correct = True
            for question, data in self.user_answers.items():
                if not data['correct']:
                    result_text_content += (
                        f"Вопрос: {question}\n"
                        f"Ваш ответ: {data['user_answer']}\n"
                        f"Правильный ответ: {data['correct_answer']}\n"
                        f"Комментарий: {data['explanation']}\n\n"
                    )
                    all_correct = False
            if not all_correct:
                self.file_manager.save_results(self.user_answers, self.current_part, self.mode, self.selected_topics, self.selected_topic)
            else:
                result_text_content = "Все ответы правильные! Отличная работа!"

        history_display = wx.TextCtrl(
            panel,
            style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL | wx.VSCROLL,
            size=(-1, 300)
        )
        history_display.SetBackgroundColour(wx.Colour(BUTTON_BG_COLOR))
        history_display.SetForegroundColour(wx.Colour("white"))
        history_display.SetFont(font_result_text)
        history_display.SetValue(result_text_content)

        sizer.Add(history_display, 1, wx.EXPAND | wx.ALL, 10)

        button_sizer = wx.BoxSizer(wx.HORIZONTAL)

        menu_button = wx.Button(panel, label="Меню", size=(150, 40))
        menu_button.SetFont(font_button)
        menu_button.SetBackgroundColour(wx.Colour(PRIMARY_COLOR))
        menu_button.SetForegroundColour(wx.Colour(BUTTON_TEXT_COLOR))
        menu_button.Bind(wx.EVT_BUTTON, lambda evt: self._show_main_menu())
        button_sizer.Add(menu_button, 0, wx.ALL, 5)

        if self.mode == "testing":
            restart_button = wx.Button(panel, label="Повторить", size=(150, 40))
            restart_button.SetFont(font_button)
            restart_button.SetBackgroundColour(wx.Colour(PRIMARY_COLOR))
            restart_button.SetForegroundColour(wx.Colour(BUTTON_TEXT_COLOR))
            restart_button.Bind(wx.EVT_BUTTON, lambda evt: self._restart_test())
            button_sizer.Add(restart_button, 0, wx.ALL, 5)

        sizer.Add(button_sizer, 0, wx.ALIGN_CENTER | wx.TOP, 20)

        panel.SetSizer(sizer)
        panel.Layout()
        self.Layout()

    def _restart_test(self):
        self.current_question_index = 0
        self.user_answers = {}
        self.correct_answers = 0
        random.shuffle(self.all_questions)
        self._show_next_question()

    def _show_theory(self):
        self._clear_and_pack_frame(self.mode_panel)
        self.SetTitle("Теория")
        panel = self.mode_panel
        sizer = wx.BoxSizer(wx.VERTICAL)
        font_entry = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_button = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_text_area = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)

        search_sizer = wx.BoxSizer(wx.HORIZONTAL)

        self.search_entry = wx.TextCtrl(
            panel,
            size=(400, 40),
            style=wx.TE_PROCESS_ENTER,
            value="Поиск по теории..."
        )
        self.search_entry.SetBackgroundColour(wx.Colour(BUTTON_BG_COLOR))
        self.search_entry.SetForegroundColour(wx.Colour("white"))
        self.search_entry.SetFont(font_entry)
        search_sizer.Add(self.search_entry, 1, wx.EXPAND | wx.ALL, 5)
        self.search_entry.Bind(wx.EVT_TEXT, self._update_theory_text)
        self.search_entry.Bind(wx.EVT_SET_FOCUS, self._clear_search_placeholder)
        self.search_entry.Bind(wx.EVT_KILL_FOCUS, self._set_search_placeholder)

        clear_button = wx.Button(panel, label="Очистить", size=(100, 40))
        clear_button.SetFont(font_button)
        clear_button.SetBackgroundColour(wx.Colour("#555555"))
        clear_button.SetForegroundColour(wx.Colour("white"))
        clear_button.Bind(wx.EVT_BUTTON, self._clear_theory_search)
        search_sizer.Add(clear_button, 0, wx.ALL, 5)

        sizer.Add(search_sizer, 0, wx.EXPAND | wx.ALL, 10)

        self.theory_text_widget = wx.TextCtrl(
            panel,
            style=wx.TE_MULTILINE | wx.TE_READONLY | wx.VSCROLL,
            size=(-1, 400)
        )
        self.theory_text_widget.SetBackgroundColour(wx.Colour(BUTTON_BG_COLOR))
        self.theory_text_widget.SetForegroundColour(wx.Colour("white"))
        self.theory_text_widget.SetFont(font_text_area)
        sizer.Add(self.theory_text_widget, 1, wx.EXPAND | wx.ALL, 10)

        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        back_button = wx.Button(panel, label="Назад", size=(150, 40))
        back_button.SetFont(font_button)
        back_button.SetBackgroundColour(wx.Colour("#555555"))
        back_button.SetForegroundColour(wx.Colour("white"))
        back_button.Bind(wx.EVT_BUTTON, lambda evt: self._show_part_selection())
        button_sizer.Add(back_button, 0, wx.ALL, 5)
        sizer.Add(button_sizer, 0, wx.ALIGN_CENTER | wx.TOP, 20)

        panel.SetSizer(sizer)
        panel.Layout()
        self.Layout()

        self._update_theory_text()

    def _clear_search_placeholder(self, event):
        if self.search_entry.GetValue() == "Поиск по теории...":
            self.search_entry.SetValue("")
            self.search_entry.SetForegroundColour(wx.Colour("white"))
        event.Skip()

    def _set_search_placeholder(self, event):
        if not self.search_entry.GetValue():
            self.search_entry.SetValue("Поиск по теории...")
            self.search_entry.SetForegroundColour(wx.Colour("grey"))
        event.Skip()

    def _clear_theory_search(self, event):
        self.search_entry.SetValue("")
        self._update_theory_text()

    def _update_theory_text(self, event=None):
        search_query = self.search_entry.GetValue().strip().lower()
        if search_query == "поиск по теории...".lower():
            search_query = ""

        filtered_content = [
            f"{key.strip()} — {value.strip()}\n\n"
            for key, value in self.part_theory
            if search_query in key.lower() or search_query in value.lower()
        ]

        self.theory_text_widget.SetEditable(True)
        self.theory_text_widget.Clear()

        if filtered_content:
            self.theory_text_widget.SetValue("".join(filtered_content))
        else:
            self.theory_text_widget.SetValue("Ничего не найдено.")
        self.theory_text_widget.SetEditable(False)

    def _show_history(self, event):
        self._clear_and_pack_frame(self.mode_panel)
        self.SetTitle(f"История - Часть {'A' if self.current_part == 'A' else 'B'}")
        panel = self.mode_panel
        sizer = wx.BoxSizer(wx.VERTICAL)
        font_title = wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, DEFAULT_FONT_FAMILY)
        font_history_text = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_button = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)

        title_label = wx.StaticText(panel, label=f"История части {'A' if self.current_part == 'A' else 'B'}")
        title_label.SetForegroundColour(wx.Colour("white"))
        title_label.SetFont(font_title)
        sizer.Add(title_label, 0, wx.ALIGN_CENTER | wx.TOP, 15)

        history_display = wx.TextCtrl(
            panel,
            style=wx.TE_MULTILINE | wx.TE_READONLY | wx.VSCROLL,
            size=(-1, 450)
        )
        history_display.SetBackgroundColour(wx.Colour(BUTTON_BG_COLOR))
        history_display.SetForegroundColour(wx.Colour("white"))
        history_display.SetFont(font_history_text)
        sizer.Add(history_display, 1, wx.EXPAND | wx.ALL, 10)

        file_path = self.file_manager._get_results_filename(self.current_part)
        display_content = ""

        try:
            with open(file_path, mode='r', encoding='utf-8') as file:
                reader = csv.reader(file)
                header = next(reader, None)
                if header is None:
                    display_content = f"Нет записей в истории части {self.current_part}."
                else:
                    history_entries = list(reader)
                    history_entries.reverse()

                    if not history_entries:
                        display_content = f"Нет записей в истории части {self.current_part}."
                    else:
                        for row in history_entries:
                            if len(row) >= 6:
                                answers = row[5].split("; ")
                                answers_formatted = "\n   • ".join([""] + answers)

                                display_content += (
                                    f"Дата: {row[0]}\n"
                                    f"Режим: {row[2]}\n"
                                    f"Темы: {row[3]}\n"
                                    f"Оценка: {row[4]}%\n"
                                    f"Ответы:{answers_formatted}\n"
                                    f"{'-'*50}\n\n"
                                )
        except FileNotFoundError:
            display_content = f"Нет записей в истории части {self.current_part}."
        except Exception as e:
            display_content = f"Ошибка при загрузке истории: {str(e)}"

        history_display.SetValue(display_content)
        history_display.SetEditable(False)

        button_sizer = wx.BoxSizer(wx.HORIZONTAL)
        menu_button = wx.Button(panel, label="Меню", size=(150, 40))
        menu_button.SetFont(font_button)
        menu_button.SetBackgroundColour(wx.Colour("#555555"))
        menu_button.SetForegroundColour(wx.Colour("white"))
        menu_button.Bind(wx.EVT_BUTTON, lambda evt: self._show_main_menu())
        button_sizer.Add(menu_button, 0, wx.ALL, 5)
        sizer.Add(button_sizer, 0, wx.ALIGN_CENTER | wx.TOP, 20)

        panel.SetSizer(sizer)
        panel.Layout()
        self.Layout()

    def _show_spaced_repetition(self, event):
        self.SetTitle("Изучение")
        self._clear_and_pack_frame(self.mode_panel)
        panel = self.mode_panel
        sizer = wx.BoxSizer(wx.VERTICAL)
        font_label = wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_term = wx.Font(24, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, DEFAULT_FONT_FAMILY)
        font_definition = wx.Font(30, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
        font_button = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)

        user_theory = self.file_manager.load_user_theory(self.user_theory_file_path)

        today = datetime.now().date()
        due_words = []

        for word in user_theory:
            if word["status"] == "nd":
                due_words.append(word)
            elif word["next_review"]:
                review_date = datetime.strptime(word["next_review"], "%Y-%m-%d").date()
                if review_date <= today:
                    due_words.append(word)

        if not due_words:
            no_words_label = wx.StaticText(panel, label="Новых слов нет")
            no_words_label.SetForegroundColour(wx.Colour("white"))
            no_words_label.SetFont(font_label)
            sizer.Add(no_words_label, 0, wx.ALIGN_CENTER | wx.TOP, 50)

            back_button = wx.Button(panel, label="Назад", size=(200, 40))
            back_button.SetFont(font_button)
            back_button.SetBackgroundColour(wx.Colour("#555555"))
            back_button.SetForegroundColour(wx.Colour("white"))
            back_button.Bind(wx.EVT_BUTTON, lambda evt: self._show_part_selection())
            sizer.Add(back_button, 0, wx.ALIGN_CENTER | wx.TOP, 20)

            panel.SetSizer(sizer)
            panel.Layout()
            self.Layout()
            return

        random.shuffle(due_words)
        self.current_review_words = due_words[:20]
        self.current_review_index = 0

        self.term_label = wx.StaticText(panel, label="")
        self.term_label.SetForegroundColour(wx.Colour("white"))
        self.term_label.SetFont(font_term)
        self.term_label.Wrap(700)
        sizer.Add(self.term_label, 0, wx.ALIGN_CENTER | wx.TOP | wx.LEFT | wx.RIGHT, 40)

        self.definition_label = wx.StaticText(panel, label="")
        self.definition_label.SetForegroundColour(wx.Colour("black"))
        self.definition_label.SetFont(font_definition)
        self.definition_label.SetBackgroundColour(wx.Colour(BG_COLOR))
        self.definition_label.Wrap(700)
        sizer.Add(self.definition_label, 0, wx.ALIGN_CENTER | wx.TOP | wx.LEFT | wx.RIGHT, 20)

        self.buttons_sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.buttons_sizer, 0, wx.ALIGN_CENTER | wx.TOP, 20)

        sr_back_button = wx.Button(panel, label="Назад в меню", size=(200, 40))
        sr_back_button.SetFont(font_button)
        sr_back_button.SetBackgroundColour(wx.Colour("#555555"))
        sr_back_button.SetForegroundColour(wx.Colour("white"))
        sr_back_button.Bind(wx.EVT_BUTTON, lambda evt: self._show_part_selection())
        sizer.Add(sr_back_button, 0, wx.ALIGN_CENTER | wx.TOP, 20)

        panel.SetSizer(sizer)
        panel.Layout()
        self.Layout()

        self._show_next_review_word()

    def _show_next_review_word(self):
        for child in self.buttons_sizer.GetChildren():
            child.GetWindow().Destroy()
        self.buttons_sizer.Clear(delete_windows=True)

        self.definition_label.SetLabel("")

        if self.current_review_index >= len(self.current_review_words):
            self._clear_and_pack_frame(self.mode_panel)
            panel = self.mode_panel
            sizer = wx.BoxSizer(wx.VERTICAL)
            font_label = wx.Font(16, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)
            font_button = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)

            session_end_label = wx.StaticText(panel, label="Сессия завершена")
            session_end_label.SetForegroundColour(wx.Colour("white"))
            session_end_label.SetFont(font_label)
            sizer.Add(session_end_label, 0, wx.ALIGN_CENTER | wx.TOP, 50)

            back_button = wx.Button(panel, label="Назад", size=(200, 40))
            back_button.SetFont(font_button)
            back_button.SetBackgroundColour(wx.Colour("#555555"))
            back_button.SetForegroundColour(wx.Colour("white"))
            back_button.Bind(wx.EVT_BUTTON, lambda evt: self._show_part_selection())
            sizer.Add(back_button, 0, wx.ALIGN_CENTER | wx.TOP, 20)

            panel.SetSizer(sizer)
            panel.Layout()
            self.Layout()
            return

        self.current_word = self.current_review_words[self.current_review_index]
        self.term_label.SetLabel(self.current_word["term"])
        self.term_label.Wrap(700)

        wx.CallLater(3000, self._show_definition_and_buttons)

    def _show_definition_and_buttons(self):
        print(f"Definition being set: '{self.current_word['definition']}'")
        self.definition_label.SetLabel(self.current_word["definition"])
        self.definition_label.Wrap(700)

        font_button = wx.Font(14, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, DEFAULT_FONT_FAMILY)

        know_button = wx.Button(self.mode_panel, label="Знаю", size=(120, 50))
        know_button.SetFont(font_button)
        know_button.SetBackgroundColour(wx.Colour(ACCENT_COLOR_KNOW))
        know_button.SetForegroundColour(wx.Colour("white"))
        know_button.Bind(wx.EVT_BUTTON, lambda evt: self._process_review_response("know"))
        self.buttons_sizer.Add(know_button, 0, wx.ALL, 10)

        not_sure_button = wx.Button(self.mode_panel, label="Не уверен", size=(120, 50))
        not_sure_button.SetFont(font_button)
        not_sure_button.SetBackgroundColour(wx.Colour(ACCENT_COLOR_NOT_SURE))
        not_sure_button.SetForegroundColour(wx.Colour("white"))
        not_sure_button.Bind(wx.EVT_BUTTON, lambda evt: self._process_review_response("not_sure"))
        self.buttons_sizer.Add(not_sure_button, 0, wx.ALL, 10)

        not_know_button = wx.Button(self.mode_panel, label="Не знаю", size=(120, 50))
        not_know_button.SetFont(font_button)
        not_know_button.SetBackgroundColour(wx.Colour(ACCENT_COLOR_NOT_KNOW))
        not_know_button.SetForegroundColour(wx.Colour("white"))
        not_know_button.Bind(wx.EVT_BUTTON, lambda evt: self._process_review_response("not_know"))
        self.buttons_sizer.Add(not_know_button, 0, wx.ALL, 10)

        self.buttons_sizer.Layout()
        self.mode_panel.Layout()

    def _process_review_response(self, response):
        word = self.current_review_words[self.current_review_index]
        today = datetime.now().date()

        if response == "know":
            new_date = today + timedelta(days=14)
        elif response == "not_sure":
            new_date = today + timedelta(days=7)
        else:
            new_date = today + timedelta(days=1)

        word["status"] = "reviewed"
        word["next_review"] = new_date.strftime("%Y-%m-%d")

        self._save_current_review_progress()

        self.current_review_index += 1
        self._show_next_review_word()

    def _save_current_review_progress(self):
        all_user_theory = self.file_manager.load_user_theory(self.user_theory_file_path)

        existing_terms_map = {item["term"]: item for item in all_user_theory}

        for reviewed_word in self.current_review_words:
            if reviewed_word["term"] in existing_terms_map:
                existing_terms_map[reviewed_word["term"]].update(reviewed_word)
            else:
                existing_terms_map[reviewed_word["term"]] = reviewed_word

        updated_all_data = list(existing_terms_map.values())
        self.file_manager.save_user_theory_data(self.user_theory_file_path, updated_all_data)

class QuizApp(wx.App):
    def OnInit(self):
        self.SetAppName("cttest")
        self.SetClassName("QuizApp")

        frame = QuizFrame(None, title=APP_TITLE)
        frame.Show(True)
        return True

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