'''
/***************************************************************************
Name	   :  contour_controller.py
Description:  Contour Controller for FEWSTools plugin
copyright  :  (C) 2020-2023 by FEWS
email      :  minxuansun@contractor.usgs.gov
Created    :  05/13/2020 - MSun
Modified   :  06/09/2020 - cholen - Fix reclassify setup for contour process
              06/15/2020 - cholen - Adjust check for getSaveFileName result
              07/09/2020 - cholen - Update color file
              07/13/2020 - MSun   - warns user to avoid setting interval to 0
              08/28/2020 - cholen - Notify on completion, cleanup to match
                                   coding patterns in other tool
              10/08/2020 - cholen - Fix os sep in browse
              10/20/2020 - cholen - Change wksp_setup name to match other files
              12/02/2020 - cholen - Handle index error
              01/05/2022 - cholen - Remove gdal and use new gdal utilities.
              02/19/2022 - cholen - Allow tiffs, update form.
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
'''
import os
import math

from PyQt5.QtWidgets import QMessageBox, QDialog, QFileDialog

from qgis.core import QgsMessageLog, Qgis

from fews_tools.forms.Ui_Contour import Ui_Contour

from fews_tools.models.workspace_setup_model import WorkspaceSetupModel
from fews_tools import fews_tools_config as config
from fews_tools.utilities import geoclim_gdal_utilities as g_util
from fews_tools.utilities import geoclim_utilities as util
from fews_tools.utilities import geoclim_qgs_utilities as qgs_util
from fews_tools.utilities import logging_utilities as log_util
from fews_tools.utilities.help_utilities import view_manual


