from pathlib import Path
import re
import sys

from pyqt_compat.QtCore import Qt
from pyqt_compat.QtCore import QSettings, pyqtSlot
from . import plugin_base, tuflow_plugin_item, tuflow_plugin_shared
from pyqt_compat.QtWidgets import QFormLayout, QFileDialog, QSpinBox
from pyqt_compat.QtWidgets import QCheckBox, QTableView
from pyqt_compat.QtWidgets import QPushButton, QComboBox

from pyqt_compat import (QT_HEADER_VIEW_FIXED, QT_HEADER_VIEW_RESIZE_TO_CONTENT, QT_HEADER_VIEW_STRETCH,
                         QT_STYLE_SP_DIR_OPEN_ICON, QT_LINE_EDIT_TRAILING_POSITION)
from pyqt_compat.QtGui import QPixmap

import bit_functions
import settings

import pytuflow as pt
from pytuflow import tuflow_binaries

from .tfversion_manager.tfversion_dialog import TuflowVersionDialog

def executable_file_filter():
    if sys.platform == "win32":
        return "Executables (*.exe);;All files (*)"
    return "Executables (*)"


class TuflowSettingsTab(plugin_base.PluginSettingsTab):
    def __init__(self, plugin):
        super().__init__()

        self.exe = plugin.get_executable()
        self.configs = plugin.configs
        self.max_num_gpus = 24 # don't let them go over this amount for now

        if hasattr(tuflow_binaries, "refresh_from_settings") and callable(getattr(tuflow_binaries, "refresh_from_settings")):
            tuflow_binaries.refresh_from_settings()

        pg_layout = QFormLayout()

        self.btn_manage_versions = QPushButton(text="Manage")
        pg_layout.addRow(
            "Manage TUFLOW Versions",
            self.btn_manage_versions
            )
        self.btn_manage_versions.clicked.connect(self.on_manage_versions)

        tuflow_versions = [x[0] for x in tuflow_binaries.items()]
        self.cbx_default_version = QComboBox()
        self.cbx_default_version.addItems(tuflow_versions)
        pg_layout.addRow(
            "Default Version",
            self.cbx_default_version
        )
        version_index = self.cbx_default_version.findText(plugin.get_default_version())
        if version_index != -1:
            self.cbx_default_version.setCurrentIndex(version_index)

        # pixmapi = QT_STYLE_SP_DIR_OPEN_ICON
        # icon = self.style().standardIcon(pixmapi)
        # self.edt_tuflow_exe = QLineEdit()
        # open_action = self.edt_tuflow_exe.addAction(icon, QT_LINE_EDIT_TRAILING_POSITION)
        # open_action.triggered.connect(self.on_browse)

        # self.edt_tuflow_exe.setText(self.exe)

        # pg_layout.addRow(
        #     "TUFLOW Executable",
        #     self.edt_tuflow_exe
        # )

        self.tog_write_hw = QCheckBox()

        pg_layout.addRow(
            "Write hardware flags (2023-03-AB and later)",
            self.tog_write_hw,
        )

        # configurations
        self.spn_numConfigurations = QSpinBox()
        self.spn_numConfigurations.setMinimum(1)
        self.num_configs = max(len(self.configs), 1)
        self.spn_numConfigurations.setValue(self.num_configs)
        pg_layout.addRow("Number of Configurations", self.spn_numConfigurations)

        self.config_model = tuflow_plugin_shared.ConfigurationTableModel(self.configs)

        self.tblConfigurations = QTableView()
        self.tblConfigurations.setModel(self.config_model)
        self.tblConfigurations.horizontalHeader().setSectionResizeMode(QT_HEADER_VIEW_RESIZE_TO_CONTENT)
        self.tblConfigurations.verticalHeader().setSectionResizeMode(QT_HEADER_VIEW_FIXED)
        self.tblConfigurations.horizontalHeader().setStretchLastSection(True)

        self.delegate = tuflow_plugin_shared.SpinBoxDelegate(parent=self)
        self.tblConfigurations.setItemDelegateForColumn(self.max_num_gpus+1, self.delegate)

        #index = self.tblConfigurations.indexAt(QPoint(0, self.max_num_gpus + 1))
        #self.tblConfigurations.openPersistentEditor(index)

        self.spn_numConfigurations.valueChanged.connect(self.on_num_config_changed)

        pg_layout.addRow(self.tblConfigurations)

        self.setLayout(pg_layout)

        self.tog_write_hw.setChecked(plugin.write_hw)

        self.on_num_config_changed(self.num_configs)

    @pyqtSlot(int)
    def on_num_config_changed(self, new_num_config):
        self.config_model.set_number_configurations(new_num_config)
        for row in range(new_num_config):
            index = self.config_model.index(row, self.max_num_gpus)
            self.tblConfigurations.openPersistentEditor(index)
            index = self.config_model.index(row, self.max_num_gpus+1)
            self.tblConfigurations.openPersistentEditor(index)

    def num_gpus_changed(self, n_gpus):
        for col in range(0, n_gpus):
            self.tblConfigurations.setColumnHidden(col, False)
        for col in range(n_gpus, self.max_num_gpus):
            self.tblConfigurations.setColumnHidden(col, True)

    # @pyqtSlot()
    # def on_browse(self):
    #     exe_filename, qtfilter = QFileDialog.getOpenFileName(self,
    #                                                          "Select TUFLOW exe",
    #                                                          "",
    #                                                          executable_file_filter())
    #     if exe_filename:
    #         self.edt_tuflow_exe.setText(exe_filename)
    
    @pyqtSlot()
    def on_manage_versions(self):
        dialog = TuflowVersionDialog()
        dialog.exec()

    def store_settings(self, settings):
        settings.beginGroup('TUFLOW')
        # settings.setValue("tuflow_exe", self.edt_tuflow_exe.text())
        settings.setValue("tuflow_default_version", self.cbx_default_version.itemText(self.cbx_default_version.currentIndex()))
        hw_int = 1 if self.tog_write_hw.isChecked() else 0
        settings.setValue("write_hw", hw_int)

        # print(f'Num configs: {self.num_configs}')
        settings.beginWriteArray("Configurations2")
        for row in range(len(self.config_model.configs)):
            settings.setArrayIndex(row)
            settings.setValue("GPU_bits", self.config_model.configs[row][0])
            settings.setValue("CPUs", self.config_model.configs[row][1])
        settings.endArray()

        settings.endGroup()


