# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial

import os
from pathlib import Path

from PySide6.QtCore import Qt, QSize, Slot
from PySide6.QtGui import (QCloseEvent, QGuiApplication, QIcon, QPixmap,
                           QKeySequence)
from PySide6.QtWidgets import (QMainWindow, QTabWidget, QToolBar)

from classwindow import ClassWindow
from gui_utils import (ask_question, choose_existing_file,
                       rotate_icon, show_warning,
                       qt_resource_icon)
from helpwindow import HelpWindow
from options import Options
from optionsdialog import OptionsDialog
from recentmenu import RecentMenu
from workbench import Workbench

import rc_shibokenwizard  # noqa F:401


class MainWindow(QMainWindow):

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

        self._workbench = Workbench(self)
        self._class_window = ClassWindow(self)

        self.setWindowIcon(QIcon(':/qt-project.org/logos/pysidelogo.png'))
        menu_bar = self.menuBar()
        file_menu = menu_bar.addMenu("File")
        build_menu = menu_bar.addMenu("Build")
        tool_bar = QToolBar()
        tool_bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        if not QIcon.themeName():  # Limit size of default icons
            tool_bar.setIconSize(QSize(20, 20))
        self.addToolBar(Qt.TopToolBarArea, tool_bar)

        self.setWindowTitle('Untitled[*]')

        self._class_window.populate_menus(file_menu, tool_bar)
        self._class_window.window_title.connect(self.setWindowTitle)
        self._class_window.window_modified.connect(self.setWindowModified)
        self._class_window.status_message.connect(self.statusBar().showMessage)
        self._class_window.loaded.connect(self._switch_to_class_window)

        self._generate_action = file_menu.addAction("Generate...")
        tool_tip = "Generate typesystem file and CMakeList.txt"
        self._generate_action.setToolTip(tool_tip)
        self._generate_action.setShortcut(Qt.CTRL | Qt.Key_G)
        self._generate_action.triggered.connect(self._generate)
        self._generate_action.setEnabled(False)
        self._class_window.can_generate_enabled.connect(self._generate_action.setEnabled)

        self._load_typesystem_action = file_menu.addAction("Load typesystem...")
        tool_tip = "Load existing typesystem and cmake files and switch directly to the Build page"
        self._load_typesystem_action.setToolTip(tool_tip)
        self._load_typesystem_action.triggered.connect(self._load_typesystem)

        self._recent_menu = RecentMenu(file_menu)
        self._recent_menu.set_files(Options.recent_files())
        self._recent_menu.load.connect(self.load_project_from_file)
        self._class_window.unloaded.connect(self._recent_menu.add_file)

        icon = QIcon.fromTheme('gtk-quit')
        quit_action = file_menu.addAction(icon, "Quit")
        quit_action.setShortcut(Qt.CTRL | Qt.Key_Q)
        quit_action.triggered.connect(self.close)

        tool_bar.addSeparator()
        self._workbench.populate_menus(build_menu, tool_bar)
        tool_bar.addSeparator()

        options_menu = menu_bar.addMenu("Options")
        icon = QIcon.fromTheme('settings-configure')
        options_action = options_menu.addAction(icon, "Options...")
        options_action.triggered.connect(self._options)

        help_menu = menu_bar.addMenu("Help")
        icon = QIcon.fromTheme('help-about')
        about_qt_action = help_menu.addAction(icon, "About Qt")
        about_qt_action.triggered.connect(qApp.aboutQt)  # noqa F:821
        fallback_icon = qt_resource_icon('standardbutton-help')
        icon = QIcon.fromTheme('help-contents', fallback_icon)
        help_action = help_menu.addAction(icon, "Help")
        help_action.setShortcut(QKeySequence.HelpContents)
        help_action.triggered.connect(self._help)

        self._mode_tab_widget = QTabWidget()
        self._mode_tab_widget.setTabPosition(QTabWidget.West)
        self.setCentralWidget(self._mode_tab_widget)

        fallback_icon = QIcon(':/images/EditorSettings.png')
        icon = QIcon.fromTheme('configure', fallback_icon)
        # QTabBar rotates the icon
        self._mode_tab_widget.addTab(self._class_window,
                                     rotate_icon(icon, 90),
                                     'Configuration')
        fallback_icon = QIcon(':/images/build.png')
        fallback_icon.addPixmap(QPixmap(':/images/build@2x.png'))
        icon = QIcon.fromTheme('viewlog', fallback_icon)
        self._mode_tab_widget.addTab(self._workbench,
                                     rotate_icon(icon, 90),
                                     'Build Log')
        self._id_class_tab = self._mode_tab_widget.indexOf(self._class_window)
        self._id_workbench_tab = self._mode_tab_widget.indexOf(self._workbench)

        self._class_window.status_message.emit("Setup your project before 'Scan for Classes'")

    def closeEvent(self, event: QCloseEvent) -> None:
        if self._class_window.ensure_clean('Quit anyways?'):
            Options.set_recent_files(self._recent_menu.files())
            event.accept()
        else:
            event.ignore()

    def load_project_from_file(self, file: str) -> bool:
        result = False
        try:
            self._class_window.load_project_from_file(file)
            result = True
        except Exception as e:
            reason = str(e)
            message = f'Failed to load {file}: {reason}'
            show_warning(self, "Failed to Load", message)
        return result

    @Slot()
    def _generate(self) -> None:
        output_directory = self._class_window.output_directory()
        if not output_directory:
            return
        name = self._class_window.project_name()
        typesystem_name = name.lower() + '_typesystem.xml'
        typesystem_file = os.path.join(output_directory, typesystem_name)
        cmake_file = os.path.join(output_directory, 'CMakeLists.txt')
        typesystem_exists = os.path.exists(typesystem_file)
        cmake_file_exists = os.path.exists(cmake_file)
        if typesystem_exists or cmake_file_exists:
            m = f"Overwrite existing files {typesystem_file}, {cmake_file}?"
            if not ask_question(self, "Files Already Exist", m):
                # TODO: Maybe switch to the Typesystem or CMakeLists tab
                # on the class view (Configuration tab)
                if typesystem_exists and cmake_file_exists:
                    self._switch_to_workbench(cmake_file, typesystem_file)
                return
        QGuiApplication.setOverrideCursor(Qt.BusyCursor)
        try:
            output_path = Path(output_directory)
            output_path.mkdir(parents=True, exist_ok=True)
            self._class_window.generate(cmake_file, typesystem_file)
            self._switch_to_workbench(cmake_file, typesystem_file)
        except Exception as e:
            reason = str(e)
            base_dir = os.path.basename(output_directory)
            message = f'Failed to generate "{base_dir}": {reason}'
            show_warning(self, "Failed to Generate", message)
        finally:
            QGuiApplication.restoreOverrideCursor()

    def _switch_to_workbench(self, cmake_file: str,
                             typesystem_file: str) -> None:
        self._workbench.set_files(cmake_file, typesystem_file)
        self._mode_tab_widget.setCurrentIndex(1)

    def _switch_to_class_window(self) -> None:
        self._mode_tab_widget.setCurrentIndex(0)

    @Slot()
    def _load_typesystem(self) -> None:
        """Load existing typesystem and cmake file and go to build"""
        typesystem_file = choose_existing_file(self, 'Type System',
                                               ['application/xml'])
        if not typesystem_file:
            return
        cmake_file = os.path.join(os.path.dirname(typesystem_file),
                                  'CMakeLists.txt')
        if os.path.exists(cmake_file):
            self._switch_to_workbench(cmake_file, typesystem_file)

    @Slot()
    def _options(self) -> None:
        dialog = OptionsDialog(self)
        dialog.exec()

    @Slot()
    def _help(self) -> None:
        help_window = HelpWindow(self)
        help_window.setAttribute(Qt.WA_DeleteOnClose)
        help_window.show()
