import sys
import os
import json
import ctypes
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QFileDialog, QLabel, QListWidget,
    QListWidgetItem, QHBoxLayout, QVBoxLayout, QPushButton, QMenu, QAction,
    QMessageBox, QScrollArea, QSizePolicy, QSplitter, QTreeView, QFileSystemModel
)
from PyQt5.QtGui import QPixmap, QIcon, QPalette, QMouseEvent, QWheelEvent
from PyQt5.QtCore import Qt, QSize, QSettings, QDir, QPoint, QStandardPaths
from PIL import Image

# Путь к папке кеша
CACHE_DIR = os.path.join(os.path.expanduser("~"), ".image_viewer_cache")
FAVORITES_FILE = os.path.join(CACHE_DIR, "favorites.json")

SUPPORTED_FORMATS = ('.png', '.jpg', '.jpeg', '.bmp', '.gif', '.tiff')


def ensure_cache():
    if not os.path.exists(CACHE_DIR):
        os.makedirs(CACHE_DIR)


class ImageLabel(QLabel):
    def __init__(self, parent=None, parent_scroll=None):
        super().__init__(parent)
        self.setBackgroundRole(QPalette.Window)
        self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
        self.setScaledContents(False)  # Отключаем автоматическое масштабирование
        self.scale_factor = 1.0
        self.original_pixmap = None

        # Для панорамирования
        self.setMouseTracking(True)
        self._drag_pos = None
        self.parent_scroll = parent_scroll  # Ссылка на QScrollArea

    def set_pixmap(self, pixmap):
        self.original_pixmap = pixmap
        self.scale_factor = 1.0
        self.update_pixmap()

    def update_pixmap(self):
        """
        Обновление отображаемого изображения с учетом текущего коэффициента масштабирования.
        """
        if self.original_pixmap:
            new_size = self.scale_factor * self.original_pixmap.size()
            scaled_pixmap = self.original_pixmap.scaled(
                new_size, Qt.KeepAspectRatio, Qt.SmoothTransformation
            )
            self.setPixmap(scaled_pixmap)
            self.resize(scaled_pixmap.size())

    def wheelEvent(self, event: QWheelEvent):
        """
        Обработка события прокрутки колесика мыши для масштабирования изображения.
        Масштабирование происходит относительно положения курсора.
        """
        if event.angleDelta().y() > 0:
            self.zoom_in(event.pos())
        else:
            self.zoom_out(event.pos())

    def zoom_in(self, cursor_pos=None):
        """
        Увеличение изображения.
        """
        self.scale_image(1.1, cursor_pos)

    def zoom_out(self, cursor_pos=None):
        """
        Уменьшение изображения.
        """
        self.scale_image(0.9, cursor_pos)

    def scale_image(self, factor, cursor_pos=None):
        """
        Масштабирование изображения на заданный коэффициент с сохранением позиции курсора.
        """
        if self.original_pixmap is None:
            return

        # Ограничение коэффициента масштабирования
        new_scale = self.scale_factor * factor
        if new_scale < 0.1 or new_scale > 10:
            return
        self.scale_factor = new_scale

        # Сохранение текущей позиции скролла
        if self.parent_scroll and cursor_pos:
            # Координаты курсора относительно изображения
            image_pos = cursor_pos
            # Процентное соотношение позиции курсора
            h_ratio = image_pos.x() / self.width()
            v_ratio = image_pos.y() / self.height()

        # Обновление изображения
        self.update_pixmap()

        # Обновление скроллов после масштабирования
        if self.parent_scroll and cursor_pos:
            h_scroll = self.parent_scroll.horizontalScrollBar()
            v_scroll = self.parent_scroll.verticalScrollBar()

            # Новые размеры изображения
            new_width = self.width()
            new_height = self.height()

            # Расчет новых значений прокрутки
            new_h = int(new_width * h_ratio - self.parent_scroll.viewport().width() / 2)
            new_v = int(new_height * v_ratio - self.parent_scroll.viewport().height() / 2)

            # Установка новых значений прокрутки
            h_scroll.setValue(new_h)
            v_scroll.setValue(new_v)

    def mousePressEvent(self, event: QMouseEvent):
        """
        Обработка нажатия кнопок мыши для панорамирования.
        """
        if event.button() == Qt.LeftButton:
            self._drag_pos = event.globalPos()
            self.setCursor(Qt.ClosedHandCursor)
            event.accept()
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event: QMouseEvent):
        """
        Обработка перемещения мыши для панорамирования.
        """
        if self._drag_pos is not None and event.buttons() & Qt.LeftButton:
            delta = event.globalPos() - self._drag_pos
            self._drag_pos = event.globalPos()
            if self.parent_scroll:
                h_scroll = self.parent_scroll.horizontalScrollBar()
                v_scroll = self.parent_scroll.verticalScrollBar()
                h_scroll.setValue(h_scroll.value() - delta.x())
                v_scroll.setValue(v_scroll.value() - delta.y())
            event.accept()
        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event: QMouseEvent):
        """
        Обработка отпускания кнопок мыши после панорамирования.
        """
        if event.button() == Qt.LeftButton:
            self._drag_pos = None
            self.setCursor(Qt.ArrowCursor)
            event.accept()
        super().mouseReleaseEvent(event)