class ContourController(QDialog):
    '''
    Class for Contour dialog
    This does intervals on either side of 0
    '''

    def __init__(self):
        QDialog.__init__(self)
        # set up UI
        self.ui = Ui_Contour()
        self.ui.setupUi(self)

        self.wrksp_setup = WorkspaceSetupModel()
        self.reclass_list = []
        self.interval = None
        self.missing = None

        # method connection
        self.ui.browseInputRasterButton.clicked[bool].connect(
            self.browse_input_raster)
        self.ui.browseOutputRasterButton.clicked[bool].connect(
            self.browse_output_raster)
        self.ui.processButton.clicked[bool].connect(self.start_contour)
        self.ui.closeButton.clicked[bool].connect(self.close)
        self.ui.helpButton.clicked.connect(self.view_manual)
        self.open_file_info = None

    def __get_reclassify_list__(self):
        '''
        Build the reclassify list
        '''
        reclass_list, geoxform = None, None
        src_file = self.ui.inputRasterEdit.text()
        input_array = g_util.extract_raster_array(src_file)
        _, _, _, geoxform, data_type_name = g_util.get_geotiff_info(src_file)

        # validate missing value
        if self.missing not in input_array:
            QMessageBox.warning(
                self,
                'Confirm Missing Data Value',
                'The missing value you assigned could not be found '
                'in the data. Please ensure you have set the correct value.',
                QMessageBox.Ok)
        else:
            # since the minumum is usually the nodata value we can't use it
            # we need to find the next smallest number
            arr_vals_list = sorted(set(list(input_array.flatten())))
            # just in case we've had an underflow, we find the missing value
            # and take the next highest value for our min
            # crashes if user puts in goofy value so handle the index error
            idx_missing = arr_vals_list.index(self.missing)
            try:
                min_data_val = arr_vals_list[idx_missing + 1]
            except IndexError:
                min_data_val = arr_vals_list[0]
            reclass_list = []
            # we want to go either side of zero
            for step in range(0, self.__round_down__(min_data_val),
                              (-1 * self.interval)):
                end_val = step - self.interval
                reclass_list.append([end_val, step, end_val])
            for step in range(0, self.__round_up__(input_array.max()),
                              self.interval):
                end_val = step + self.interval
                reclass_list.append([step, end_val, step])
        return reclass_list, geoxform, data_type_name

    def __lock_control__(self):
        '''
        Lock controls for processing
        '''
        self.ui.contourIntervalSpinBox.setEnabled(False)
        self.ui.missingValueSpinBox.setEnabled(False)
        self.ui.closeButton.setEnabled(False)

    def __round_up__(self, val):
        '''
        Round arg up to next level determined by interval
        param(int) - val The number to round up
        '''
        return int(math.ceil(val / float(self.interval))) * self.interval

    def __round_down__(self, val):
        '''
        Round arg down to next level determined by interval
        param(int) - val The number to round down
        '''
        return int(math.floor(val / float(self.interval))) * self.interval

    def __unlock_control__(self):
        '''
        Unlock controls when not processing
        '''
        self.ui.contourIntervalSpinBox.setEnabled(True)
        self.ui.missingValueSpinBox.setEnabled(True)
        self.ui.closeButton.setEnabled(True)

    def browse_input_raster(self):
        '''
        Browse input file
        '''
        file_name = \
            QFileDialog.getOpenFileName(self,
                                        'Select Input Raster File',
                                        self.wrksp_setup.get_output_path(),
                                        config.RST_FILE_TYPES)
        if file_name:
            self.ui.inputRasterEdit.setText(
                self.wrksp_setup.fix_os_sep_in_path(file_name[0]))
            default_output_name = (os.path.splitext(file_name[0])[0] +
                                   '_filt' +
                                   os.path.splitext(file_name[0])[1])
            self.ui.outputRasterEdit.setText(
                self.wrksp_setup.fix_os_sep_in_path(default_output_name))

    def browse_output_raster(self):
        '''
        Browse output file
        '''
        file_name = \
            QFileDialog.getSaveFileName(self,
                                        'Please Select an Output File',
                                        self.wrksp_setup.get_output_path(),
                                        config.RST_FILE_TYPES)
        if file_name[0]:
            self.ui.outputRasterEdit.setText(
                self.wrksp_setup.fix_os_sep_in_path(file_name[0]))

    def view_manual(self):
        '''
        Function to browse to output directory and set line edit control.
        '''
        clim_manual_webpage_section = "chapter-7-contour-tool"
        view_manual(clim_manual_webpage_section)

    def start_contour(self):
        '''
        Process for contours
        '''
        err = True
        # getting info
        input_file = self.ui.inputRasterEdit.text()
        output_file = self.ui.outputRasterEdit.text()

        # Checks to see if any output file names are in the map panel
        self.open_file_info = qgs_util.get_open_files_info()
        out_string = os.path.splitext(os.path.basename(output_file))[0]
        for entry in self.open_file_info:
            if entry[1] in out_string:
                qgs_util.notify_loaded_file(self, entry[1], entry[0])

        # warns user interval can't be zero
        self.interval = self.ui.contourIntervalSpinBox.value()
        if self.interval == 0:
            QMessageBox.warning(
                self,
                'Contour interval can\'t be zero',
                'Please change Contour interval to a '
                'non zero unsigned integer.',
                QMessageBox.Yes
            )
            return

        # disable UI
        self.__lock_control__()
        self.missing = self.ui.missingValueSpinBox.value()
        reclass_list, geoxform, data_type_name = self.__get_reclassify_list__()

        if reclass_list and geoxform:
            err = g_util.replace_value(geoxform, reclass_list,
                                       input_file, output_file, data_type_name)
        if not err:
            qgs_util.display_raster_layer(
                output_file, os.path.join(self.wrksp_setup.get_colors_path(),
                                          config.CONTOUR_200_COLOR_FILE))
        else:
            # report the error
            QgsMessageLog.logMessage('Contours did not succeed',
                                     level=Qgis.Critical)

        self.__unlock_control__()
        all_done = util.notify_process_completed('Contours')

        log_util.add_log("Contour",
                         log_util.log_string("Input File", [input_file]) +
                         log_util.log_string("Interval", [self.interval]) +
                         log_util.log_string("Missing Value", [self.missing]),
                         [output_file])

        if all_done:
            self.ui.closeButton.click()
