'''
/***************************************************************************
Name	   :  view_available_data_controller.py
Description:  View Available Data class for FEWSTools plugin,
              updated from QGIS2
copyright  :  (C) 2019-2023 by FEWS
email      :  minxuansun@contractor.usgs.gov
Created    :  12/11/2019 cholen
Modified   :  12/31/2019 cholen - Moved find_missing_files method to
                                    geoclim_utilities
              07/01/2020 cholen - Update initial dataset combo load, static
                           analysis updates
              08/25/2020 cholen - Include metadata with archive
              10/02/2020 cholen - Added handling for cancels
              12/03/2020 cholen - Update OSError and user messages
              01/13/2021 cholen - Handle regex problems
              02/01/2021 cholen - Comment NetCDF until external tool works
              04/15/2022 cholen - Codebase consistency and add tiff support
              07/13/2022 cholen - Handle FEWS daily date format
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 re
import shutil
# import subprocess

from PyQt5.QtWidgets import QMessageBox, QDialog, QFileDialog
from qgis.core import QgsMessageLog, Qgis

from fews_tools.models.datasets_model import DatasetsModel
from fews_tools.controllers.new_name_controller import NewNameController
from fews_tools.models.workspace_setup_model import WorkspaceSetupModel
from fews_tools.forms.Ui_ViewAvailableData import Ui_ViewAvailableData

from fews_tools import fews_tools_config as config
from fews_tools.utilities import archive_utilities as arc_util
from fews_tools.utilities import geoclim_utilities as util
from fews_tools.utilities import geoclim_qgs_utilities as qgs_util


class ViewAvailableDataController(QDialog):
    '''
    Class for View Available Data functions
    '''

    def __init__(self):
        QDialog.__init__(self)
        self.ui = Ui_ViewAvailableData()
        self.ui.setupUi(self)

        self.wrksp_setup = WorkspaceSetupModel()
        self.ds_info = DatasetsModel()
        self.input_file_list = []
        self.missing_file_list = []
        self.ds_dic = None
        self.interval_list = None
        util.fill_dataset_combo(self.ui.datasetComboBox)
        self.ui.listMissingButton.setCheckable(True)
        # make connections to methods
        self.ui.datasetComboBox.currentIndexChanged.connect(
            self.dataset_selection_changed)
        self.ui.listMissingButton.clicked[bool].connect(
            self.switch_display)
        self.ui.deleteButton.clicked[bool].connect(self.delete_files)
        self.ui.exportButton.clicked[bool].connect(self.export_files)
        self.ui.fileListWidget.itemSelectionChanged.connect(
            qgs_util.clear_status_msg)
        self.ui.expArchRadioButton.clicked.connect(
            qgs_util.clear_status_msg)
        self.ui.expRastersRadioButton.clicked.connect(
            qgs_util.clear_status_msg)
        self.ui.expNetCDFRadioButton.setVisible(False)
#        self.ui.expNetCDFRadioButton.clicked.connect(
#            qgs_util.clear_status_msg)
        self.dataset_selection_changed()

    def __helper_split_date_string__(self, src_filename):
        '''
        Helper to split out the date string from a filename
        params(string) - src_filename
        returns(tuple) - year_i, mon_i, per_i as integers
        '''
        year_i = None
        mon_i = None
        per_i = None
        reg_exp = config.DATE_FORMATS_DIC[
            self.ds_dic['DATADATEFORMAT']]['REG_EXPR']

        # have to split down to what we think is the date string
        # and then check if it matches the expected length of the date string
        # because prefix and/or file path could contain digits that would
        # return an incorrect value if we just use the regex alone
        date_string = os.path.splitext(
            os.path.basename(src_filename))[0].split(
                self.ds_dic['DATAPREFIX'])[1]
        try:
            dt_st_ck = re.findall(reg_exp, date_string)[0]
        except IndexError:
            raise IOError    # will occur if findall comes back empty
        if dt_st_ck != date_string:
            raise IOError
        year_l, mon_l, per_l = util.split_date_string(
            date_string, self.ds_dic['DATADATEFORMAT'])

        if year_l:
            year_i = int(year_l)
        if mon_l:
            mon_i = int(mon_l)
        if per_l:
            per_i = int(per_l)
        return year_i, mon_i, per_i

    def __lock_controls__(self):
        '''
        Lock controls
        '''
        self.ui.closeButton.setEnabled(False)
        self.ui.listMissingButton.setEnabled(False)
        self.ui.fileListWidget.setEnabled(False)
        self.ui.datasetComboBox.setEnabled(False)
        self.ui.deleteButton.setEnabled(False)
        self.ui.expRastersRadioButton.setEnabled(False)
        # self.ui.expNetCDFRadioButton.setEnabled(False)
        self.ui.expArchRadioButton.setEnabled(False)
        self.ui.exportButton.setEnabled(False)

    def __unlock_controls__(self):
        '''
        Unlock controls
        '''
        self.ui.closeButton.setEnabled(True)
        self.ui.listMissingButton.setEnabled(True)
        self.ui.fileListWidget.setEnabled(True)
        self.ui.datasetComboBox.setEnabled(True)
        self.ui.deleteButton.setEnabled(True)
        self.ui.expRastersRadioButton.setEnabled(True)
        # self.ui.expNetCDFRadioButton.setEnabled(True)
        self.ui.expArchRadioButton.setEnabled(True)
        self.ui.exportButton.setEnabled(True)

    def dataset_selection_changed(self):
        '''
        Update the view data to a the selected dataset.
        '''
        qgs_util.clear_status_msg()
        self.ui.fileListWidget.clear()
        err = self.ds_info.query_named_dataset(
            self.ui.datasetComboBox.currentText())
        self.ds_dic = self.ds_info.get_ds_dictionary()
        self.interval_list = util.get_interval_list(self.ds_dic['PERIODICITY'])
        self.input_file_list = util.get_input_file_list(
            self.ds_dic)
        if self.input_file_list == []:
            QMessageBox.information(self,
                                    u'Data missing!!',
                                    u'No data available for dataset!!',
                                    QMessageBox.Ok)
            err = True
        if err is False:
            if self.ui.listMissingButton.isChecked():
                self.ui.listMissingButton.click()
            self.load_available_files()

    def delete_files(self):
        '''
        Deletes selected files from disk
        '''
        self.__lock_controls__()
        delete_list = []

        if not self.ui.fileListWidget.selectedItems():
            QMessageBox.information(self,
                                    u'No files selected!',
                                    u'Select files for deletion.',
                                    QMessageBox.Ok)
        else:
            for item in self.ui.fileListWidget.selectedItems():
                file_base = item.text()
                file_path = os.path.join(self.ds_dic['DATAFOLDER'], file_base)
                delete_list.append(file_path)
        try:
            for entry in delete_list:
                util.remove_raster_file(entry)
            QMessageBox.information(self,
                                    u'Delete files complete!',
                                    u'Selected file(s) deleted.',
                                    QMessageBox.Ok)
        except OSError:
            QMessageBox.critical(self,
                                 u'Delete files failed!',
                                 u'Selected file(s) in use.',
                                 QMessageBox.Ok)
        finally:
            self.dataset_selection_changed()  # will update list widget
            self.__unlock_controls__()

    def export_rasters(self):
        '''
        Export selected files to a selected folder.
        returns(boolean) - err - Error Success = False, Error = True
        '''
        err = True
        export_message = ''
        export_dir_name =\
            QFileDialog.getExistingDirectory(None,
                                             u'Select Export Directory',
                                             self.wrksp_setup.get_workspace())
        if not export_dir_name:
            export_message = 'Export cancelled'
            err = False
        else:  # if user selected a export directory
            QgsMessageLog.logMessage(u'Exporting files to ' + export_dir_name,
                                     level=Qgis.Info)
            selected_items = self.ui.fileListWidget.selectedItems()
            copy_file_list = []
            for item in selected_items:
                file_base = item.text()
                file_path = os.path.join(self.ds_dic['DATAFOLDER'], file_base)
                copy_file_list.append(file_path)
                if self.ds_dic['DATASUFFIX'] == config.BIL_SUFFIX:
                    hdr_path = file_path.replace(
                        self.ds_dic['DATASUFFIX'], config.HDR_SUFFIX)
                    copy_file_list.append(hdr_path)
            util.copy_files(copy_file_list, export_dir_name)
            # check if all the files are exported
            destination_file_list = \
                [os.path.join(export_dir_name, os.path.basename(file_path))
                 for file_path in copy_file_list]
            if len(destination_file_list) == len(
                    util.is_files_exist(destination_file_list)):
                export_message = 'Export completed'
                err = False
        return err, export_message

    def export_files(self):
        '''
        Export selected files.
        '''
        if not self.ui.fileListWidget.selectedItems():
            QMessageBox.warning(self,
                                u'Please select files',
                                u'Please select files',
                                QMessageBox.Ok)
            return

        qgs_util.clear_status_msg()
        # lock controls
        self.__lock_controls__()
        err = True
        exp_msg = ''
        if self.ui.expArchRadioButton.isChecked():
            err, exp_msg = self.export_geoclim_archive()
        elif self.ui.expRastersRadioButton.isChecked():
            err, exp_msg = self.export_rasters()
#        elif self.ui.expNetCDFRadioButton.isChecked():
#            err = self.export_netcdf()
        else:
            QMessageBox.information(self,
                                    u'No export output selected!!',
                                    u'Select export type!',
                                    QMessageBox.Ok)
            self.__unlock_controls__()
            return
        if exp_msg:
            msg = exp_msg
            QMessageBox.information(self, msg, msg, QMessageBox.Ok)
            QgsMessageLog.logMessage(msg, level=Qgis.Info)
        elif not err:
            msg = u'Export complete'
            QMessageBox.information(self, msg, msg, QMessageBox.Ok)
            QgsMessageLog.logMessage(msg, level=Qgis.Info)
        else:
            msg = u'Export failed'
            QMessageBox.critical(self, msg, msg, QMessageBox.Ok)
            QgsMessageLog.logMessage(msg, level=Qgis.Critical)
        self.__unlock_controls__()

    def export_geoclim_archive(self):
        '''
        Export selected files to a GeoCLIM archive.
        returns(boolean) - err - Error Success = False, Error = True
        '''
        err = True
        export_message = 'GeoCLIM Archive export failed'
        export_cancel_message = 'GeoCLIM Archive export cancelled'
        # get a filename for the zipped file
        new_dataset_name_dlg = NewNameController('Archive')
        new_dataset_name_dlg.exec_()
        tar_gz_file_base = new_dataset_name_dlg.get_new_name()

        if tar_gz_file_base:
            wksp = self.wrksp_setup.get_workspace()
            initial_archive_dir = os.path.join(wksp,
                                               config.PROGRAM_SETTINGS,
                                               config.DATA,
                                               config.ARCHIVES)
            temp_zip_dir = os.path.join(wksp,
                                        config.PROGRAM_SETTINGS,
                                        config.TEMP,
                                        tar_gz_file_base)
            archive_dir = QFileDialog.getExistingDirectory(
                None, 'Select Destination Location', initial_archive_dir)
            if not archive_dir:
                export_message = export_cancel_message
                err = False
            else:
                QgsMessageLog.logMessage(u'Exporting files to GeoCLIM archive',
                                         level=Qgis.Info)
                tar_gz_path = os.path.join(
                    archive_dir,
                    tar_gz_file_base + config.ARCHIVE_SUFFIX)
                reply = QMessageBox.Yes
                if os.path.exists(tar_gz_path):
                    reply = QMessageBox.question(
                        self,
                        'Archive already exists!!',
                        (u'If you continue the existing archive ' +
                         'file will be overwritten.  Proceed?'),
                        QMessageBox.Yes, QMessageBox.Cancel)
                if reply == QMessageBox.Cancel:
                    export_message = export_cancel_message
                else:
                    if not os.path.exists(archive_dir):
                        os.makedirs(archive_dir)
                    if not os.path.exists(temp_zip_dir):
                        os.makedirs(temp_zip_dir)
                    # create a metadata file to add to the archive
                    arc_util.write_dataset_metadata(
                        self.ds_dic, tar_gz_file_base)
                    shutil.copy(
                        os.path.join(self.ds_dic['DATAFOLDER'],
                                     arc_util.METADATA_FILENAME),
                        temp_zip_dir)
                    # copy selected files (and the headers) into a temp folder
                    # if multiple file types this will need work
                    for item in self.ui.fileListWidget.selectedItems():
                        file_base = item.text()
                        file_path = os.path.join(self.ds_dic['DATAFOLDER'],
                                                 file_base)
                        shutil.copy(file_path, temp_zip_dir)
                        if self.ds_dic['DATASUFFIX'] == config.BIL_SUFFIX:
                            hdr_path = file_path.replace(
                                self.ds_dic['DATASUFFIX'], config.HDR_SUFFIX)
                            shutil.copy(hdr_path, temp_zip_dir)
                    # zip up the folder and place output in archive folder
                    arc_util.gc_zip_file(tar_gz_path, temp_zip_dir)
                    # remove temp files
                    try:
                        shutil.rmtree(temp_zip_dir)
                    except OSError:
                        pass
                    export_message = 'GeoCLIM Archive export completed'
                    err = False
        else:
            export_message = export_cancel_message
            err = False
        return err, export_message

#    def export_netcdf(self):
#        '''
#        Export selected files to NetCDF.
#        returns(boolean) - err - Error Success = False, Error = True
#        '''
#        err = True
#        if self.ds_dic['PERIODICITY'] == config.PERIODICITY_LIST[1]:
#            interval = 'month'
#        elif self.ds_dic['PERIODICITY'] ==\
#                config.PERIODICITY_LIST[0]:
#            interval = 'dekad'
#        else:
#            interval = 'pentad'
#        netcdf_exe_path =\
#            os.path.join(os.path.dirname(os.path.realpath(__file__)),
#                         config.NET_CDF_DIR,
#                         config.NET_CDF_EXE)
#        export_file =\
#            os.path.join(self.wrksp_setup.get_workspace(),
#                         config.PROGRAM_SETTINGS,
#                         config.TEMP,
#                         config.NET_CDF_TXT)
#
#        with open(export_file, 'wb') as file_out:
#            for item in self.ui.fileListWidget.selectedItems():
#                file_base = item.text()
#                file_path = os.path.join(self.ds_dic['DATAFOLDER'], file_base)
#                reg_exp = (r'(\d{' +
#                           str(len(self.ds_dic['DATADATEFORMAT'])) + '})')
#
#                # have to split down to what we think is the date string
#                # and then check if it matches the expected length of the date
#                # string because prefix could contain digits that would
#                # return an incorrect value if we just use the regex alone
#                date_string = os.path.splitext(
#                    os.path.basename(file_base))[0].split(
#                        self.ds_dic['DATAPREFIX'])[1]
#                try:
#                    dt_st_ck = re.findall(reg_exp, date_string)[0]
#                except IndexError:
#                    raise IOError    # will occur if findall comes back empty
#                if dt_st_ck != date_string:
#                    raise IOError
#                year, mon, per = util.split_date_string(
#                    date_string, self.ds_dic['DATADATEFORMAT'])
#                if not mon:
#                    temp_str = (year + '/' + per + '|' +
#                                file_path + '\n').encode()
#                else:
#                    temp_str = (year + '/' + mon + '/' +
#                                per + '|' + file_path + '\n').encode()
#                file_out.write(temp_str)
#        # Note that it is on the user to select the correct location!!!!
#        QgsMessageLog.logMessage(u'Exporting to NetCDF', level=Qgis.Info)
#        cmd = \
#            (util.handle_subprocess_call_path(netcdf_exe_path) +
#             ' exportfromgeoclim ' +
#             interval + ' ' + export_file)
#        err = subprocess.call(cmd, shell=False)
#        return err

    def load_available_files(self):
        '''
        Load the good files into the widget (this does not list averages)
        '''
        self.ui.lblData.setText(u'Available Data')
        self.ui.listMissingButton.setText(u'List Missing')
        self.ui.fileListWidget.clear()
        self.ui.exportButton.setEnabled(True)
        self.ui.deleteButton.setEnabled(True)

        for entry in self.input_file_list:
            self.ui.fileListWidget.addItem(os.path.basename(entry))
        self.ui.fileCtLineEdit.setText(str(len(self.input_file_list)))

    def load_missing_files(self):
        '''
        Load the missing files into the widget (this does not list averages)
        '''
        if self.input_file_list:
            self.missing_file_list = []
            self.ui.lblData.setText(u'Missing Data')
            self.ui.listMissingButton.setText(u'List Available')
            self.ui.fileListWidget.clear()
            self.ui.exportButton.setEnabled(False)
            self.ui.deleteButton.setEnabled(False)

            min_year, beg_mon, beg_per = self.__helper_split_date_string__(
                self.input_file_list[0])
            max_year, end_mon, end_per = self.__helper_split_date_string__(
                self.input_file_list[-1])
            max_per = len(util.get_interval_dic(
                self.ds_dic['PERIODICITY']))

            if self.ds_dic['DATADATEFORMAT'] == "YYYY.MM.DD":
                self.missing_file_list = util.find_missing_files_daily(
                    self.ds_dic, self.input_file_list,
                    min_year, beg_mon, beg_per,
                    max_year, end_mon, end_per)
            else:
                self.missing_file_list = util.find_missing_files(
                    self.ds_dic, self.input_file_list, max_per,
                    min_year, beg_mon, beg_per,
                    max_year, end_mon, end_per)

            for entry in self.missing_file_list:
                self.ui.fileListWidget.addItem(os.path.basename(entry))
            self.ui.fileCtLineEdit.setText(str(len(self.missing_file_list)))

    def switch_display(self, pressed):
        '''
        Switch display to alternate(missing or available) list
        params(boolean) - pressed - state of the button
        '''
        qgs_util.clear_status_msg()
        source = self.sender()
        if pressed:
            self.ui.lblData.setText('Missing files')
            source.setText(u'List Available')
            self.load_missing_files()
        else:
            self.ui.lblData.setText('Available files')
            source.setText(u'List Missing')
            self.load_available_files()