class ImageViewer(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Просмотровщик Изображений")
        self.resize(1200, 800)

        ensure_cache()
        self.favorites = self.load_favorites()
        self.clipboard_path = None
        self.copied = False

        # Основной виджет и компоновка
        self.central_widget = QWidget()
        self.setCentralWidget(self.central_widget)
        self.main_layout = QVBoxLayout()
        self.central_widget.setLayout(self.main_layout)

        # Панель инструментов
        self.toolbar = QHBoxLayout()
        self.main_layout.addLayout(self.toolbar)

        self.open_folder_btn = QPushButton("Открыть папку")
        self.open_folder_btn.clicked.connect(self.open_folder)
        self.toolbar.addWidget(self.open_folder_btn)

        self.open_file_btn = QPushButton("Открыть файл")
        self.open_file_btn.clicked.connect(self.open_file)
        self.toolbar.addWidget(self.open_file_btn)

        self.edit_btn = QPushButton("Изменить (Paint)")
        self.edit_btn.clicked.connect(self.edit_image)
        self.toolbar.addWidget(self.edit_btn)

        self.delete_btn = QPushButton("Удалить")
        self.delete_btn.clicked.connect(self.delete_image)
        self.toolbar.addWidget(self.delete_btn)

        self.fullscreen_btn = QPushButton("На весь экран")
        self.fullscreen_btn.clicked.connect(self.toggle_fullscreen)
        self.toolbar.addWidget(self.fullscreen_btn)

        self.png_info_btn = QPushButton("PNG info")
        self.png_info_btn.clicked.connect(self.png_info)
        self.toolbar.addWidget(self.png_info_btn)

        self.favorites_btn = QPushButton("Избранное")
        self.favorites_btn.clicked.connect(self.show_favorites)
        self.toolbar.addWidget(self.favorites_btn)

        self.save_size_btn = QPushButton("Сохранить размер")
        self.save_size_btn.clicked.connect(self.save_window_size)
        self.toolbar.addWidget(self.save_size_btn)

        # Добавление кнопки для доступа к Рабочему столу
        self.desktop_btn = QPushButton("Рабочий стол")
        self.desktop_btn.clicked.connect(self.open_desktop)
        self.toolbar.addWidget(self.desktop_btn)

        # Основная область с разделителем и списками и изображением
        self.splitter_main = QSplitter(Qt.Horizontal)
        self.main_layout.addWidget(self.splitter_main)

        # Левая панель: Дерево папок
        self.file_model = QFileSystemModel()
        # Установите корневой путь в пустую строку, чтобы отображать все корневые диски
        self.file_model.setRootPath("")
        # Добавьте QDir.Drives к фильтру, чтобы отображать все жесткие диски
        self.file_model.setFilter(QDir.AllDirs | QDir.NoDotAndDotDot | QDir.Drives)

        self.navigation_tree = QTreeView()
        self.navigation_tree.setModel(self.file_model)
        # Установите корневой индекс в пустом пути для отображения всех дисков
        self.navigation_tree.setRootIndex(self.file_model.index(""))
        self.navigation_tree.setColumnWidth(0, 250)
        self.navigation_tree.hideColumn(1)  # Размер
        self.navigation_tree.hideColumn(2)  # Тип
        self.navigation_tree.hideColumn(3)  # Дата
        self.navigation_tree.clicked.connect(self.navigate_to_directory)
        self.splitter_main.addWidget(self.navigation_tree)

        # Средняя панель: Превью изображений
        self.splitter_images = QSplitter(Qt.Vertical)
        self.splitter_main.addWidget(self.splitter_images)

        self.image_list = QListWidget()
        self.image_list.setViewMode(QListWidget.IconMode)
        self.image_list.setResizeMode(QListWidget.Adjust)
        self.image_list.setMovement(QListWidget.Static)
        self.image_list.setWordWrap(True)
        self.image_list.setWrapping(True)
        self.image_list.setSpacing(10)
        self.image_list.setIconSize(QSize(150, 150))  # Увеличенные иконки для лучшего отображения
        self.image_list.setSelectionMode(QListWidget.SingleSelection)
        self.image_list.itemClicked.connect(self.display_image)
        self.splitter_images.addWidget(self.image_list)

        # Правая панель: Область просмотра изображения
        self.image_area = QScrollArea()
        self.image_label = ImageLabel(parent_scroll=self.image_area)  # Передача ссылки на QScrollArea
        self.image_label.setAlignment(Qt.AlignCenter)
        self.image_area.setWidget(self.image_label)
        self.image_area.setWidgetResizable(False)  # Изменено с True на False
        self.splitter_main.addWidget(self.image_area)

        self.splitter_main.setStretchFactor(1, 1)  # Средняя панель растягивается
        self.splitter_images.setStretchFactor(0, 1)  # Превью изображения растягиваются

        # Переменные состояния
        self.current_folder = None
        self.current_image = None
        self.fullscreen = False

        # Загрузка окна
        self.load_window_state()

        # Поддержка drag and drop
        self.setAcceptDrops(True)

    def load_favorites(self):
        """
        Загрузка списка избранных изображений из файла.
        """
        if os.path.exists(FAVORITES_FILE):
            try:
                with open(FAVORITES_FILE, 'r', encoding='utf-8') as f:
                    return set(json.load(f))
            except Exception as e:
                print(f"Ошибка загрузки избранного: {e}")
                return set()
        return set()

    def save_favorites(self):
        """
        Сохранение списка избранных изображений в файл.
        """
        try:
            with open(FAVORITES_FILE, 'w', encoding='utf-8') as f:
                json.dump(list(self.favorites), f, ensure_ascii=False, indent=4)
        except Exception as e:
            print(f"Ошибка сохранения избранного: {e}")

    def open_folder(self):
        """
        Открытие диалога выбора папки и загрузка изображений из выбранной папки.
        """
        folder = QFileDialog.getExistingDirectory(self, "Выбрать папку", "")
        if folder:
            self.current_folder = folder
            self.load_images(folder)
            self.select_first_image()

    def open_file(self):
        """
        Открытие диалога выбора файла и загрузка соответствующей папки.
        """
        file, _ = QFileDialog.getOpenFileName(
            self, "Выбрать файл", "", "Images (*.png *.jpg *.jpeg *.bmp *.gif *.tiff)"
        )
        if file:
            self.current_folder = os.path.dirname(file)
            self.load_images(self.current_folder)
            self.select_image(file)

    def load_images(self, folder):
        """
        Загрузка изображений из указанной папки и отображение их в виде превью.
        """
        try:
            self.image_list.clear()
            for filename in os.listdir(folder):
                if filename.lower().endswith(SUPPORTED_FORMATS):
                    filepath = os.path.join(folder, filename)
                    thumbnail = self.get_thumbnail(filepath)
                    if thumbnail:
                        item = QListWidgetItem()
                        item.setIcon(QIcon(thumbnail))
                        # Убираем текст, оставляем только иконку
                        item.setData(Qt.UserRole, filepath)
                        self.image_list.addItem(item)
        except Exception as e:
            QMessageBox.warning(self, "Ошибка", f"Не удалось загрузить изображения: {e}")

    def get_thumbnail(self, filepath):
        """
        Создание или загрузка превью изображения из кеша.
        """
        try:
            ensure_cache()
            thumb_path = os.path.join(CACHE_DIR, self.sanitize_filename(filepath) + ".png")
            if not os.path.exists(thumb_path):
                img = Image.open(filepath)
                img.thumbnail((150, 150))  # Размер превью соответствует установленному IconSize
                img.save(thumb_path, "PNG")
            return thumb_path
        except Exception as e:
            print(f"Ошибка создания превью для {filepath}: {e}")
            return None

    def sanitize_filename(self, filepath):
        """
        Преобразование пути к файлу в допустимое имя файла для превью.
        """
        return filepath.replace(os.sep, "_").replace(":", "_").replace("\\", "_").replace("/", "_").replace(" ", "_")

    def display_image(self, item):
        """
        Отображение выбранного изображения в основном окне с возможностью масштабирования и панорамирования.
        """
        try:
            filepath = item.data(Qt.UserRole)
            pixmap = QPixmap(filepath)
            if pixmap.isNull():
                QMessageBox.warning(self, "Ошибка", f"Не удалось загрузить изображение: {filepath}")
                return
            self.image_label.set_pixmap(pixmap)
            self.current_image = filepath
        except Exception as e:
            QMessageBox.warning(self, "Ошибка", f"Не удалось отобразить изображение: {e}")

    def select_image(self, filepath):
        """
        Выбор изображения в списке по его пути и отображение.
        """
        try:
            for index in range(self.image_list.count()):
                item = self.image_list.item(index)
                if item.data(Qt.UserRole) == filepath:
                    self.image_list.setCurrentItem(item)
                    self.display_image(item)
                    break
        except Exception as e:
            QMessageBox.warning(self, "Ошибка", f"Не удалось выбрать изображение: {e}")

    def select_first_image(self):
        """
        Выбор и отображение первого изображения в списке.
        """
        try:
            if self.image_list.count() > 0:
                self.image_list.setCurrentRow(0)
                item = self.image_list.item(0)
                self.display_image(item)
        except Exception as e:
            QMessageBox.warning(self, "Ошибка", f"Не удалось выбрать первое изображение: {e}")

    def edit_image(self):
        """
        Открытие выбранного изображения в Paint для редактирования.
        """
        if self.current_image:
            try:
                if sys.platform.startswith('win'):
                    os.startfile(self.current_image, 'edit')
                else:
                    QMessageBox.warning(self, "Ошибка", "Функция редактирования доступна только на Windows.")
            except Exception as e:
                QMessageBox.warning(self, "Ошибка", f"Не удалось открыть изображение для редактирования: {e}")

    def delete_image(self):
        """
        Удаление выбранного изображения с подтверждением.
        """
        if self.current_image:
            reply = QMessageBox.question(
                self, 'Удаление',
                f"Вы действительно хотите удалить {os.path.basename(self.current_image)}?",
                QMessageBox.Yes | QMessageBox.No, QMessageBox.No
            )
            if reply == QMessageBox.Yes:
                try:
                    os.remove(self.current_image)
                    self.load_images(self.current_folder)
                    self.image_label.clear()
                    self.current_image = None
                except Exception as e:
                    QMessageBox.warning(self, "Ошибка", f"Не удалось удалить файл: {e}")

    def toggle_fullscreen(self):
        """
        Переключение полноэкранного режима.
        """
        if self.fullscreen:
            self.showNormal()
            self.fullscreen = False
        else:
            self.showFullScreen()
            self.fullscreen = True

    def png_info(self):
        """
        Отображение информации о PNG-изображении.
        """
        if self.current_image and self.current_image.lower().endswith('.png'):
            try:
                img = Image.open(self.current_image)
                info = img.info
                msg = "\n".join([f"{k}: {v}" for k, v in info.items()])
                QMessageBox.information(self, "PNG Info", msg)
            except Exception as e:
                QMessageBox.warning(self, "Ошибка", f"Не удалось получить информацию о PNG: {e}")
        else:
            QMessageBox.information(self, "Информация", "Текущее изображение не является PNG.")

    def show_favorites(self):
        """
        Отображение списка избранных изображений.
        """
        try:
            favorite_images = list(self.favorites)
            if not favorite_images:
                QMessageBox.information(self, "Избранное", "Список избранных изображений пуст.")
                return
            self.image_list.clear()
            for filepath in favorite_images:
                if os.path.exists(filepath):
                    thumbnail = self.get_thumbnail(filepath)
                    if thumbnail:
                        item = QListWidgetItem()
                        item.setIcon(QIcon(thumbnail))
                        item.setData(Qt.UserRole, filepath)
                        self.image_list.addItem(item)
        except Exception as e:
            QMessageBox.warning(self, "Ошибка", f"Не удалось отобразить избранные изображения: {e}")

    def save_window_size(self):
        """
        Сохранение текущего размера и положения окна.
        """
        try:
            settings = QSettings("MyCompany", "ImageViewer")
            settings.setValue("size", self.size())
            settings.setValue("pos", self.pos())
            QMessageBox.information(self, "Сохранено", "Размер и положение окна сохранены.")
        except Exception as e:
            QMessageBox.warning(self, "Ошибка", f"Не удалось сохранить размеры окна: {e}")

    def load_window_state(self):
        """
        Загрузка сохраненного размера и положения окна, а также состояний разделителей.
        """
        try:
            settings = QSettings("MyCompany", "ImageViewer")
            size = settings.value("size")
            pos = settings.value("pos")
            if size:
                self.resize(size)
            if pos:
                self.move(pos)

            # Загрузка состояний разделителей
            splitter_main_sizes = settings.value("splitter_main_sizes")
            splitter_images_sizes = settings.value("splitter_images_sizes")
            if splitter_main_sizes:
                # QSettings возвращает значения как QVariantList, преобразуем их в список int
                self.splitter_main.setSizes([int(s) for s in splitter_main_sizes])
            if splitter_images_sizes:
                self.splitter_images.setSizes([int(s) for s in splitter_images_sizes])
        except Exception as e:
            print(f"Ошибка загрузки состояния окна: {e}")

    def contextMenuEvent(self, event):
        """
        Создание и отображение контекстного меню при правом клике на изображение.
        """
        if not self.current_image:
            return  # Не показывать контекстное меню, если нет выбранного изображения

        context_menu = QMenu(self)

        if self.current_image in self.favorites:
            remove_fav_action = QAction("Удалить из избранного", self)
            remove_fav_action.triggered.connect(self.remove_from_favorites)
            context_menu.addAction(remove_fav_action)
        else:
            add_fav_action = QAction("Добавить в избранное", self)
            add_fav_action.triggered.connect(self.add_to_favorites)
            context_menu.addAction(add_fav_action)

        copy_action = QAction("Копировать", self)
        copy_action.triggered.connect(self.copy_file)
        context_menu.addAction(copy_action)

        paste_action = QAction("Вставить", self)
        paste_action.triggered.connect(self.paste_file)
        context_menu.addAction(paste_action)

        cut_action = QAction("Вырезать", self)
        cut_action.triggered.connect(self.cut_file)
        context_menu.addAction(cut_action)

        set_wallpaper_action = QAction("Сделать изображением рабочего стола", self)
        set_wallpaper_action.triggered.connect(self.set_as_wallpaper)
        context_menu.addAction(set_wallpaper_action)

        properties_action = QAction("Свойства", self)
        properties_action.triggered.connect(self.show_properties)
        context_menu.addAction(properties_action)

        context_menu.exec_(event.globalPos())

    def add_to_favorites(self):
        """
        Добавление текущего изображения в избранное.
        """
        if self.current_image:
            try:
                self.favorites.add(self.current_image)
                self.save_favorites()
                QMessageBox.information(
                    self, "Избранное",
                    f"Добавлено в избранное: {os.path.basename(self.current_image)}"
                )
            except Exception as e:
                QMessageBox.warning(self, "Ошибка", f"Не удалось добавить в избранное: {e}")

    def remove_from_favorites(self):
        """
        Удаление текущего изображения из избранного.
        """
        if self.current_image and self.current_image in self.favorites:
            try:
                self.favorites.remove(self.current_image)
                self.save_favorites()
                QMessageBox.information(
                    self, "Избранное",
                    f"Удалено из избранного: {os.path.basename(self.current_image)}"
                )
            except Exception as e:
                QMessageBox.warning(self, "Ошибка", f"Не удалось удалить из избранного: {e}")

    def copy_file(self):
        """
        Копирование текущего изображения в буфер обмена.
        """
        if self.current_image:
            try:
                self.copied = True
                self.clipboard_path = self.current_image
                QMessageBox.information(
                    self, "Копирование",
                    f"Скопировано: {os.path.basename(self.current_image)}"
                )
            except Exception as e:
                QMessageBox.warning(self, "Ошибка", f"Не удалось скопировать файл: {e}")

    def paste_file(self):
        """
        Вставка изображения из буфера обмена в текущую папку.
        """
        if self.clipboard_path and self.current_folder:
            try:
                base, name = os.path.split(self.clipboard_path)
                dest = os.path.join(self.current_folder, name)
                if os.path.exists(dest):
                    QMessageBox.warning(
                        self, "Копирование",
                        "Файл уже существует в папке назначения."
                    )
                    return
                if self.copied:
                    # Копирование
                    with open(self.clipboard_path, 'rb') as fr:
                        with open(dest, 'wb') as fw:
                            fw.write(fr.read())
                    self.load_images(self.current_folder)
                    QMessageBox.information(
                        self, "Копирование",
                        f"Вставлено: {dest}"
                    )
                else:
                    # Вырезание
                    if os.path.exists(self.clipboard_path):
                        with open(self.clipboard_path, 'rb') as fr:
                            with open(dest, 'wb') as fw:
                                fw.write(fr.read())
                        os.remove(self.clipboard_path)
                        self.load_images(self.current_folder)
                        QMessageBox.information(
                            self, "Вырезание",
                            f"Вставлено: {dest}"
                        )
            except Exception as e:
                QMessageBox.warning(
                    self, "Копирование/Вырезание",
                    f"Не удалось вставить файл: {e}"
                )
        else:
            QMessageBox.information(
                self, "Вставка", "Буфер обмена пуст или не выбрана папка назначения."
            )

    def cut_file(self):
        """
        Вырезание текущего изображения.
        """
        if self.current_image:
            try:
                self.copied = False
                self.clipboard_path = self.current_image
                QMessageBox.information(
                    self, "Вырезание",
                    f"Вырезано: {os.path.basename(self.current_image)}"
                )
            except Exception as e:
                QMessageBox.warning(self, "Ошибка", f"Не удалось вырезать файл: {e}")

    def set_as_wallpaper(self):
        """
        Установка текущего изображения как фон рабочего стола (только для Windows).
        """
        if self.current_image:
            try:
                if sys.platform.startswith('win'):
                    SPI_SETDESKWALLPAPER = 20
                    abs_path = os.path.abspath(self.current_image)
                    ctypes.windll.user32.SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, abs_path, 3)
                    QMessageBox.information(
                        self, "Рабочий стол",
                        "Фон рабочего стола установлен."
                    )
                else:
                    QMessageBox.warning(self, "Ошибка", "Функция доступна только на Windows.")
            except Exception as e:
                QMessageBox.warning(
                    self, "Ошибка",
                    f"Не удалось установить фон: {e}"
                )

    def show_properties(self):
        """
        Отображение свойств текущего изображения.
        """
        if self.current_image:
            try:
                stat = os.stat(self.current_image)
                info = (
                    f"Путь: {self.current_image}\n"
                    f"Размер: {stat.st_size} байт\n"
                    f"Последнее изменение: {self.format_time(stat.st_mtime)}"
                )
                QMessageBox.information(self, "Свойства", info)
            except Exception as e:
                QMessageBox.warning(
                    self, "Ошибка",
                    f"Не удалось получить свойства: {e}"
                )

    def format_time(self, epoch_time):
        """
        Форматирование времени из epoch в читаемый формат.
        """
        import datetime
        return datetime.datetime.fromtimestamp(epoch_time).strftime('%Y-%m-%d %H:%M:%S')

    def navigate_to_directory(self, index):
        """
        Обработка выбора директории из дерева папок.
        """
        try:
            folder = self.file_model.filePath(index)
            if os.path.isdir(folder):
                self.current_folder = folder
                self.load_images(folder)
                self.select_first_image()
        except Exception as e:
            QMessageBox.warning(self, "Ошибка", f"Не удалось перейти в директорию: {e}")

    def dragEnterEvent(self, event):
        """
        Обработка события перетаскивания файлов в окно приложения.
        """
        if event.mimeData().hasUrls():
            event.acceptProposedAction()

    def dropEvent(self, event):
        """
        Обработка события отпуска файлов после перетаскивания в окно приложения.
        """
        urls = event.mimeData().urls()
        if urls:
            filepath = urls[0].toLocalFile()
            if os.path.isdir(filepath):
                self.current_folder = filepath
                self.load_images(filepath)
                self.select_first_image()
            elif os.path.isfile(filepath) and filepath.lower().endswith(SUPPORTED_FORMATS):
                self.current_folder = os.path.dirname(filepath)
                self.load_images(self.current_folder)
                self.select_image(filepath)
            else:
                QMessageBox.information(self, "Неверный формат", "Перетащенный файл не поддерживается.")

    def closeEvent(self, event):
        """
        Обработка события закрытия окна приложения.
        """
        try:
            # Сохранение состояний разделителей
            settings = QSettings("MyCompany", "ImageViewer")
            settings.setValue("splitter_main_sizes", self.splitter_main.sizes())
            settings.setValue("splitter_images_sizes", self.splitter_images.sizes())
        except Exception as e:
            print(f"Ошибка сохранения состояний разделителей: {e}")

        self.save_favorites()
        event.accept()

    def open_desktop(self):
        """
        Быстрый доступ к рабочему столу текущего пользователя.
        """
        desktop_path = QStandardPaths.writableLocation(QStandardPaths.DesktopLocation)
        if os.path.exists(desktop_path):
            self.current_folder = desktop_path
            self.load_images(desktop_path)
            self.select_first_image()
            # Также можно установить выбранную папку в дереве, но это требует дополнительных шагов
        else:
            QMessageBox.warning(self, "Ошибка", "Не удалось найти папку Рабочего стола.")


if __name__ == "__main__":
    try:
        app = QApplication(sys.argv)
        viewer = ImageViewer()
        viewer.show()
        sys.exit(app.exec_())
    except Exception as e:
        print(f"Не удалось запустить приложение: {e}")
центрировать изображение в окне просмотра , изменить тип увеличения изображения - изображение привязано к правому верхнему углу нужно изменить на привязку к центру окна просмотра