######################################################################
#             / ____|  ____|  __ \| |  | |  ____|  ____|             #
#            | |    | |__  | |__) | |__| | |__  | |__                #
#            | |    |  __| |  ___/|  __  |  __| |  __|               #
#            | |____| |____| |    | |  | | |____| |____              #
#             \_____|______|_|    |_|  |_|______|______|             #
######################################################################
#
# CEPHEE
# Copyright (C) 2024 Toulouse INP
#
# 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 :
# <http://www.gnu.org/licenses/>.
#
######################################################################

from qgis.PyQt.QtWidgets import QDockWidget
from qgis.PyQt.QtCore import pyqtSignal
from qgis.core import (QgsVectorLayer, QgsRasterLayer,QgsProject, QgsMapLayerProxyModel, QgsMessageLog, Qgis, QgsTask,
                       QgsApplication, QgsCoordinateReferenceSystem)
from .ui.Ui_HydraulicWidget import Ui_HydraulicWidget
from ..core.postCEPHEE import *
from ..core.CrossSection import *
from ..core.Hydraulics import run_hydraulics
from .ReachTabWindow import ReachTab

class HydraulicWidget(QDockWidget, Ui_HydraulicWidget):

    closingWidget = pyqtSignal(name="closingWidget")

    def __init__(self, iface, BV, param, parent = None):
        super(HydraulicWidget, self).__init__(parent)
        self.setupUi(self)
        self.iface = iface
        self.BV = BV
        self.param = param
        self.task = None
        self.pushButton_compute.clicked.connect(self.compute)
        self.checkBox_useFrictionMap.stateChanged.connect(self.updatePanel)
        self.mMapLayerComboBox.setFilters(QgsMapLayerProxyModel.VectorLayer)
        self.checkBox_hydraulicCurve.stateChanged.connect(self.updatePanel)
        self.checkBox_createBanks.stateChanged.connect(self.updatePanel)
        self.checkBox_lateralSubSection.stateChanged.connect(self.updatePanel)
        self.comboBox_createBanks.currentIndexChanged.connect(self.updatePanel)
        self.comboBox_dischargeMethod.currentIndexChanged.connect(self.updatePanel)
        self.pushButton_reachList.clicked.connect(self.open_reach_list_panel)
        self.updatePanel()

    def closeEvent(self, event):
        self.closingWidget.emit()
        event.accept()

    def updatePanel(self):
        self.lineEdit_outletDischarge.setEnabled(True)
        self.comboBox_dischargeMethod.setEnabled(True)
        if self.comboBox_dischargeMethod.currentText() == "Uniform per reach":
            self.pushButton_reachList.setEnabled(True)
        else:
            self.pushButton_reachList.setDisabled(True)
        if self.checkBox_useFrictionMap.isChecked():
            self.mMapLayerComboBox.setEnabled(True)
            self.lineEdit_frictionValue.setDisabled(True)
        else:
            self.mMapLayerComboBox.setDisabled(True)
            self.comboBox_frictionLaw.setEnabled(True)
            self.lineEdit_frictionValue.setEnabled(True)
        if self.checkBox_lateralSubSection.isChecked():
            self.lineEdit_lateralStep.setEnabled(True)
        else:
            self.lineEdit_lateralStep.setDisabled(True)
        if self.checkBox_hydraulicCurve.isChecked():
            self.lineEdit_dz.setEnabled(True)
            self.lineEdit_hmax.setEnabled(True)
        else:
            self.lineEdit_dz.setDisabled(True)
            self.lineEdit_hmax.setDisabled(True)
        self.lineEdit_hWaterOutlet.setDisabled(True)
        if self.checkBox_createBanks.isChecked():
            self.comboBox_createBanks.setEnabled(True)
            if self.comboBox_createBanks.currentText() == "1D backwater":
                self.label_12.setText("Water elevation at outlet")
                self.lineEdit_hWaterOutlet.setEnabled(True)
            elif self.comboBox_createBanks.currentText() == "Constant depth":
                self.label_12.setText("Constant depth")
                self.lineEdit_hWaterOutlet.setEnabled(True)
                self.lineEdit_outletDischarge.setDisabled(True)
                self.comboBox_dischargeMethod.setDisabled(True)
            elif self.comboBox_createBanks.currentText() == "From data":
                self.lineEdit_outletDischarge.setDisabled(True)
                self.comboBox_dischargeMethod.setDisabled(True)
            self.comboBox_mappingMethod.setEnabled(True)
        else:
            self.comboBox_createBanks.setDisabled(True)
            self.comboBox_mappingMethod.setDisabled(True)

    def ui_to_param(self):
        self.param['H']['dischargeMethod'] = self.comboBox_dischargeMethod.currentText()
        Q = self.lineEdit_outletDischarge.text().split(";")
        if len(Q) > 1:
            for i in range(len(Q)):
                Q[i] = float(Q[i])
        else:
            Q = float(Q[0])
        self.param['H']['outletDischarge'] = Q
        self.param['H']['write_discharge'] = self.checkBox_writeDischarge.isChecked()
        self.param['H']['frictionLaw'] = self.comboBox_frictionLaw.currentText()
        self.param['H']['frictionValue'] = float(self.lineEdit_frictionValue.text())

        self.param['H']['dxlat'] = float(self.lineEdit_lateralStep.text())

        if self.checkBox_useFrictionMap.isChecked():
            self.param['H']['frictionMap'] = True
            map_name = self.mMapLayerComboBox.currentLayer()
            self.param['H']['friction_filename'] = map_name.dataProvider().dataSourceUri()

        else:
            self.param['H']['frictionMap'] = False

        if self.checkBox_lateralSubSection.isChecked():
            self.param['H']['dxlat'] = float(self.lineEdit_lateralStep.text())
        else:
            self.param['H']['dxlat'] = -1.0

        if self.checkBox_levee.isChecked():
            self.param['H']['levee'] = True
        else:
            self.param['H']['levee'] = False
           
        if self.checkBox_hydraulicCurve.isChecked():
            self.param['H']['hydraulicCurve'] = True
            self.param['H']['dz'] = float(self.lineEdit_dz.text())
            self.param['H']['hmax'] = float(self.lineEdit_hmax.text())
        else:
            self.param['H']['hydraulicCurve'] = False
            
        if self.comboBox_createBanks.currentText() == "Normal depth":
            self.param['H']['createBanksMethods'] = "Normal"
        elif self.comboBox_createBanks.currentText() == "1D backwater":
            self.param['H']['createBanksMethods'] = "1D"
        elif self.comboBox_createBanks.currentText() == "Constant depth":
            self.param['H']['createBanksMethods'] = "Himposed"
        elif self.comboBox_createBanks.currentText() == "From data":
            self.param['H']['createBanksMethods'] = "WSE"

        self.param['H']['hWaterOutlet'] = float(self.lineEdit_hWaterOutlet.text())
        self.param['H']['himposed'] = float(self.lineEdit_hWaterOutlet.text())

        if self.checkBox_createBanks.isChecked():
            self.param['H']['createBanks'] = True
        else:
            self.param['H']['createBanks'] = False
        self.param['H']['mapping_method'] = self.comboBox_mappingMethod.currentText()
        self.param['H']['output_resolution'] = int(self.lineEdit_outputResolution.text())

    def compute(self):
        self.ui_to_param()
        self.task = BackgroundProcess('process_hydraulicComputation', self.BV, self.param)
        QgsApplication.taskManager().addTask(self.task)

    def open_reach_list_panel(self):
        reach_tab = ReachTab(self.BV, self.lineEdit_outletDischarge)
        reach_tab.exec()

