# -*- coding: utf-8 -*-
# Author: Milan Nikolic <gen2brain@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import os

from PyQt6.QtCore import Qt, QSettings
from PyQt6.QtWidgets import QDialog, QFileDialog, QRadioButton, QVBoxLayout

from m64py.core.defs import *
from m64py.loader import find_library
from m64py.core.vidext import MODES
from m64py.platform import DLL_FILTER, CONFIG_DIR
from m64py.frontend.plugin import Plugin
from m64py.frontend.input import Input
from m64py.ui.settings_ui import Ui_Settings


class Settings(QDialog, Ui_Settings):
    """Settings dialog"""

    def __init__(self, parent):
        QDialog.__init__(self, parent)
        self.parent = parent
        self.setupUi(self)
        self.core = None
        self.plugins = []
        self.emumode = []
        self.combomap = {}

        config_file = os.path.join(CONFIG_DIR, "m64py", "m64py.conf")
        self.qset = QSettings(config_file, QSettings.Format.IniFormat)

        self.add_items()
        self.connect_signals()

    def showEvent(self, event):
        self.set_config()

    def closeEvent(self, event):
        self.save_config()

    def add_items(self):
        self.combomap = {
            M64PLUGIN_RSP: (
                self.comboRSP, self.pushButtonRSP,
                Plugin(self.parent)),
            M64PLUGIN_GFX: (
                self.comboVideo, self.pushButtonVideo,
                Plugin(self.parent)),
            M64PLUGIN_AUDIO: (
                self.comboAudio, self.pushButtonAudio,
                Plugin(self.parent)),
            M64PLUGIN_INPUT: (
                self.comboInput, self.pushButtonInput,
                Input(self.parent))
        }

        self.emumode = [
            QRadioButton(self.tr("Pure Interpreter")),
            QRadioButton(self.tr("Cached Interpreter")),
            QRadioButton(self.tr("Dynamic Recompiler"))
        ]

        vbox = QVBoxLayout(self.groupEmuMode)
        for widget in self.emumode:
            vbox.addWidget(widget)

    def show_page(self, index=0):
        self.tabWidget.setCurrentIndex(index)
        self.show()

    def save_config(self):
        self.save_paths()
        self.save_plugins()
        if self.core and self.core.get_handle():
            self.save_video()
            self.save_core()
            self.core.config.save_file()
        self.qset.sync()

    def set_config(self):
        if self.core and self.core.get_handle():
            self.set_paths()
            self.set_plugins()
            self.set_video()
            self.set_core()

    def on_vidext_changed(self, state):
        self.parent.vidext = state
        self.comboResolution.setEnabled(not self.parent.vidext)
        self.checkFullscreen.setEnabled(not self.parent.vidext)
        self.parent.worker.quit()
        self.parent.worker.init()

    def connect_signals(self):
        self.browseLibrary.clicked.connect(lambda: self.browse_dialog(
            (self.pathLibrary, self.groupLibrary, False)))
        self.browsePlugins.clicked.connect(lambda: self.browse_dialog(
            (self.pathPlugins, self.groupPlugins, True)))
        self.browseData.clicked.connect(lambda: self.browse_dialog(
            (self.pathData, self.groupData, True)))
        self.browseROM.clicked.connect(lambda: self.browse_dialog(
            (self.pathROM, self.groupROM, True)))
        self.checkEnableVidExt.clicked.connect(self.on_vidext_changed)
        for plugin_type in self.combomap:
            self.connect_combo_signals(self.combomap[plugin_type])

    def connect_combo_signals(self, combomap):
        combo, button, settings = combomap
        if settings is not None:
            combo.activated.connect(
                lambda: self.set_section(combo, button, settings))
            button.clicked.connect(settings.show_dialog)

    def browse_dialog(self, args):
        widget, groupbox, directory = args
        dialog = QFileDialog()
        if directory:
            dialog.setFileMode(QFileDialog.FileMode.Directory)
            path = dialog.getExistingDirectory(
                self, groupbox.title(), widget.text(), QFileDialog.Option.ShowDirsOnly)
        else:
            dialog.setFileMode(QFileDialog.FileMode.ExistingFile)
            path, _ = dialog.getOpenFileName(
                self, groupbox.title(), widget.text(),
                "%s (*%s);;All files (*)" % (groupbox.title(), DLL_FILTER))

        if not path:
            return

        widget.setText(path)
        if widget == self.pathLibrary:
            self.parent.worker.quit()
            if not self.parent.worker.core.get_handle():
                self.parent.worker.init(path)
                if self.parent.worker.core.get_handle():
                    self.core = self.parent.worker.core
                    self.set_core()
                    self.set_video()
                    self.parent.window_size_triggered(self.get_size_safe())
                    self.parent.state_changed.emit((True, False, False, False))
        elif widget == self.pathPlugins:
            self.parent.worker.plugins_shutdown()
            self.parent.worker.plugins_unload()
            self.parent.worker.plugins_load(path)
            self.parent.worker.plugins_startup()
            self.set_plugins()

    def get_int_safe(self, key, default):
        try:
            return int(self.qset.value(key, default))
        except ValueError:
            return default

    def get_size_safe(self):
        try:
            size = self.qset.value("size", SIZE_2X)
        except TypeError:
            size = SIZE_2X
        if not type(size) == tuple:
            size = SIZE_2X
        if len(size) != 2:
            size = SIZE_2X
        if type(size[0]) != int or type(size[1]) != int:
            size = SIZE_2X
        if size[0] <= 0 or size[1] <= 0:
            size = SIZE_2X
        return size

    def get_parameter_help_safe(self, parameter):
        help = self.core.config.get_parameter_help(parameter)
        if help is not None:
            return help.decode()
        return ""

    def get_section(self, combo):
        plugin = combo.currentText()
        index = combo.findText(plugin)
        desc = combo.itemData(index)
        name = os.path.splitext(plugin)[0][12:]
        section = "-".join([n.capitalize() for n in name.split("-")[0:2]])
        return section, desc

    def set_section(self, combo, button, settings):
        if settings:
            section, desc = self.get_section(combo)
            if section == "Input-Sdl":
                section = "Input-SDL-Control1"
            settings.set_section(section, desc)
            self.core.config.open_section(section)
            items = self.core.config.parameters[
                self.core.config.section].items()
            if items:
                button.setEnabled(True)
            else:
                button.setEnabled(False)
        else:
            button.setEnabled(False)

    def set_paths(self):
        path_library = self.qset.value(
            "Paths/Library", find_library(CORE_NAME))
        path_data = self.qset.value(
            "Paths/Data", self.core.config.get_path("SharedData"))
        path_roms = self.qset.value("Paths/ROM")

        try:
            path_plugins = self.qset.value("Paths/Plugins", os.path.realpath(
                os.path.dirname(self.parent.worker.plugin_files[0])))
        except IndexError:
            path_plugins = ""

        try:
            self.pathROM.setText(path_roms)
        except TypeError:
            pass

        self.pathLibrary.setText(path_library)
        self.pathPlugins.setText(path_plugins)
        self.pathData.setText(path_data)

    def set_video(self):
        self.core.config.open_section("Video-General")
        self.set_resolution()

        self.checkFullscreen.setChecked(
            bool(self.core.config.get_parameter("Fullscreen")))
        self.checkFullscreen.setEnabled(not self.parent.vidext)

        self.checkVsync.setChecked(
            bool(self.core.config.get_parameter("VerticalSync")))
        self.checkVsync.setToolTip(
            self.get_parameter_help_safe("VerticalSync"))

        self.checkEnableVidExt.setChecked(
            bool(self.get_int_safe("enable_vidext", 1)))

        self.checkDisableScreenSaver.setChecked(bool(self.get_int_safe("disable_screensaver", 1)))
        self.checkPauseOnFocusLoss.setChecked(bool(self.get_int_safe("pause_on_focus_loss", 0)))

    def set_core(self):
        self.core.config.open_section("Core")
        mode = self.core.config.get_parameter("R4300Emulator")
        self.emumode[mode].setChecked(True)
        self.checkOSD.setChecked(
            self.core.config.get_parameter("OnScreenDisplay"))
        self.checkOSD.setToolTip(
            self.get_parameter_help_safe("OnScreenDisplay"))
        self.checkNoCompiledJump.setChecked(
            self.core.config.get_parameter("NoCompiledJump"))
        self.checkNoCompiledJump.setToolTip(
            self.get_parameter_help_safe("NoCompiledJump"))
        self.checkDisableExtraMem.setChecked(
            self.core.config.get_parameter("DisableExtraMem"))
        self.checkDisableExtraMem.setToolTip(
            self.get_parameter_help_safe("DisableExtraMem"))

        count_per_op = self.core.config.get_parameter("CountPerOp")
        if count_per_op is not None:
            self.comboCountPerOp.setCurrentIndex(count_per_op)
        else:
            self.comboCountPerOp.setEnabled(False)
        self.comboCountPerOp.setToolTip(
            self.get_parameter_help_safe("CountPerOp"))

    def set_plugins(self):
        plugin_map = self.core.plugin_map
        for plugin_type in self.combomap:
            combo, button, settings = self.combomap[plugin_type]
            combo.clear()
            for plugin in plugin_map[plugin_type].values():
                (plugin_handle, plugin_path, plugin_name,
                    plugin_desc, plugin_version) = plugin
                name = os.path.basename(plugin_path)
                combo.addItem(name)
                index = combo.findText(str(name))
                combo.setItemData(index, plugin_desc)
                combo.setItemData(index, plugin_desc, Qt.ItemDataRole.ToolTipRole)
            current = self.qset.value("Plugins/%s" % (
                PLUGIN_NAME[plugin_type]), PLUGIN_DEFAULT[plugin_type])
            index = combo.findText(current)
            if index == -1:
                current = self.qset.value("Plugins/%s" % (
                    PLUGIN_NAME[plugin_type]), PLUGIN_FALLBACK[plugin_type])
                index = combo.findText(current)
            if index == -1:
                index = 0
            combo.setCurrentIndex(index)
            self.set_section(combo, button, settings)

    def set_resolution(self):
        width = self.core.config.get_parameter("ScreenWidth")
        height = self.core.config.get_parameter("ScreenHeight")
        if (width, height) not in MODES:
            MODES.append((width, height))

        self.comboResolution.clear()
        for mode in MODES:
            w, h = mode
            self.comboResolution.addItem(
                "%sx%s" % (w, h), (w, h))

        index = self.comboResolution.findText(
            "%sx%s" % (width, height), Qt.MatchFlag.MatchExactly)
        if index == -1: index = 0
        self.comboResolution.setCurrentIndex(index)
        self.comboResolution.setEnabled(not self.parent.vidext)

    def save_paths(self):
        if self.pathLibrary.text():
            self.qset.setValue("Paths/Library", self.pathLibrary.text())
        if self.pathPlugins.text():
            self.qset.setValue("Paths/Plugins", self.pathPlugins.text())
        if self.pathData.text():
            self.qset.setValue("Paths/Data", self.pathData.text())
        if self.pathROM.text():
            self.qset.setValue("Paths/ROM", self.pathROM.text())

    def save_video(self):
        self.core.config.open_section("Video-General")
        if self.parent.vidext:
            width, height = self.get_size_safe()
        else:
            width, height = self.comboResolution.currentText().split("x")
        self.core.config.set_parameter("ScreenWidth", int(width))
        self.core.config.set_parameter("ScreenHeight", int(height))
        self.core.config.set_parameter("Fullscreen", self.checkFullscreen.isChecked())
        self.core.config.set_parameter("VerticalSync", self.checkVsync.isChecked())

        self.qset.setValue("enable_vidext", int(self.checkEnableVidExt.isChecked()))
        self.qset.setValue("disable_screensaver", int(self.checkDisableScreenSaver.isChecked()))
        self.qset.setValue("pause_on_focus_loss", int(self.checkPauseOnFocusLoss.isChecked()))

    def save_core(self):
        self.core.config.open_section("Core")
        emumode = [n for n,m in enumerate(self.emumode) if m.isChecked()][0]
        self.core.config.set_parameter("R4300Emulator", emumode)
        self.core.config.set_parameter("OnScreenDisplay", self.checkOSD.isChecked())
        self.core.config.set_parameter("NoCompiledJump", self.checkNoCompiledJump.isChecked())
        self.core.config.set_parameter("DisableExtraMem", self.checkDisableExtraMem.isChecked())
        self.core.config.set_parameter("CountPerOp", self.comboCountPerOp.currentIndex())
        self.core.config.set_parameter("SharedDataPath", self.pathData.text().encode())

    def save_plugins(self):
        for plugin_type in self.combomap:
            combo, button, settings = self.combomap[plugin_type]
            self.qset.setValue("Plugins/%s" % PLUGIN_NAME[plugin_type], combo.currentText())