class TuflowPlugin(plugin_base.PluginBase):
    def __init__(self):
        super(TuflowPlugin, self).__init__()
        self.write_hw = True
        # configurations are (GPUs, CPUs, CPUs counted)
        self.configs = [(1, 1, 0)]
        self.current_version = self.get_default_version()
        self.load_from_settings()
        self.tab_settings = None
        self.configs_running = set()

    def uses_versions(self):
        return True
    
    def get_version_names_paths(self):
        return tuflow_binaries.items()

    def get_default_version(self):
        settings = QSettings()
        if 'TUFLOW' in settings.childGroups():
            settings.beginGroup('TUFLOW')
            return settings.value("tuflow_default_version")
        return ""
    
    def get_current_version(self):
        return self.current_version

    def set_current_version(self, version_name):
        self.current_version = version_name

    def get_executable(self):
        curr_path = tuflow_binaries.get(self.get_current_version(), "")
        print(f'Exe path: {curr_path}')
        return curr_path
    
        #settings = QSettings()
        #if 'TUFLOW' in settings.childGroups():
        #    settings.beginGroup('TUFLOW')
        #    return settings.value("tuflow_exe")
        #return ""    

    def load_from_settings(self):
        qsettings = QSettings()
        total_gpus = settings.settings_int(qsettings, "Resources/GPU Count", 1)

        if 'TUFLOW' in qsettings.childGroups():
            qsettings.beginGroup('TUFLOW')

            self.write_hw = qsettings.value("write_hw", 0) == 1

            self.configs = []

            last_gpu = -1

            nrows = qsettings.beginReadArray("Configurations2")
            if nrows:
                for i in range(nrows):
                    qsettings.setArrayIndex(i)
                    gpu_bits = settings.settings_int(qsettings, "GPU_bits", 0)
                    ncpus = settings.settings_int(qsettings, "CPUs", 1)

                    self.configs.append((gpu_bits, ncpus))
                qsettings.endArray()
            else:
                # Try to read previous version of settings
                nrows = qsettings.beginReadArray("Configurations")
                for i in range(nrows):
                    qsettings.setArrayIndex(i)
                    ngpus = settings.settings_int(qsettings, "GPUs", 1)
                    ncpus = settings.settings_int(qsettings, "CPUs", 1)

                    gpu_bits = 0
                    for igpu in range(ngpus):
                        gpu = last_gpu + 1
                        if gpu >= total_gpus:
                            gpu = 0
                        print(f"row: {i}, turn on GPU {gpu}")
                        gpu_bits = bit_functions.set_bit(gpu_bits, gpu)
                        last_gpu = gpu

                    self.configs.append((gpu_bits, ncpus))
                qsettings.endArray()

            qsettings.endGroup()

    @classmethod
    def get_name(cls):
        return "TUFLOW"

    @classmethod
    def get_simulation_extension(cls):
        return "*.tcf"

    def get_settings_tabctrl(self):
        self.tab_settings = TuflowSettingsTab(self)
        return self.tab_settings

    def get_plugin_items(self, simulation_filename):
        # make sure we have a valid executable selected
        exe_path = Path(self.get_executable())

        if not exe_path.exists() or not exe_path.is_file():
            raise ValueError(
                "TUFLOW executable not specified or does not exist. Please set the executable in the settings dialog accessed from the menu.")
        return tuflow_plugin_item.dlg_new_plugin_items(self, simulation_filename)

    def start_running_config(self, config_index):
        self.configs_running.add(config_index)

    def finished_running_config(self, config_index):
        # print(f'Finished config: {config_index}')
        self.configs_running.remove(config_index)
        # print(self.configs_running)

    def available_configs(self):
        configs_avail = []
        for i in range(len(self.configs)):
            if i not in self.configs_running:
                configs_avail.append((i, self.configs[i]))

        return configs_avail

    def create_plugin_item(self, sim_filename, plugin_options):
        scenarios_events = plugin_options['scenarios_events']
        sim_items = re.findall(r"(~s\d~)", str(sim_filename))
        n_sims = len(sim_items)
        return tuflow_plugin_item.TuflowPluginItem(self,
                                                   sim_filename,
                                                   scenarios_events,
                                                   n_sims)