class BackgroundProcess(QgsTask):

    def __init__(self, description, BV, param):
        super().__init__(description, QgsTask.CanCancel)
        self.BV = BV
        self.param = param

    def run(self):
        QgsMessageLog.logMessage(message="Starting Hydraulic computation", tag="CEPHEE", level=Qgis.MessageLevel.Info)
        # Run hydraulic computation
        config_cal = run_hydraulics(self.BV,self.param)
        # load banks lines
        bank_filepath = path.join(self.param.work_path, 'banks_lines_'+config_cal+'.shp')
        bank_layer = QgsVectorLayer(bank_filepath, 'banks_lines_'+config_cal)
        bank_layer.setCrs(QgsCoordinateReferenceSystem(self.BV.crs.to_string()))
        QgsProject.instance().addMapLayer(bank_layer)
        self.setProgress(25)

        # 1D to 2D results interpolation
        if self.param['H']['mapping_method'] == 'interpolation':

            QgsMessageLog.logMessage(message="Creating polygon from cross-sections", tag="CEPHEE", level=Qgis.MessageLevel.Info)
            data_poly = create_poly_CEPHEE(self.BV, self.param)
            self.setProgress(50)
            QgsMessageLog.logMessage(message="Interpolating results", tag="CEPHEE", level=Qgis.MessageLevel.Info)
            raster_result_path = interpolate_result_CEPHEE(data_poly, self.param.work_path, config_cal, self.param,
                                                             self.param['C']['DEM_CRS_ID'], DEM_file =self.BV.global_DEM_path,qgstask=self)
        elif self.param['H']['mapping_method'] == 'by_polygon':
            QgsMessageLog.logMessage(message="Creating polygon from cross-sections", tag="CEPHEE", level=Qgis.MessageLevel.Info)
            data_poly = create_poly_CEPHEE(self.BV, self.param)
            self.setProgress(50)
            QgsMessageLog.logMessage(message="Interpolating results", tag="CEPHEE", level=Qgis.MessageLevel.Info)
            raster_result_path = rasterize_poly_CEPHEE(data_poly, self.param.work_path, config_cal,
                                                       self.param, self.BV.global_DEM_path, qgstask=self)
        elif self.param['H']['mapping_method'] == 'selafin':
            result_slf(self.BV, self.param, self.param.work_path, config_cal, self.param['C']['DEM_CRS_ID'])
            self.setProgress(50)
        QgsMessageLog.logMessage(message="1D->2D result interpolation finished", tag="CEPHEE", level=Qgis.MessageLevel.Info)
        # load raster in QGIS
        if self.param['H']['mapping_method'] != 'selafin':
            depth_layer = QgsRasterLayer(raster_result_path['depth'],path.splitext(path.basename(raster_result_path['depth']))[0])
            vel_layer = QgsRasterLayer(raster_result_path['velocity'],path.splitext(path.basename(raster_result_path['velocity']))[0])
            WSE_layer = QgsRasterLayer(raster_result_path['WSE'],path.splitext(path.basename(raster_result_path['WSE']))[0])
            QgsProject.instance().addMapLayer(depth_layer)
            QgsProject.instance().addMapLayer(vel_layer)
            QgsProject.instance().addMapLayer(WSE_layer)

        if os.path.exists(os.path.join(self.param.work_path, 'inline_structure.shp')):
            inline_layer = QgsVectorLayer(path.join(self.param.work_path, 'inline_structure.shp'), 'inline_structure')
            QgsMessageLog.logMessage(message="inline structure detected", tag="CEPHEE",
                                     level=Qgis.MessageLevel.Info)
            QgsProject.instance().addMapLayer(inline_layer)

        if self.param['H']['dischargeMethod'] == 'Per reach':
            if os.path.exists(os.path.join(self.param.work_path, 'discharge.shp')):
                discharge_layer = QgsVectorLayer(path.join(self.param.work_path, 'discharge.shp'), 'discharge')
                QgsMessageLog.logMessage(message="discharge repartition computed", tag="CEPHEE",
                                         level=Qgis.MessageLevel.Info)
                QgsProject.instance().addMapLayer(discharge_layer)
        self.setProgress(100)
        return True

    def finished(self, result):
        if result:
            QgsMessageLog.logMessage(message="Process OK", tag="CEPHEE", level=Qgis.MessageLevel.Info)
        else:
            QgsMessageLog.logMessage(message="Process Error", tag="CEPHEE", level=Qgis.MessageLevel.Critical)
