'''
/***************************************************************************
Name       :  geowrsi_controller.py
Description:  Controller for GeoWRSI Tool
copyright  :  (C) 2022-2023 by FEWS
email      :  dhackman@contractor.usgs.gov
Author     :  Derek Hackman
Modified   :  mm/dd/yyyy - descripton - Description

 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 numpy as np

from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QDialog, QMessageBox, QProgressBar, QVBoxLayout

from qgis.core import Qgis, QgsMessageLog, QgsProject, QgsLayerTree
from qgis.utils import iface

from fews_tools.forms.Ui_GeoWRSIRun import Ui_RunGeoWRSI
from fews_tools.models.crops_model import CropsModel
from fews_tools.models.datasets_model import DatasetsModel
from fews_tools.models.region_model import RegionModel
from fews_tools.models.workspace_setup_model import WorkspaceSetupModel
from fews_tools.workers.geowrsi_worker import GeoWRSIWorker
from fews_tools.utilities.animation_utilities import setup_animation, \
    set_start_end

from fews_tools import fews_tools_config as config
from fews_tools.utilities import geoclim_qgs_utilities as qgs_util
from fews_tools.utilities import geoclim_utilities as util
from fews_tools.utilities import logging_utilities as log_util
from fews_tools.utilities import geowrsi_utilities as geo_util


class GeoWRSIController (QDialog):
    '''
    Class for calculating the WRSI
    '''

    def __init__(self):
        QDialog.__init__(self)
        self.ui = Ui_RunGeoWRSI()
        self.ui.setupUi(self)
        self.is_processing = False  # Flag to indicate if running a process
        self.wrksp_setup = WorkspaceSetupModel()
        self.ds_dic_ppt = {}
        self.ds_dic_pet = {}
        self.region_info = RegionModel()
        self.reg_dic = {}
        self.crop_dic = {}
        self.available_year_list_both = []
        self.input_file_path =\
            os.path.join(self.wrksp_setup.get_workspace(),
                         config.PROGRAM_SETTINGS,
                         config.GEOWRSI_SETTINGS_FILE)
        self.output_file_path =\
            self.wrksp_setup.get_output_path()
        self.set_dic = {
            # Region/crop tab
            'analysis_region': '',
            'crop_type': '',
            # SOS tab
            'sos_type': '',
            'sos_offset': 0,
            'sos_period': 1,
            'sos_file': '',
            # LGP tab
            'lgp_type': '',
            'lgp_period': 1,
            'lgp_file': '',
            # WHC Tab
            'whc_type': '',
            'whc_mm': 0.0,
            'whc_file': '',
            # Mask Tab
            'mask_type': '',
            'mask_file': '',
            # Precip/PET data
            'precip_type': '',
            'pet_type': '',
            'use_simulated_data': False,
            'ppt_dataset': '',
            'pet_dataset': '',
            'initial_period': '',
            'final_period': '',
            'start_year': 0,
            'cross_year': False,
            'needed_basenames_list_ppt': [],
            'available_files_list_ppt': [],
            'missing_files_list_ppt': [],
            'needed_basenames_list_pet': [],
            'available_files_list_pet': [],
            'missing_files_list_pet': [],
            'output_file_list_wrsi': [],
            'output_file_list_lgp_phen': [],
            'output_file_list_swi': [],
            'output_file_wrsi_eos': '',
            'output_file_wrsi_anom_eos': '',
            'output_file_lgp_phen_eos': '',
            'output_file_swi_eos': '',
            'output_file_water_req_tot_eos': '',
            'output_file_max_water_def_eos': '',
            'output_file_water_def_tot_eos': '',
            'output_file_max_water_surp_eos': '',
            'output_file_water_surp_tot_eos': '',
            'output_file_aet_tot_eos': '',
            'output_file_sos': '',
            'output_file_sos_anom': '',
            'output_file_list_crop_stages': [],
            'output_file_list_wrsi_cur': [],
            'output_file_list_lgp_phen_cur': [],
            'output_file_list_swi_cur': [],
            'output_file_wrsi_cur': '',
            'output_file_wrsi_anom_cur': '',
            'output_file_lgp_phen_cur': '',
            'output_file_swi_cur': '',
            'output_file_water_req_tot_cur': '',
            'output_file_max_water_def_cur': '',
            'output_file_water_def_tot_cur': '',
            'output_file_max_water_surp_cur': '',
            'output_file_water_surp_tot_cur': '',
            'output_file_aet_tot_cur': '',
            'output_file_sos_cur': '',
            'output_file_sos_anom_cur': '',
            'output_file_list_wrsi_for': [],
            'output_file_list_lgp_phen_for': [],
            'output_file_list_swi_for': [],
            'output_file_wrsi_for': '',
            'output_file_wrsi_anom_for': '',
            'output_file_lgp_phen_for': '',
            'output_file_swi_for': '',
            'output_file_water_req_tot_for': '',
            'output_file_max_water_def_for': '',
            'output_file_water_def_tot_for': '',
            'output_file_max_water_surp_for': '',
            'output_file_water_surp_tot_for': '',
            'output_file_aet_tot_for': '',
            'output_file_sos_for': '',
            'output_file_sos_anom_for': '',
            'type_of_sos_calculation': "3-Period Rain Threshold",
            'period_1_rainfall': 25,
            'period_2_3_rainfall': 20,
            'ignore_sos_clim': False,
            'max_periods_early': 2,
            'max_periods_late': 9,
            'recalculate_sos_after_crop_failure': False,
            'crop_failure_definition_for_restart': None,
            'crop_failure_definition_for_restart_percent': 30,
            'dont_restart_after': 40,
            'max_growing_periods_after_eos': 6,
            'exclude_areas_less_than': False,
            'exclude_areas_less_than_percent': 90,
            'wrsi_run_type': '',  # multi_historical or historical or current
            'forecast_run': False,
            'ext_avg_run': False,
            'ext_med_run': False,
            'ext_years_list': [],
            'ext_output_files_dict': {},
            'ext_ppt_data_dict': {},
            'ext_pet_data_dict': {},
            'ppt_includes_forecast': False,
            'forecast_ppt_file': '',
            'forecast_pet_avg_file': '',
            'num_current_periods': 0,
            'region_name': '',
            'hist_years_list': [],
            'hist_output_files_dict': {},
            'hist_ppt_data_dict': {},
            'hist_pet_data_dict': {},
        }
        self.output_settings_path =\
            os.path.join(self.wrksp_setup.get_workspace(),
                         config.PROGRAM_SETTINGS,
                         config.GEOWRSI_OUT_SETTINGS_FILE)
        self.output_dic = geo_util.get_default_output_dic()
        self.output_dic['output_directory'] =\
            self.wrksp_setup.get_output_path()
        # used in the datachecks ---------------------------------------
        self.open_file_info = None
        # set up worker
        self.worker = None
        self.thread = None
        self.test_wrsi_color_file = \
            os.path.join(self.wrksp_setup.get_colors_path(),
                         config.WRSI_COLOR_FILE)
        self.test_swi_color_file = \
            os.path.join(self.wrksp_setup.get_colors_path(),
                         config.SWI_COLOR_FILE)
        self.test_lgp_phen_color_file = \
            os.path.join(self.wrksp_setup.get_colors_path(),
                         config.LGP_PHEN_COLOR_FILE)
        self.test_totals_color_file = \
            os.path.join(self.wrksp_setup.get_colors_path(),
                         config.TOTALS_COLOR_FILE)
        self.max_water_testing_color_file = \
            os.path.join(self.wrksp_setup.get_colors_path(),
                         config.MAX_COLOR_FILE)
        self.wrsi_anom_color_file = \
            os.path.join(self.wrksp_setup.get_colors_path(),
                         config.WRSI_ANOM_COLOR_FILE)
        self.sos_anom_dekads_color_file = \
            os.path.join(self.wrksp_setup.get_colors_path(),
                         config.SOS_ANOM_DEKAD_COLOR_FILE)
        self.sos_anom_pentads_color_file = \
            os.path.join(self.wrksp_setup.get_colors_path(),
                         config.SOS_ANOM_PENTAD_COLOR_FILE)
        # Run once flag
        self.checked_extended_years = False
        # Setup Ui
        self.ui.progressBar.setValue(0)
        self.ui.cancelButton.setEnabled(False)
        self.closed = False  # A flag to indicate if close_event is triggered
        self.close_event = None
        load_error = self.initial_form_load()
        if load_error:
            self.ui.okButton.setEnabled(False)
        self.ui.okButton.clicked.connect(self.start_geowrsi)
        self.ui.yearComboBox.currentIndexChanged.connect(
            self.setup_gui_functionality)
        self.ui.checkBoxExtendedWRSIAverage.stateChanged.connect(
            self.set_extended_avg_checkbox)
        self.ui.checkBoxExtendedWRSIMedian.stateChanged.connect(
            self.set_extended_med_checkbox)
        self.ui.pushButtonSelectAllYears.clicked.connect(
            self.select_all_ext_hist_years)
        self.ui.checkBoxMulptipleHistoricalYears.stateChanged.connect(
            self.set_multi_hist_years_checkbox)

    def initial_form_load(self):
        '''
        This function is called inside the init function in order to
        setup the default datasets for PPT and PET data, fill the years
        comboBox on GUI and read in the information from the
        config.GEOWRSI_SETTINGS_FILE file
        returns(bool) - if there was an error in loading data for form
        '''
        # read in the settings dictionary from the
        # config.GEOWRSI_SETTINGS_FILE file in ProgramSettings
        if self.fill_settings_dict():
            return True
        # Check for existing config.GEOWRSI_OUT_SETTINGS_FILE file
        # Fill it if it does, otherwise leave it as default settings
        self.output_dic = geo_util.set_output_dic_from_file(
            self.output_settings_path, self.output_dic)
        # Call load datasets function
        self.load_datasets()
        self.ui.lineEditPPT.setText(self.ds_dic_ppt['DATASETNAME'])
        self.ui.lineEditPPT.setEnabled(False)
        self.ui.lineEditPET.setText(self.ds_dic_pet['DATASETNAME'])
        self.ui.lineEditPET.setEnabled(False)
        self.ui.lineEditRegion.setText(self.set_dic['analysis_region'])
        self.ui.lineEditRegion.setEnabled(False)
        self.ui.lineEditCrop.setText(self.set_dic['crop_type'])
        self.ui.lineEditCrop.setEnabled(False)
        # Check each dataset was filled
        if not self.ds_dic_ppt:
            QMessageBox.warning(
                self,
                'Dataset missing!!',
                'No PPT dataset selected, '
                'or selected dataset does not exist!!',
                QMessageBox.Ok)
            return True
        if not self.ds_dic_pet:
            QMessageBox.warning(
                self,
                'Dataset missing!!',
                'No PET dataset selected, '
                'or selected dataset does not exist!!',
                QMessageBox.Ok)
            return True
        # Check datasets have same PERIODICITY
        if self.ds_dic_ppt['PERIODICITY'] != self.ds_dic_pet['PERIODICITY']:
            QMessageBox.information(self,
                                    u'Dataset Periodicity mismatch!!',
                                    'PPT and PET datasets do '
                                    'not use same periodicity',
                                    QMessageBox.Ok)
            self.ui.lineEdit.setText('Error - Dataset Periodicity Mismatch')
            return True
        interval_dic = util.get_interval_dic(
            self.ds_dic_ppt['PERIODICITY'])
        self.ui.lineEdit.setText(self.ds_dic_ppt['PERIODICITY'])
        self.ui.lineEdit.setEnabled(False)
        # Based on PPT data to get list of all files in order
        # to get possible years
        input_file_list_ppt = \
            util.get_input_file_list(self.ds_dic_ppt)
        input_file_list_pet = \
            util.get_input_file_list(self.ds_dic_pet)
        if not input_file_list_ppt:
            self.ui.yearComboBox.clear()
            QMessageBox.warning(
                self,
                'Data missing!!',
                'No data available for PPT dataset '
                + self.ds_dic_ppt['DATASETNAME'] + '!!',
                QMessageBox.Ok)
            return True
        if not input_file_list_pet:
            self.ui.yearComboBox.clear()
            QMessageBox.warning(
                self,
                'Data missing!!',
                'No data available for PET dataset '
                + self.ds_dic_pet['DATASETNAME'] + '!!',
                QMessageBox.Ok)
            return True
        # Fill the year's combo box
        available_year_list_ppt = \
            util.extract_data_years(
                self.ds_dic_ppt, input_file_list_ppt)
        available_year_list_pet = \
            util.extract_data_years(
                self.ds_dic_pet, input_file_list_pet)
        self.available_year_list_both = list(
            set(available_year_list_ppt).intersection(
                set(available_year_list_pet)))
        year_end_period = len(interval_dic)
        mid_point = util.get_midpoint(self.ds_dic_ppt)
        util.fill_year_widget(self.ui.yearComboBox, False,
                              available_year_list_ppt,
                              1, year_end_period, mid_point)
        # Call set and check region function
        if self.set_and_check_region_from_txt_file():
            return
        # Set cross year to true or false
        self.set_dic['cross_year'] = \
            self.set_dic['initial_period'] > self.set_dic['final_period']
        # ask if the ppt data includes a forecast dekad
        ans = QMessageBox.question(
            None, self.ds_dic_ppt['DATASETNAME'],
            ('Is there a forecast period in the PPT dataset?'),
            QMessageBox.Yes, QMessageBox.No)
        self.set_dic['ppt_includes_forecast'] = ans == QMessageBox.Yes
        # Setup GUI Functionality
        self.setup_gui_functionality()
        # Start with these disabled before we
        self.ui.yearListWidget.setEnabled(False)
        self.ui.pushButtonSelectAllYears.setEnabled(False)
        return False

    def setup_gui_functionality(self):
        '''
        Based on the selected start year in the combo box this will decide
        what to enable and disable (historical, current, forecast, extended)
        '''
        # Grab currently selected start year
        self.get_start_year()
        # Reset forecast ppt file back to empty when we reset the gui
        # so that it will repop as needed in a current run if we run again
        # for a different year that is also setup for a current run based
        # on the data
        self.set_dic['forecast_ppt_file'] = ''
        self.set_dic['forecast_pet_avg_file'] = ''
        # Get list of files that would be needed for historical process
        # based on this start year and call
        # get_ppt_pet_file_lists_wrsi_historical function
        (self.set_dic['needed_basenames_list_ppt'],
         self.set_dic['available_files_list_ppt'],
         self.set_dic['missing_files_list_ppt'],
         self.set_dic['needed_basenames_list_pet'],
         self.set_dic['available_files_list_pet'],
         self.set_dic['missing_files_list_pet']) =\
            self.get_ppt_pet_file_lists_wrsi_historical(
                self.set_dic['initial_period'],
                self.set_dic['final_period'],
                self.set_dic['start_year'],
                self.set_dic['cross_year'])
        # If all files for historical run exists then disable
        # forecast and extended
        if not (self.set_dic['missing_files_list_ppt'] or
                self.set_dic['missing_files_list_pet']):
            self.disable_forecast_and_extended()
            self.set_dic['wrsi_run_type'] = config.HISTORICAL
        # If files are missing, check if it is only files at the end of season
        # Enable forecast and extended options
        else:
            current_run = self.check_if_current()
            # if there are missing files at EOS and PPT/PET missing files
            # start at the same period
            if current_run:
                self.enable_forecast_and_extended()
                self.set_dic['wrsi_run_type'] = config.CURRENT
            # else missing files are in the middle of the season or PPT/PET
            # missing files are for different periods so give a warning message
            else:
                # It can't be a valid current year if we have data for the
                # last period of data, so if we are missing files we will
                # alert user when they go to run and ask if they would like to
                # fill them in with CLIM data
                self.disable_forecast_and_extended()
                self.set_dic['wrsi_run_type'] = config.HISTORICAL

    def check_if_current(self):
        '''
        This function is use to check if there is a missing file at the end
        the needed PPT data, if so, then it must be a current run
        returns(bool) - current_run - True if current_run, False if not
        '''
        current_run = False
        missing_list_length_ppt = len(self.set_dic['missing_files_list_ppt'])
        if missing_list_length_ppt != 0:
            last_missing_file_basename = os.path.basename(
                self.set_dic['missing_files_list_ppt'][-1])
            if self.set_dic['needed_basenames_list_ppt'][-1] ==\
                last_missing_file_basename:
                current_run = True
        return current_run

    def get_missing_files_within_season(self):
        '''
        This function will get the list of missing PPT and PET files within
        the season based on the most recent PPT data available in that season
        returns(list[string]) - missing_ppt - List of the ppt files within
            the season that are missing and need to be filled in order to run
        returns(list[string]) - mising_pet -  List of the pet files within
            the season that are missing and need to be filled in order to run
        '''
        missing_ppt = []
        missing_pet = []
        last_avail_ppt = self.set_dic['available_files_list_ppt'][-1]
        last_avail_ppt_basename = os.path.basename(last_avail_ppt)
        last_avail_ppt_index =\
            self.set_dic['needed_basenames_list_ppt'].index(
                last_avail_ppt_basename)
        
        for missing_ppt_file in self.set_dic['missing_files_list_ppt']:
            missing_ppt_basename = os.path.basename(missing_ppt_file)
            index_ppt = self.set_dic['needed_basenames_list_ppt'].index(
                missing_ppt_basename)
            if index_ppt < last_avail_ppt_index:
                missing_ppt.append(missing_ppt_file)

        for missing_pet_file in self.set_dic['missing_files_list_pet']:
            missing_pet_basename = os.path.basename(missing_pet_file)
            index_pet = self.set_dic['needed_basenames_list_pet'].index(
                missing_pet_basename)
            if index_pet <= last_avail_ppt_index:
                missing_pet.append(missing_pet_file)

        return missing_ppt, missing_pet

    def fill_missing_files_within_season(self, missing_ppt, missing_pet):
        '''
        This will fill the missing files within a season
        args(list[string]) - missing_ppt - List of the ppt files within
            the season that are missing and need to be filled in order to run
        args(list[string]) - mising_pet -  List of the pet files within
            the season that are missing and need to be filled in order to run
        returns(bool) - err - if any missing files are not able to be filled
            with clim data, otherwise returns false
        returns(list[string]) - missing_avg_files - list of missing avg files,
            and/or if there was an error trying to get a avg file for a
            missing file
        '''
        err = False
        missing_avg_files = []
        for ppt in missing_ppt:
            # Get clim file
            avg_file = self.get_clim_avg_file(ppt, self.ds_dic_ppt)
            if avg_file is None:
                # Report issue trying to get avg file for the missing file
                missing_avg_files.append(
                    f"Issue in dataset for finding clim avg file for: {ppt}")
                err = True
            elif not os.path.exists(avg_file):
                # Report missing the avg file to user and return false
                missing_avg_files.append(avg_file)
                err = True
            else:
                # Fill into the available files list with avg file at
                # correct index
                ppt_basename = os.path.basename(ppt)
                fill_index_ppt =\
                    self.set_dic['needed_basenames_list_ppt'].index(
                        ppt_basename)
                self.set_dic['available_files_list_ppt'].insert(
                    fill_index_ppt, avg_file)

        for pet in missing_pet:
            # Get clim file
            avg_file = self.get_clim_avg_file(pet, self.ds_dic_pet)
            if avg_file is None:
                # Report issue trying to get avg file for the missing file
                missing_avg_files.append(
                    f"Issue in dataset for finding clim avg file for: {pet}")
                err = True
            elif not os.path.exists(avg_file):
                # Report missing the avg file to user and return false
                missing_avg_files.append(avg_file)
                err = True
            else:
                # Fill into the available files list with avg file at
                # correct index
                pet_basename = os.path.basename(pet)
                fill_index_pet =\
                    self.set_dic['needed_basenames_list_pet'].index(
                        pet_basename)
                self.set_dic['available_files_list_pet'].insert(
                    fill_index_pet, avg_file)

        return err, missing_avg_files

    def get_clim_avg_file(self, missing_file, ds_dic):
        '''
        This function will take in the missing file and the corresponding
        ds_dic to then get the needed clim avg file based on that
        args(string) - missing_file - the missing file we are looking for
            the corresponding clim avg file we need
        args(dict) - ds_dic - the ppt or pet dataset dictionary 
        returns(string) - avg_file - the clim avg file path we would need to
            fill with
        '''
        interval_list = util.get_all_period_string(ds_dic)
        missing_file_no_suffix = \
            missing_file.split(ds_dic['DATASUFFIX'])[0]
        for period in interval_list:
            per_len = len(period)
            if period == missing_file_no_suffix[-per_len:]:
                avg_file = os.path.join(
                    ds_dic['AVGDATAFOLDER'],
                    ds_dic['AVGDATAPREFIX'] + period +
                    ds_dic["AVGDATASUFFIX"])
                return avg_file
        return None

    def enable_forecast_and_extended(self):
        '''
        Used to enable the GUI options for forecast and extended options
        '''
        self.ui.checkBoxForecast.setEnabled(True)
        self.ui.checkBoxExtendedWRSIAverage.setEnabled(True)
        self.ui.checkBoxExtendedWRSIMedian.setEnabled(True)
        if self.ui.checkBoxExtendedWRSIAverage.isChecked()\
                or self.ui.checkBoxExtendedWRSIMedian.isChecked():
            self.ui.yearListWidget.setEnabled(True)
            self.ui.pushButtonSelectAllYears.setEnabled(True)

    def disable_forecast_and_extended(self):
        '''
        Used to disable the GUI options for forecast and extended options
        '''
        self.ui.checkBoxForecast.setEnabled(False)
        self.ui.checkBoxExtendedWRSIAverage.setEnabled(False)
        self.ui.checkBoxExtendedWRSIMedian.setEnabled(False)
        self.ui.yearListWidget.setEnabled(False)
        self.ui.pushButtonSelectAllYears.setEnabled(False)

    def set_extended_avg_checkbox(self):
        '''
        Used to set the extended checkbox functionality when avgerage
        checkbox is toggled
        '''
        self.check_extended_historical_years()
        if self.ui.checkBoxExtendedWRSIAverage.isChecked():
            self.ui.checkBoxExtendedWRSIMedian.setChecked(False)
            self.ui.yearListWidget.setEnabled(True)
            self.ui.pushButtonSelectAllYears.setEnabled(True)
        else:
            self.ui.yearListWidget.setEnabled(False)
            self.ui.pushButtonSelectAllYears.setEnabled(False)

    def set_extended_med_checkbox(self):
        '''
        Used to set the extended checkbox functionality when median
        checkbox is toggled
        '''
        self.check_extended_historical_years()
        if self.ui.checkBoxExtendedWRSIMedian.isChecked():
            self.ui.checkBoxExtendedWRSIAverage.setChecked(False)
            self.ui.yearListWidget.setEnabled(True)
            self.ui.pushButtonSelectAllYears.setEnabled(True)
        else:
            self.ui.yearListWidget.setEnabled(False)
            self.ui.pushButtonSelectAllYears.setEnabled(False)

    def set_multi_hist_years_checkbox(self):
        '''
        Used to set the multiple historical years functionality when the
        checkbox is toggled
        '''
        self.check_extended_historical_years()
        if self.ui.checkBoxMulptipleHistoricalYears.isChecked():
            self.ui.lblExtendedYears.setText(
                "Select Years to run historical WRSI")
            self.ui.yearListWidget.setEnabled(True)
            self.ui.pushButtonSelectAllYears.setEnabled(True)
            self.ui.yearComboBox.setEnabled(False)
            self.ui.checkBoxForecast.setEnabled(False)
            self.ui.checkBoxExtendedWRSIAverage.setEnabled(False)
            self.ui.checkBoxExtendedWRSIMedian.setEnabled(False)
            self.set_dic['wrsi_run_type'] = config.MULTI_HISTORICAL
        else:
            self.ui.lblExtendedYears.setText(
                "Select Years to use in extended WRSI")
            self.ui.yearComboBox.setEnabled(True)
            self.ui.checkBoxForecast.setEnabled(True)
            self.ui.checkBoxExtendedWRSIAverage.setEnabled(True)
            self.ui.checkBoxExtendedWRSIMedian.setEnabled(True)
            if self.ui.checkBoxExtendedWRSIAverage.isChecked():
                self.set_extended_avg_checkbox()
            else:
                self.set_extended_med_checkbox()
            self.setup_gui_functionality()

    def check_extended_historical_years(self):
        '''
        Used to check the available extended/historical years in the data and 
        fill the yearListWidget widget
        '''
        if not self.checked_extended_years:
            self.checked_extended_years = True

            extended_years_list = []
            year_end_period = len(util.get_interval_dic(
                self.ds_dic_ppt['PERIODICITY']))
            mid_point = util.get_midpoint(self.ds_dic_ppt)

            # Create a custom QDialog for the progress bar
            progress_dialog = QDialog(self)
            progress_dialog.setWindowTitle("Checking Available Extended Years")
            progress_dialog.setFixedWidth(250)
            # Create a progress bar
            progress_bar = QProgressBar()

            # Set progress min and max
            progress_bar.setRange(0, len(self.available_year_list_both) - 1)

            # Create a layouyt for the dialog
            layout = QVBoxLayout()
            layout.addWidget(progress_bar)
            progress_dialog.setLayout(layout)

            # Force the dialog to be on the top layer
            progress_dialog.setWindowFlags(
                progress_dialog.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
            progress_dialog.setModal(True)

            # Show the dialog
            progress_dialog.show()

            # This loop is to fill the extended years list with
            # valid years only
            for count, yr in enumerate(self.available_year_list_both):
                # check for needed historical data to add yr to
                # extended_years_list
                _, _, missing_files_list_ppt, _, _, missing_files_list_pet =\
                    self.get_ppt_pet_file_lists_wrsi_historical(
                        self.set_dic['initial_period'],
                        self.set_dic['final_period'],
                        int(yr),
                        self.set_dic['cross_year'])
                if not (missing_files_list_ppt or
                        missing_files_list_pet):
                    extended_years_list.append(yr)

                progress_bar.setValue(count)
                # Allow GUI updates
                QtCore.QCoreApplication.processEvents()

            progress_dialog.close()

            util.fill_year_widget(self.ui.yearListWidget, False,
                                  extended_years_list,
                                  1, year_end_period, mid_point)

    def select_all_ext_hist_years(self):
        '''
        Used to select all items in the years list widget
        '''
        if self.ui.pushButtonSelectAllYears.text() ==\
                "Select All Years":
            self.ui.yearListWidget.selectAll()
            self.ui.pushButtonSelectAllYears.setText(
                "Clear All Years")
        else:
            self.ui.yearListWidget.clearSelection()
            self.ui.pushButtonSelectAllYears.setText(
                "Select All Years")

    def load_datasets(self):
        '''
        This function will take the default PPT and default PET
        datasets, query them and set the ds_dic for each of them
        '''
        # load the dataset for PPT
        ds_info_ppt = DatasetsModel()
        if not ds_info_ppt.query_named_dataset(self.set_dic['ppt_dataset']):
            self.ds_dic_ppt = ds_info_ppt.get_ds_dictionary()
        else:
            self.ds_dic_ppt = None
        # load the dataset for PET
        ds_info_pet = DatasetsModel()
        if not ds_info_pet.query_named_dataset(self.set_dic['pet_dataset']):
            self.ds_dic_pet = ds_info_pet.get_ds_dictionary()
        else:
            self.ds_dic_pet = None

    def fill_settings_dict(self):
        '''
        This function will open the config.GEOWRSI_SETTINGS_FILE file in order
        to fill the set_dic with needed data from the
        geowrsi_settings_controller
        returns(bool) - err - if there was an error or not loading set_dic
        '''
        if os.path.exists(self.input_file_path):
            lines = []
            with open(self.input_file_path, "r") as f_obj:
                lines = f_obj.readlines()
            for line in lines:
                row = line.split(' ', 1)
                if row[1].strip() == "True":
                    self.set_dic[row[0]] = True
                elif row[1].strip() == "False":
                    self.set_dic[row[0]] = False
                else:
                    self.set_dic[row[0]] = row[1].strip()
            return False
        QMessageBox.information(
            self, config.GEOWRSI_SETTINGS_FILE + ' File: Path Not Found',
            'The path to the settings file cannot be found!\n' +
            f'Expected file path: {self.input_file_path}\n' +
            f'User needs to first run the WRSI Settings tool', QMessageBox.Ok)
        # Fill lines with message saying to run settings tool
        self.ui.lineEditPPT.setText("Need to run WRSI Settings tool first")
        self.ui.lineEditPPT.setEnabled(False)
        self.ui.lineEditPET.setText("Need to run WRSI Settings tool first")
        self.ui.lineEditPET.setEnabled(False)
        self.ui.lineEditRegion.setText("Need to run WRSI Settings tool first")
        self.ui.lineEditRegion.setEnabled(False)
        self.ui.lineEditCrop.setText("Need to run WRSI Settings tool first")
        self.ui.lineEditCrop.setEnabled(False)
        return True

    def set_and_check_region_from_txt_file(self):
        '''
        This function will be called along with the datachecks at the start
        of the start_geowrsi function, it queries the named region that is
        read in from the settings dic and splits it from the comments about
        the region in order to get only the region name
        '''
        if not self.region_info.query_named_region(
                self.set_dic['analysis_region']):
            self.reg_dic = self.region_info.get_region_dictionary()
        else:
            QMessageBox.information(
                self, 'Region Not Found',
                'Error: The region from the ' + config.GEOWRSI_SETTINGS_FILE
                + ' file cannot be found in database!', QMessageBox.Ok)
            return True
        if self.ds_dic_ppt['PERIODICITY'] != self.reg_dic['PeriodType']:
            QMessageBox.information(
                self, 'Region vs Dataset Periodicity Mismatch',
                'Error: The region periodicity is '
                + self.reg_dic['PeriodType']
                + ', and datasets periodicity is '
                + self.ds_dic_ppt['PERIODICITY']
                + '.', QMessageBox.Ok)
            return True
        if self.check_files():
            return True
        self.set_dic['initial_period'] = self.reg_dic['InitialPeriod']
        self.set_dic['final_period'] = self.reg_dic['FinalPeriod']
        self.set_dic['region_name'] = \
            self.reg_dic['RegionName'].strip()\
                .replace(" ", "_").replace("-", "_")
        return False

    def check_files(self):
        '''
        This function is used to make sure that the selected and default file
        paths actually exists.
        '''
        flag = False
        if self.set_dic['sos_type'] == 'Selected':
            # Check that file path is not empty and that it exists
            if self.set_dic['sos_file'] == '':
                # Error - SOS type is Selected but no file has been chosen
                QMessageBox.information(
                    self, 'Missing selected sos file',
                    'Selected sos type: No file selected!', QMessageBox.Ok)
                flag = True
            elif not os.path.exists(self.set_dic['sos_file']):
                # Error - Path to selected file does not exist
                QMessageBox.information(
                    self, 'Selected SOS File: Path Not Found',
                    'The selected path to the SOS file cannot be found!',
                    QMessageBox.Ok)
                flag = True
            else:
                sos_params = qgs_util.extract_raster_file_params(
                    self.set_dic['sos_file'])
                sos_ok = \
                    qgs_util.check_dataset_vs_region_extents(
                        self, sos_params[0],
                        qgs_util.get_region_extent(self.reg_dic))
                if not sos_ok:
                    QMessageBox.information(
                        self, 'Selected SOS File: Error',
                        'This file has a region mismatch error',
                        QMessageBox.Ok)
                    flag = True
        elif self.set_dic['sos_type'] == 'Calculated':
            # Check that file path is not empty and that it exists
            if self.reg_dic['SOS'] == '':
                # Error - SOS type is Selected but no file has been chosen
                QMessageBox.information(
                    self, 'Missing caclulated sos file',
                    'Calculated sos type: Region SOS file not found!',
                    QMessageBox.Ok)
                flag = True
            elif not os.path.exists(self.reg_dic['SOS']):
                # Error - Path to selected file does not exist
                QMessageBox.information(
                    self, 'Calculated SOS File: Path Not Found',
                    'The path to the Region SOS file cannot be found!',
                    QMessageBox.Ok)
                flag = True
            else:
                sos_params = qgs_util.extract_raster_file_params(
                    self.reg_dic['SOS'])
                sos_ok = \
                    qgs_util.check_dataset_vs_region_extents(
                        self, sos_params[0],
                        qgs_util.get_region_extent(self.reg_dic))
                if not sos_ok:
                    QMessageBox.information(
                        self, 'Selected SOS File: Error',
                        'This file has a region mismatch error',
                        QMessageBox.Ok)
                    flag = True
        if self.set_dic['lgp_type'] == 'Selected' or\
                self.set_dic['lgp_type'] == 'Default':
            # Check that file path is not empty and that it exists
            if self.set_dic['lgp_file'] == '':
                # Error - SOS type is Selected but no file has been chosen
                QMessageBox.information(
                    self, 'Missing LGP File',
                    'LGP: No file selected!', QMessageBox.Ok)
                flag = True
            elif not os.path.exists(self.set_dic['lgp_file']):
                # Error - Path to selected file does not exist
                QMessageBox.information(
                    self, 'LGP File: Path Not Found',
                    'The path to the LGP file cannot be found!',
                    QMessageBox.Ok)
                flag = True
            else:  # Check file extents
                lgp_params = qgs_util.extract_raster_file_params(
                    self.set_dic['lgp_file'])
                lgp_ok = \
                    qgs_util.check_dataset_vs_region_extents(
                        self, lgp_params[0],
                        qgs_util.get_region_extent(self.reg_dic))
                if not lgp_ok:
                    QMessageBox.information(
                        self, 'LGP File: Error',
                        'This file has a region mismatch error',
                        QMessageBox.Ok)
                    flag = True
        if self.set_dic['whc_type'] == 'Selected' or\
                self.set_dic['whc_type'] == 'Default':
            # Check that file path is not empty and that it exists
            if self.set_dic['whc_file'] == '':
                # Error - SOS type is Selected but no file has been chosen
                QMessageBox.information(
                    self, 'Missing WHC File',
                    'WHC: No file selected!', QMessageBox.Ok)
                flag = True
            elif not os.path.exists(self.set_dic['whc_file']):
                # Error - Path to selected file does not exist
                QMessageBox.information(
                    self, 'WHC File: Path Not Found',
                    'The path to the WHC file cannot be found!',
                    QMessageBox.Ok)
                flag = True
            else:
                whc_params = qgs_util.extract_raster_file_params(
                    self.set_dic['whc_file'])
                whc_ok = \
                    qgs_util.check_dataset_vs_region_extents(
                        self, whc_params[0],
                        qgs_util.get_region_extent(self.reg_dic))
                if not whc_ok:
                    QMessageBox.information(
                        self, 'WHC File: Error',
                        'This file has a region mismatch error',
                        QMessageBox.Ok)
                    flag = True
        if self.set_dic['mask_type'] == 'Selected' or\
                self.set_dic['mask_type'] == 'Default':
            # Check that file path is not empty and that it exists
            if self.set_dic['mask_file'] == '':
                # Error - SOS type is Selected but no file has been chosen
                QMessageBox.information(
                    self, 'Missing Mask File',
                    'Mask: No file selected!', QMessageBox.Ok)
                flag = True
            elif not os.path.exists(self.set_dic['mask_file']):
                # Error - Path to selected file does not exist
                QMessageBox.information(
                    self, 'Mask File: Path Not Found',
                    'The path to the Mask file cannot be found!',
                    QMessageBox.Ok)
                flag = True
            else:
                mask_params = qgs_util.extract_raster_file_params(
                    self.set_dic['mask_file'])
                mask_ok = \
                    qgs_util.check_dataset_vs_region_extents(
                        self, mask_params[0],
                        qgs_util.get_region_extent(self.reg_dic))
                if not mask_ok:
                    QMessageBox.information(
                        self, 'Mask File: Error',
                        'This file has a region mismatch error',
                        QMessageBox.Ok)
                    flag = True
        return flag

    def get_ppt_pet_file_lists_wrsi_historical(self, initial_period,
                                               final_period, start_year,
                                               cross_year):
        '''
        Takes the initialization data for region, year and datasets. Gets the
        file lists for the data encompassing the beginning of the period
        described by the region file, along with the 6 previous dekads of
        information before the period starts, and fills them into the
        available/missing file
        lists for future use
        params(int) - initial_period - initial period based on sos
        params(int) - final_period - final period based on sos+lgp
        params(int) - start_year - start year from the Run GUI
        params(bool) - cross_year - if it is a cross year then this is true
        returns(list) - needed_basenames_ppt - list of needed ppt basenames
        returns(list) - available_ppt - list of available ppt files
        returns(list) - missing_ppt - list of missing ppt files
        returns(list) - needed_basenames_pet - list of needed pet basenames
        returns(list) - available_pet - list of available pet files
        returns(list) - missing_pet - list of missing pet files
        '''
        all_periods_list_ppt = util.get_consecutive_periods_list(
            self.ds_dic_ppt)
        all_periods_list_pet = util.get_consecutive_periods_list(
            self.ds_dic_pet)
        # Periodicity of both datasets is the same and was checked in form load
        # shift: is for getting the 6 dekads/12 pendads before the initial
        # period and  6 dekads/12 pentads after the final period
        # prev_yr_shift: is for situations where the shift of 6/12 will result
        # in a negative index and we actually need to get a period at the end
        # of the previous year, the best way to do that is decrease the
        # start year by 1 and add the prev_year_shift to the idx_start value
        if self.ds_dic_ppt['PERIODICITY'] == 'Dekadal':
            shift = config.DEKAD_SHIFT
            num_periods_in_yr = config.DEKADS_PER_YEAR
            prev_yr_shift = num_periods_in_yr - shift
        else:
            shift = config.PENTAD_SHIFT
            num_periods_in_yr = config.PENTADS_PER_YEAR
            prev_yr_shift = num_periods_in_yr - shift
        # Get minimum number of needed periods for season
        # final_period + shift - initial_period
        if cross_year:
            min_num_periods = final_period + num_periods_in_yr + shift
        else:
            min_num_periods = final_period + shift
        min_num_periods -= initial_period
        # Set original start index and year higher/lower to start year
        idx_start = initial_period - 1
        year_lower = start_year
        year_higher = start_year
        # Clean temp folder to make sure the files in the max periods
        # calculation are properly resampled
        util.remove_temp_files(self.output_file_path)
        # get max number of periods needed based on SOS and LGP
        max_periods = geo_util.calculate_max_sos_plus_lgp(
            cross_year,
            self.ds_dic_ppt['PERIODICITY'],
            self.output_file_path,
            self.set_dic, self.reg_dic)
        # get extra periods needed based on max periods and inital period
        extra_periods = max_periods - initial_period
        # Use larger value between extra_periods and min_num_periods
        if extra_periods < min_num_periods:
            extra_periods = min_num_periods
        idx_end = idx_start + extra_periods
        # if index will be >= periods in a year add a year to end yr
        # and subtract periods in year from index
        while idx_end >= num_periods_in_yr:
            idx_end -= num_periods_in_yr
            year_higher += 1
        # Handles Pentad vs Dekad depending on what the dataset is setup as
        # Will reset the start period to 6 dekads or 12 pentads before to then
        # pass in as the periods to check for files
        if idx_start >= shift:
            new_start = idx_start - shift
        else:
            # add previous year shift and subtract 1 from start year
            new_start = idx_start + prev_yr_shift
            year_lower -= 1
        # Reset the start_period_<ppt/pet> to the new index value
        # Set values to pass into the check_consecutive_process_inputs function
        # year_lower has needed start year
        # year_higher has needed end year
        start_period_ppt = all_periods_list_ppt[new_start]
        end_period_ppt = all_periods_list_ppt[idx_end]
        start_period_pet = all_periods_list_pet[new_start]
        end_period_pet = all_periods_list_pet[idx_end]
        # Call the util.check_consecutive_process_inputs function for PPT
        needed_basenames_ppt = []
        available_ppt = []
        missing_ppt = []
        needed_basenames_pet = []
        available_pet = []
        missing_pet = []
        (needed_basenames_ppt,
         available_ppt,
         missing_ppt) = \
            util.check_consecutive_process_inputs(
            self.ds_dic_ppt,
            year_lower, year_higher,
            start_period_ppt, end_period_ppt)
        # Call the util.check_consecutive_process_inputs function for PET
        (needed_basenames_pet,
         available_pet,
         missing_pet) = \
            util.check_consecutive_process_inputs(
            self.ds_dic_pet,
            year_lower, year_higher,
            start_period_pet, end_period_pet)
        return (needed_basenames_ppt, available_ppt, missing_ppt,
                needed_basenames_pet, available_pet, missing_pet)

    def get_start_year(self):
        '''
        This function will get the year value from the comboBox
        '''
        self.set_dic['start_year'] = int(self.ui.yearComboBox.currentText())

    def get_current_run_args(self):
        '''
        This function will be used if run type is current to get/set the
        current run types for forecast and the extended options
        '''
        self.set_dic['forecast_run'] = self.ui.checkBoxForecast.isChecked()
        self.set_dic['ext_avg_run'] =\
            self.ui.checkBoxExtendedWRSIAverage.isChecked()
        self.set_dic['ext_med_run'] =\
            self.ui.checkBoxExtendedWRSIMedian.isChecked()
        # Fill extended years list from widget
        self.set_dic['ext_years_list'] =\
            sorted([x.text() for x in self.ui.yearListWidget.selectedItems()])

    def get_historical_run_years(self):
        '''
        This function will be used if the run type is multi_historical and will
        get the list of selected historical years the process will run for
        '''
        # Fill historical years list from widget
        self.set_dic['hist_years_list'] =\
            sorted([x.text() for x in self.ui.yearListWidget.selectedItems()])

    def get_crop_data(self):
        '''
        This function is used to set the crop_dic with needed crop values
        that will be used in the GeoWRSI tool later
        '''
        err = False
        crop_info = CropsModel()
        if not crop_info.query_named_crop(self.set_dic['crop_type']):
            self.crop_dic = crop_info.get_crop_dictionary()
        else:
            QMessageBox.information(
                self, 'Crop Not Found',
                'Error: The crop_type: "' + self.set_dic['crop_type'] +
                '" from the ' + config.GEOWRSI_SETTINGS_FILE +
                ' file cannot be found in database!', QMessageBox.Ok)
            err = True
        return err

    def start_geowrsi(self):
        '''
        This function will be called when the ok button is clicked for the
        GeoWrsi run GUI. It will take the year from the comboBox and then
        perform the calculations from there.
        '''
        self.ui.progressBar.setValue(0)
        # Initial setup to make sure all data is setup and correct ---------
        self.get_start_year()
        # Handle output prefix input
        if len(self.ui.lineEditOutputPrefix.text()) > 25:
            QMessageBox.warning(
                self,
                "Output Prefix too long",
                'The length of the output prefix should be less than 25 '
                'characters',
                QMessageBox.Ok)
            return

        err = util.check_line_edit(self.ui.lineEditOutputPrefix)
        if err is True:
            QMessageBox.warning(
                self,
                "Invalid Output Prefix - " +
                self.ui.lineEditOutputPrefix.text(),
                'Valid characters include alpha, digit, underscore and '
                'hyphen symbol',
                QMessageBox.Ok)
            return

        self.output_dic['output_prefix'] = self.ui.lineEditOutputPrefix.text()
        # Load the crop info
        if self.get_crop_data():
            return

        # Check for open files from output list, ppt, pet,
        # mask, sos, whc, lgp
        self.open_file_info = qgs_util.get_open_files_info()

        if self.set_dic['wrsi_run_type'] == config.MULTI_HISTORICAL:
            # Get the historical run years
            self.get_historical_run_years()
            if len(self.set_dic['hist_years_list']) < 1:
                QMessageBox.information(
                    self,
                    u"No historical years selected!!",
                    "Please select at least 1 historical year from the " +
                    "list in order to run the historical process.",
                    QMessageBox.Ok)
                QgsMessageLog.logMessage(
                    u"No historical years selected!!",
                    "Please select at least 1 historical year from the " +
                    "list in order to run the historical process.",
                    level=Qgis.Info)
                return
            # Get output file name lists
            self.set_multi_historical_output_files_dict()
            # Get data file lists ppt and pet for the missing periods
            self.set_multi_historical_data_dicts()
            # Check for open files needed for multi historical runs
            self.check_for_open_files_multi_historical()

        elif self.set_dic['wrsi_run_type'] == config.HISTORICAL:
            if self.set_dic['missing_files_list_ppt'] or\
                    self.set_dic['missing_files_list_pet']:
                missing_ppt, missing_pet =\
                    self.get_missing_files_within_season()
                ans = QMessageBox.question(
                    self, u'Missing files!!',
                    str(missing_ppt) + str(missing_pet) +
                    "\n\nWould you like to replace them with CLIM avg files?",
                    QMessageBox.Yes, QMessageBox.No)
                if ans == QMessageBox.Yes:
                    # Fill missing files within season
                    err, missing_avg =\
                        self.fill_missing_files_within_season(
                            missing_ppt, missing_pet)
                    if err:
                        QMessageBox.information(
                            self, u'Missing files!!',
                            str(missing_avg),
                            QMessageBox.Ok)
                        QgsMessageLog.logMessage(
                            u'Missing files!! \n ' +
                            str(missing_avg),
                            level=Qgis.Info)
                        return
                else:
                    QMessageBox.information(
                        self, u'Missing files!!',
                        str(self.set_dic['missing_files_list_ppt'])
                        + str(self.set_dic['missing_files_list_pet']),
                        QMessageBox.Ok)
                    QgsMessageLog.logMessage(
                        u'Missing files!! \n PPT: ' +
                        str(self.set_dic['missing_files_list_ppt'])
                        + "\n PET: " + str(self.set_dic['missing_files_list_pet']),
                        level=Qgis.Info)
                    return
            # Setup the output files for the historical run
            self.set_historical_output_files()
            # Check for open historical files
            self.check_for_open_files_historical()

        else:  # Else we are running the Current version
            if self.reg_dic['PeriodType'] == 'Dekadal':
                shift = config.DEKAD_SHIFT
            elif self.reg_dic['PeriodType'] == 'Pentadal':
                shift = config.PENTAD_SHIFT
            if len(self.set_dic['available_files_list_ppt']) < (shift + 1) or\
                    len(self.set_dic['available_files_list_pet']) < (shift + 1):
                QMessageBox.information(
                    self,
                    u"No Data for Region's season!!",
                    "The season for this region starts at the "
                    + str(self.set_dic['initial_period']) +
                    "th period of the year",
                    QMessageBox.Ok)
                QgsMessageLog.logMessage(
                    u"No Data for Region's season!!\n" +
                    "The season for this region starts at the "
                    + str(self.set_dic['initial_period']) +
                    "th period of the year",
                    level=Qgis.Info)
                return
            elif self.set_dic['missing_files_list_ppt'] or\
                    self.set_dic['missing_files_list_pet']:
                missing_ppt, missing_pet =\
                    self.get_missing_files_within_season()
                if not missing_ppt == [] or not missing_pet == []:
                    ans = QMessageBox.question(
                        self, u'Missing files!!',
                        str(missing_ppt) + str(missing_pet) +
                        "\n\nWould you like to replace them with CLIM avg files?",
                        QMessageBox.Yes, QMessageBox.No)

                    if ans == QMessageBox.Yes:
                        # Fill missing files within season
                        err, missing_avg =\
                            self.fill_missing_files_within_season(
                                missing_ppt, missing_pet)
                        if err:
                            QMessageBox.information(
                                self, u'Missing files!!',
                                str(missing_avg),
                                QMessageBox.Ok)
                            QgsMessageLog.logMessage(
                                u'Missing files!! \n ' +
                                str(missing_avg),
                                level=Qgis.Info)
                            return
                    else:
                        QMessageBox.information(
                            self, u'Missing files!!',
                            str(missing_ppt)
                            + str(missing_pet),
                            QMessageBox.Ok)
                        QgsMessageLog.logMessage(
                            u'Missing files!! \n PPT: ' +
                            str(missing_ppt)
                            + "\n PET: " + str(missing_pet),
                            level=Qgis.Info)
                        return

            # Get the needed run args from the GUI
            self.get_current_run_args()

            # Check if PET data is beyond the PPT if so, trim that off
            ppt_len = len(self.set_dic['available_files_list_ppt'])
            pet_len = len(self.set_dic['available_files_list_pet'])
            if pet_len > ppt_len:
                self.set_dic['available_files_list_pet'] =\
                    self.set_dic['available_files_list_pet'][:ppt_len]
                pet_len = len(self.set_dic['available_files_list_pet'])

            if self.set_dic['ppt_includes_forecast']:
                # check to see that the forecast file is empty so that we
                # dont pop off another file from the file list when we are not
                # trying to
                if self.set_dic['forecast_ppt_file'] == '':
                    # Remove the forecast period from the available file list
                    ele = self.set_dic['available_files_list_ppt'].pop()
                    # Save this as the forecast ppt file
                    self.set_dic['forecast_ppt_file'] = ele
                    # If we already have more PET than PPT we can use that instead of
                    # looking for an avg file
                    # Check if PET data is beyond the PPT after removing PPT
                    # forecast period, if so, set forecast PET mean based on that PET file
                    ppt_len = len(self.set_dic['available_files_list_ppt'])
                    if pet_len > ppt_len:
                        pet_ele =\
                            self.set_dic['available_files_list_pet'].pop()
                        self.set_dic['forecast_pet_avg_file'] = pet_ele

            else:
                # else if they do not have a forecast period in the dataset
                # but they try to run the forecast option give user a message
                if self.set_dic['forecast_run']:
                    QMessageBox.information(
                        self,
                        u"No Forecast period!!",
                        "Cannot run the forecast option without a period" +
                        " of forecast PPT data",
                        QMessageBox.Ok)
                    QgsMessageLog.logMessage(
                        u"No Forecast period!!",
                        "Cannot run the forecast option without a period" +
                        " of forecast PPT data",
                        level=Qgis.Info)
                    return

            # Setup the output files for the current run
            self.set_current_output_files()
            # Set number of current periods based on the file lists
            # for current outputs
            self.set_dic['num_current_periods'] = \
                len(self.set_dic['output_file_list_wrsi_cur'])
            # Check for open current files
            self.check_for_open_files_current()

            # if forecast run then create forecast files here
            if self.set_dic['forecast_run']:
                # Get the PET avg file for the same period as the PPT
                # forecast file
                ans = QMessageBox.No
                if not self.set_dic['forecast_pet_avg_file'] == '':
                    ans = QMessageBox.question(
                        self, u'PET file already exists for this period!!',
                        "PET file already exists for this forecast period: "
                        + self.set_dic['forecast_pet_avg_file'] + "\n"
                        + "Use this file? \n"
                        + "Press Yes to use this file as the forecast "
                        + "period's PET file.\n" 
                        + "Press No to instead find the CLIM AVG file.",
                        QMessageBox.Yes, QMessageBox.No)

                if ans == QMessageBox.No:
                    err, err_msg = self.get_forecast_pet_mean_file()
                    if err:
                        QMessageBox.information(
                            self,
                            u"Could not find average file!!",
                            "Unable to find an average PET file to match " +
                            "the forecast PPT file's period: " +
                            err_msg,
                            QMessageBox.Ok)
                        QgsMessageLog.logMessage(
                            u"Could not find average file!!",
                            "Unable to find an average PET file to match " +
                            "the forecast PPT file's period: " +
                            err_msg,
                            level=Qgis.Info)
                        return

                # Setup the output files for the forecast run
                self.set_forecast_output_files()
                # if forecast, check for open forecast files
                self.check_for_open_files_forecast()

            if self.set_dic['ext_avg_run'] or self.set_dic['ext_med_run']:
                if len(self.set_dic['ext_years_list']) < 1:
                    QMessageBox.information(
                        self,
                        u"No extended years selected!!",
                        "Please select at least 1 extended year from the " +
                        "list in order to run the extended process.",
                        QMessageBox.Ok)
                    QgsMessageLog.logMessage(
                        u"No extended years selected!!",
                        "Please select at least 1 extended year from the " +
                        "list in order to run the extended process.",
                        level=Qgis.Info)
                    return
                # Running an extended run either avg or med, for this section
                # of code it will be the same for either one
                # Get output file name lists
                self.set_extended_output_files_dict()
                # Get data file lists ppt and pet for the missing periods
                self.set_extended_data_dicts()
                # Check for open files needed for extended runs
                self.check_for_open_files_extended()

        '''
        Pass everything into the worker right here to run calcs
        Run actual GeoWRSI here passing in the stacked cube a pixel at a time
        Do this by creating a worker here and passing in the data we need to
        process through the WRSI
        '''
        # put info into worker
        self.worker = \
            GeoWRSIWorker(
                self.wrksp_setup,
                self.set_dic,
                self.reg_dic,
                self.crop_dic,
                self.output_dic)

        self.ui.closeButton.setEnabled(False)
        self.ui.cancelButton.setEnabled(True)
        self.ui.okButton.setEnabled(False)
        self.ui.yearComboBox.setEnabled(False)
        self.ui.checkBoxForecast.setEnabled(False)
        self.ui.checkBoxExtendedWRSIAverage.setEnabled(False)
        self.ui.checkBoxExtendedWRSIMedian.setEnabled(False)
        self.ui.checkBoxMulptipleHistoricalYears.setEnabled(False)
        self.ui.lineEditOutputPrefix.setEnabled(False)
        self.ui.cancelButton.clicked.connect(self.worker.kill)

        message_text = "WRSI calculating..."
        iface.mainWindow().statusBar().showMessage(message_text)
        # Start process in a new thread
        iface.messageBar().clearWidgets()
        self.thread = QtCore.QThread(self)
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.thread.start()
        self.worker.finished.connect(self.wrsi_finished)
        self.worker.error.connect(self.wrsi_error)
        self.worker.progress.connect(self.ui.progressBar.setValue)
        self.is_processing = True

    def wrsi_finished(self, return_value):
        '''
        Clean up the worker and thread
        '''
        self.is_processing = False
        self.thread.quit()
        self.thread.wait()
        logged_files = []
        if self.closed:
            self.thread_worker_cleanup()
            self.close_event.accept()
            QDialog.closeEvent(self, self.close_event)
            return
        if self.worker.killed:
            self.ui.progressBar.setValue(0)
            # msg = (u"WRSI aborted by user")
            iface.messageBar().pushCritical("WRSI", return_value[1])
            QgsMessageLog.logMessage(return_value[1], level=Qgis.Critical)
            iface.mainWindow().statusBar().showMessage(return_value[1], 3000)
        elif return_value is not None:
            msg = (u"Displaying output files")
            QgsMessageLog.logMessage(msg, level=Qgis.Info)
            iface.mainWindow().statusBar().showMessage(msg, 3000)

            lyr_tree = iface.layerTreeView()
            root = lyr_tree.currentGroupNode()
            if root is None:
                root = QgsProject.instance().layerTreeRoot()

            # Only ask about the animator if there is an output we animate
            # Dont want to ask about the animator for multi-historical since
            # nothing is being displayed
            if self.set_dic['wrsi_run_type'] != config.MULTI_HISTORICAL:
                if self.output_dic["save_every_wrsi"] or\
                    self.output_dic["save_every_lgp_phenology"] or \
                        self.output_dic['save_every_soil_water_index']:
                    ans = QMessageBox.question(
                        None, 'Animator',
                        ('Would you like the animator to be initialized?'),
                        QMessageBox.Yes, QMessageBox.No)
                    animated = ans == QMessageBox.Yes
                else:
                    animated = False
            else:
                animated = False

            if self.set_dic['wrsi_run_type'] == config.MULTI_HISTORICAL:
                # There are no outputs being displayed in multi-hist, but
                # outputs are being created so we would want to log them
                logged_files = self.get_multi_historical_output_list()
            elif self.set_dic['wrsi_run_type'] == config.HISTORICAL:
                logged_files = self.display_output_files_historical()
                self.group_and_animate_historical_outputs(
                    root, logged_files, animated)
            else:  # Running the current version
                forecast_run = False
                extended_run = False
                logged_files = self.display_output_files_current()
                if self.set_dic['forecast_run']:
                    logged_files += self.display_output_files_forecast()
                    forecast_run = True
                if self.set_dic['ext_avg_run'] or self.set_dic['ext_med_run']:
                    logged_files += self.display_output_files_extended()
                    extended_run = True

                self.group_and_animate_cur_ext_outputs(
                    root, logged_files, animated, extended_run, forecast_run)

            lyr_tree.collapseAllNodes()
            lyr_tree.setCurrentLayer(None)
            self.ui.progressBar.setValue(100)
            QgsMessageLog.logMessage(return_value[1], level=Qgis.Info)
            iface.mainWindow().statusBar().showMessage(return_value[1], 3000)
            set_dic_log_string = []
            for pair in self.set_dic.items():
                if "output_file" not in pair[0]:
                    set_dic_log_string += log_util.log_string(
                        pair[0], [pair[1]])
            log_util.add_log("WRSI",
                             set_dic_log_string,
                             logged_files)
        else:
            # notify of an error
            msg = (u'Error: WRSI did not finish')
            iface.messageBar().pushCritical('WRSI', msg)
            QgsMessageLog.logMessage(msg, level=Qgis.Critical)
            iface.mainWindow().statusBar().showMessage(msg, 3000)

        util.remove_temp_files(self.output_file_path)

        self.ui.closeButton.setEnabled(True)
        self.ui.cancelButton.setEnabled(False)
        self.ui.okButton.setEnabled(True)
        self.ui.yearComboBox.setEnabled(True)
        self.ui.checkBoxForecast.setEnabled(True)
        self.ui.checkBoxExtendedWRSIAverage.setEnabled(True)
        self.ui.checkBoxExtendedWRSIMedian.setEnabled(True)
        self.ui.checkBoxMulptipleHistoricalYears.setEnabled(True)
        self.ui.lineEditOutputPrefix.setEnabled(True)
        self.thread_worker_cleanup()

        all_done = util.notify_process_completed('WRSI')
        if all_done:
            self.ui.closeButton.click()
        else:
            # Call setup gui functionality in case user decides not
            # to close the form and then runs again (this will get things back
            # to how they were before any decisions to which data to use are
            # done) and keeps errors from happening due to that
            self.setup_gui_functionality()

    def wrsi_error(self, err, ex_str):
        '''
        Function to log any errors from the object and thread.
        Arguments:
        err -- Not used
        ex_str -- Exception info
        '''
        self.is_processing = False
        QgsMessageLog.logMessage(ex_str, level=Qgis.Critical)
        QMessageBox.critical(self, 'Error!', ex_str, QMessageBox.Ok)
        iface.mainWindow().statusBar().showMessage(ex_str)

    def closeEvent(self, a_0: QtGui.QCloseEvent) -> None:
        '''
        Close event used to handle a case where user closes the form
        while the process is running.
        '''
        self.closed = True
        if self.worker:
            self.worker.kill()
        self.close_event = a_0
        if self.is_processing:
            self.close_event.ignore()
        else:
            self.close_event.accept()

    def thread_worker_cleanup(self):
        """
        Cleanup thread if it exists.
        """
        if self.thread is not None:
            self.thread.deleteLater()

    def get_output_file_list_historical(self, str_name, year):
        '''
        Function to generate the list of output names for the wrsi
        outputs should only be from region initial period to the
        region final period + 6 or 12 depending on Dekad or Pentad
        params(string) - str_name - pass in "do", "md", or
                        "mx", etc.
        params(string/int) - year - will be converted to an int for calcs and
                        cast to a string when used in filename
        returns(list) - output_list - returns the output file list
                        that was generated
        '''
        output_list = []
        start_per = self.set_dic['initial_period']
        yr = int(year)
        end_per = self.set_dic['final_period']
        if self.reg_dic['PeriodType'] == 'Dekadal':
            if start_per > end_per:
                end_per += config.DEKADS_PER_YEAR
            # Add 6 to include 6 dekads after final_period
            end_per += config.DEKAD_SHIFT
            last_per = config.DEKADS_PER_YEAR
            period = "_d"

        elif self.reg_dic['PeriodType'] == 'Pentadal':
            if start_per > end_per:
                end_per += config.PENTADS_PER_YEAR
            # Add 12 to include 12 pentads after final_period
            end_per += config.PENTAD_SHIFT
            last_per = config.PENTADS_PER_YEAR
            period = "_p"

        # Add all files to the output_directory path in the output options
        out_folder_path = os.path.join(self.output_dic['output_directory'])
        if not os.path.exists(out_folder_path):
            os.makedirs(out_folder_path)

        per = start_per
        for _ in range(start_per, end_per + 1):
            if per > last_per:
                per = 1
                yr += 1
            out_file = os.path.join(out_folder_path,
                                    self.output_dic['output_prefix']
                                    + self.set_dic['region_name']
                                    + "_w" + str(yr) + str_name
                                    + period + str(per).zfill(2)
                                    + self.ds_dic_ppt['DATASUFFIX'])
            output_list.append(out_file)
            per += 1

        return output_list

    def get_output_file_list_current(self, str_name):
        '''
        Function to generate the list of output names for the wrsi
        outputs should only be from region initial period to the
        region final period + 6 or 12 depending on Dekad or Pentad
        params(string) - str_name - pass in "do", "md", or
                        "mx", etc.
        returns(list) - output_list - returns the output file list
                        that was generated
        '''
        output_list = []
        start_per = self.set_dic['initial_period']
        yr = self.set_dic['start_year']
        end_per = self.set_dic['final_period']
        if self.reg_dic['PeriodType'] == 'Dekadal':
            if start_per > end_per:
                end_per += config.DEKADS_PER_YEAR
            # Add 6 to include 6 dekads after final_period
            end_per += config.DEKAD_SHIFT
            last_per = config.DEKADS_PER_YEAR
            period = "_d"
            shift = config.DEKAD_SHIFT

        elif self.reg_dic['PeriodType'] == 'Pentadal':
            if start_per > end_per:
                end_per += config.PENTADS_PER_YEAR
            # Add 12 to include 12 pentads after final_period
            end_per += config.PENTAD_SHIFT
            last_per = config.PENTADS_PER_YEAR
            period = "_p"
            shift = config.PENTAD_SHIFT

        # Add all files to the output_directory path in the output options
        out_folder_path = os.path.join(self.output_dic['output_directory'])
        if not os.path.exists(out_folder_path):
            os.makedirs(out_folder_path)

        num_periods = end_per - start_per + 1
        ppt_len = len(self.set_dic['available_files_list_ppt'])
        if num_periods > (ppt_len - shift):
            end_per = start_per + ppt_len - shift - 1

        per = start_per
        for _ in range(start_per, end_per + 1):
            if per > last_per:
                per = 1
                yr += 1
            out_file = os.path.join(out_folder_path,
                                    self.output_dic['output_prefix']
                                    + self.set_dic['region_name']
                                    + "_w" + str(yr) + str_name
                                    + period + str(per).zfill(2)
                                    + self.ds_dic_ppt['DATASUFFIX'])
            output_list.append(out_file)
            per += 1

        return output_list

    def get_output_file_list_forecast(self, str_name, suffix):
        '''
        Function to generate the list of output names for the wrsi
        outputs should only be one file for forecast and will be the next
        period after the current files
        params(string) - str_name - pass in "do", "md", or
                        "mx", etc.
        params(string) - suffix - _for for example
        returns(list) - output_list - returns the output file list
                        that was generated
        '''
        output_list = []
        start_per = self.set_dic['initial_period']
        yr = self.set_dic['start_year']
        end_per = self.set_dic['final_period']
        if self.reg_dic['PeriodType'] == 'Dekadal':
            if start_per > end_per:
                end_per += config.DEKADS_PER_YEAR
            # Add 6 to include 6 dekads after final_period
            end_per += config.DEKAD_SHIFT
            last_per = config.DEKADS_PER_YEAR
            period = "_d"

        elif self.reg_dic['PeriodType'] == 'Pentadal':
            if start_per > end_per:
                end_per += config.PENTADS_PER_YEAR
            # Add 12 to include 12 pentads after final_period
            end_per += config.PENTAD_SHIFT
            last_per = config.PENTADS_PER_YEAR
            period = "_p"

        # To only make output files for periods after the current files
        start_per += self.set_dic['num_current_periods']

        # Add all files to the output_directory path in the output options
        out_folder_path = os.path.join(self.output_dic['output_directory'])
        if not os.path.exists(out_folder_path):
            os.makedirs(out_folder_path)

        per = start_per
        # We just get the one file after the current files
        for _ in range(start_per, start_per + 1):
            if per > last_per:
                per -= last_per
                yr += 1
            out_file = os.path.join(out_folder_path,
                                    self.output_dic['output_prefix']
                                    + self.set_dic['region_name']
                                    + "_w" + str(yr) + str_name
                                    + period + str(per).zfill(2)
                                    + suffix
                                    + self.ds_dic_ppt['DATASUFFIX'])
            output_list.append(out_file)
            per += 1

        return output_list

    def get_crop_stages_file_list(self, year, suffix=""):
        '''
        Function to generate the file list for the crop stages file lists
        params(int) year - the year we are creating these files for
        params(string) suffix - optional suffix to pass in
        returns(list) crop_stages_list - list of all the crop stages files
        '''
        crop_stages_list = []
        stages = ["_i", "_v", "_f", "_r"]
        names = ["te", "twr", "tws", "twd"]

        for i in names:
            for j in stages:
                out_file = self.get_output_file_name(i + j, suffix, year)
                crop_stages_list.append(out_file)

        return crop_stages_list

    def get_output_file_name(self, str_name, suffix, year):
        '''
        Function to generate the output names for the wrsi outputs
        either current or eos
        params(string) - str_name - pass in "do", "md", or
                        "mx", etc.
        params(string) - suffix - eos or current, _eos or _cur
        params(string or int) - year - year that this file is for
        returns(list) - out_file - returns the output file name
                        that was generated
        '''
        yr = int(year)
        if self.set_dic['cross_year']:
            yr2 = yr + 1
            yr_str = str(yr) + "_" + str(yr2)
        else:
            yr_str = str(yr)

        # Add all files to the output_directory path in the output options
        out_folder_path = os.path.join(self.output_dic['output_directory'])
        if not os.path.exists(out_folder_path):
            os.makedirs(out_folder_path)

        out_file = os.path.join(out_folder_path,
                                self.output_dic['output_prefix']
                                + self.set_dic['region_name']
                                + "_w" + yr_str + str_name
                                + suffix + self.ds_dic_ppt['DATASUFFIX'])
        return out_file

    def check_for_open_files_historical(self):
        '''
        Function that will check to make sure none of the needed files
        are open in qgis
        '''
        for name in self.set_dic['output_file_list_wrsi']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        for name in self.set_dic['output_file_list_swi']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        for name in self.set_dic['output_file_list_lgp_phen']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        for name in self.set_dic['available_files_list_ppt']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        for name in self.set_dic['available_files_list_pet']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_wrsi_eos'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_wrsi_anom_eos'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_swi_eos'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_lgp_phen_eos'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_water_req_tot_eos'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_max_water_def_eos'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_water_def_tot_eos'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_max_water_surp_eos'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_water_surp_tot_eos'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_aet_tot_eos'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_sos'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_sos_anom'])
        self.check_loaded_map(b_name)
        for name in self.set_dic['output_file_list_crop_stages']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        if os.path.exists(self.set_dic['sos_file']):
            b_name = os.path.basename(self.set_dic['sos_file'])
            self.check_loaded_map(b_name)
        if os.path.exists(self.set_dic['lgp_file']):
            b_name = os.path.basename(self.set_dic['lgp_file'])
            self.check_loaded_map(b_name)
        if os.path.exists(self.set_dic['whc_file']):
            b_name = os.path.basename(self.set_dic['whc_file'])
            self.check_loaded_map(b_name)
        if os.path.exists(self.set_dic['mask_file']):
            b_name = os.path.basename(self.set_dic['mask_file'])
            self.check_loaded_map(b_name)

    def check_for_open_files_current(self):
        '''
        Function that will check to make sure none of the needed files
        are open in qgis
        '''
        for name in self.set_dic['output_file_list_wrsi_cur']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        for name in self.set_dic['output_file_list_swi_cur']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        for name in self.set_dic['output_file_list_lgp_phen_cur']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        for name in self.set_dic['available_files_list_ppt']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        for name in self.set_dic['available_files_list_pet']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_wrsi_cur'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_wrsi_anom_cur'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_swi_cur'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_lgp_phen_cur'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_water_req_tot_cur'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_max_water_def_cur'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_water_def_tot_cur'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_max_water_surp_cur'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_water_surp_tot_cur'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_aet_tot_cur'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_sos_cur'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_sos_anom_cur'])
        self.check_loaded_map(b_name)
        if os.path.exists(self.set_dic['sos_file']):
            b_name = os.path.basename(self.set_dic['sos_file'])
            self.check_loaded_map(b_name)
        if os.path.exists(self.set_dic['lgp_file']):
            b_name = os.path.basename(self.set_dic['lgp_file'])
            self.check_loaded_map(b_name)
        if os.path.exists(self.set_dic['whc_file']):
            b_name = os.path.basename(self.set_dic['whc_file'])
            self.check_loaded_map(b_name)
        if os.path.exists(self.set_dic['mask_file']):
            b_name = os.path.basename(self.set_dic['mask_file'])
            self.check_loaded_map(b_name)

    def check_for_open_files_forecast(self):
        '''
        Function that will check to make sure none of the needed files
        are open in qgis
        '''
        for name in self.set_dic['output_file_list_wrsi_for']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        for name in self.set_dic['output_file_list_swi_for']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        for name in self.set_dic['output_file_list_lgp_phen_for']:
            b_name = os.path.basename(name)
            self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_wrsi_for'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_wrsi_anom_for'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_swi_for'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_lgp_phen_for'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_water_req_tot_for'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_max_water_def_for'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_water_def_tot_for'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_max_water_surp_for'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(
            self.set_dic['output_file_water_surp_tot_for'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_aet_tot_for'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_sos_for'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['output_file_sos_anom_for'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['forecast_ppt_file'])
        self.check_loaded_map(b_name)
        b_name = os.path.basename(self.set_dic['forecast_pet_avg_file'])
        self.check_loaded_map(b_name)

    def check_for_open_files_extended(self):
        '''
        Function that will check to make sure none of the needed files
        are open in qgis
        '''
        self.dict_walk_check_open_files(self.set_dic['ext_output_files_dict'])
        self.dict_walk_check_open_files(self.set_dic['ext_ppt_data_dict'])
        self.dict_walk_check_open_files(self.set_dic['ext_pet_data_dict'])

    def check_for_open_files_multi_historical(self):
        '''
        Function that will check to make sure none of the needed files
        are open in qgis
        '''
        self.dict_walk_check_open_files(self.set_dic['hist_output_files_dict'])
        self.dict_walk_check_open_files(self.set_dic['hist_ppt_data_dict'])
        self.dict_walk_check_open_files(self.set_dic['hist_pet_data_dict'])
        if os.path.exists(self.set_dic['sos_file']):
            b_name = os.path.basename(self.set_dic['sos_file'])
            self.check_loaded_map(b_name)
        if os.path.exists(self.set_dic['lgp_file']):
            b_name = os.path.basename(self.set_dic['lgp_file'])
            self.check_loaded_map(b_name)
        if os.path.exists(self.set_dic['whc_file']):
            b_name = os.path.basename(self.set_dic['whc_file'])
            self.check_loaded_map(b_name)
        if os.path.exists(self.set_dic['mask_file']):
            b_name = os.path.basename(self.set_dic['mask_file'])
            self.check_loaded_map(b_name)

    def dict_walk_check_open_files(self, dictionary):
        '''
        This function is used to recursively walk through the nested
        dictionaries and check if any of the needed output or data
        files are open in qgis
        '''
        for _, v in dictionary.items():
            if type(v) == dict:   # option 1 with “type()”
                self.dict_walk_check_open_files(v)
            else:
                if type(v) == list:
                    for val in v:
                        b_name = os.path.basename(val)
                        self.check_loaded_map(b_name)
                else:
                    b_name = os.path.basename(v)
                    self.check_loaded_map(b_name)

    def check_loaded_map(self, basename):
        '''
        Check a basename to see if it's in the map panel and notify
        if it is.  Will remove the specified map layer.
        Arguments:
        basename -- Basename as a string
        '''
        root_ext = os.path.splitext(basename)
        for entry in self.open_file_info:
            if root_ext[0] == entry[1]:
                qgs_util.notify_loaded_file(self, entry[1], entry[0])

    def group_files(self, group_name, file_list, root):
        '''
        Set up a layer group to output the passed list into
        params(string) - group_name - the name of the group
        params(list) - file_list - list of layernames to go into the group
        params(layerTreeRoot) - root - The layer tree root used for grouping
        '''
        if root.findGroup(group_name) is None:
            root.addGroup(group_name)
        parent = root.findGroup(group_name)

        for layer in root.findLayers():
            if QgsLayerTree.isLayer(layer):
                lname = layer.name()
                for file in file_list:
                    if lname == os.path.splitext(os.path.basename(file))[0]:
                        clone = layer.clone()
                        parent.insertChildNode(0, clone)
                        root.removeChildNode(layer)

    def group_wrsi_output(self, group_name, file_list, root):
        '''
        Set up a layer group to output the passed list into
        params(string) - group_name - the name of the group
        params(list) - file_list - list of layernames to go into the group
        params(layerTreeRoot) - root - The layer tree root used for grouping
        '''
        if root.findGroup(group_name) is None:
            root.addGroup(group_name)
        parent = root.findGroup(group_name)

        for layer in root.findLayers():
            if QgsLayerTree.isLayer(layer):
                lname = layer.name()
                for file in file_list:
                    if lname == os.path.splitext(os.path.basename(file))[0]:
                        clone = layer.clone()
                        parent.insertChildNode(0, clone)
                        root.removeChildNode(layer)

    def get_multi_historical_output_list(self):
        '''
        This will get list of output files that are created in the multi year
        historical process
        returns(list) - logged_files - list of the files that get displayed
                        this will be used for the logging and grouping of
                        files
        '''
        logged_files = []
        for year in self.set_dic['hist_years_list']:
            if self.output_dic['eos_wrsi']:
                logged_files += [self.set_dic['hist_output_files_dict']
                                 [year]["output_file_wrsi_eos"]]
            if self.output_dic['eos_wrsi_anomaly']:
                logged_files += [self.set_dic['hist_output_files_dict']
                                 [year]['output_file_wrsi_anom_eos']]
            logged_files += [self.set_dic['hist_output_files_dict'][year]
                             ['output_file_sos']]
            logged_files += [self.set_dic['hist_output_files_dict'][year]
                             ['output_file_sos_anom']]
            if self.output_dic['eos_lgp_phenology']:
                logged_files += [self.set_dic['hist_output_files_dict']
                                 [year]['output_file_lgp_phen_eos']]
            if self.output_dic['eos_soil_water_index']:
                logged_files += [self.set_dic['hist_output_files_dict']
                                 [year]['output_file_swi_eos']]
            if self.output_dic['eos_water_req_totals']:
                logged_files += [self.set_dic['hist_output_files_dict']
                                 [year]['output_file_water_req_tot_eos']]
            if self.output_dic['eos_max_water_deficits']:
                logged_files += [self.set_dic['hist_output_files_dict']
                                 [year]['output_file_max_water_def_eos']]
            if self.output_dic['eos_water_deficit_totals']:
                logged_files += [self.set_dic['hist_output_files_dict']
                                 [year]['output_file_water_def_tot_eos']]
            if self.output_dic['eos_max_water_surplus']:
                logged_files += [self.set_dic['hist_output_files_dict']
                                 [year]['output_file_max_water_surp_eos']]
            if self.output_dic['eos_water_surplus_totals']:
                logged_files += [self.set_dic['hist_output_files_dict']
                                 [year]['output_file_water_surp_tot_eos']]
            if self.output_dic['eos_aet_totals']:
                logged_files += [self.set_dic['hist_output_files_dict']
                                 [year]['output_file_aet_tot_eos']]
            if self.output_dic['save_every_wrsi']:
                logged_files += (self.set_dic['hist_output_files_dict'][year]
                    ["output_file_list_wrsi"])
            if self.output_dic['save_every_soil_water_index']:
                logged_files += (self.set_dic['hist_output_files_dict'][year]
                    ["output_file_list_swi"])
            if self.output_dic['save_every_lgp_phenology']:
                logged_files += (self.set_dic['hist_output_files_dict'][year]
                    ["output_file_list_lgp_phen"])
            if self.output_dic['save_crop_stage_totals_eos']:
                logged_files += (self.set_dic['hist_output_files_dict'][year]
                    ['output_file_list_crop_stages'])

        return logged_files

    def display_output_files_historical(self):
        '''
        This will display the output files for historical run based on the
        selected output options in the WRSI Output Settings
        returns(list) - logged_files - list of the files that get displayed
                        this will be used for the logging and grouping of
                        files
        '''
        logged_files = []
        if self.output_dic['eos_wrsi']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_wrsi_eos'],
                self.test_wrsi_color_file)
            logged_files += [self.set_dic['output_file_wrsi_eos']]
        if self.output_dic['eos_wrsi_anomaly']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_wrsi_anom_eos'],
                self.wrsi_anom_color_file)
            logged_files += [self.set_dic['output_file_wrsi_anom_eos']]
        if self.output_dic['eos_soil_water_index']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_swi_eos'],
                self.test_swi_color_file)
            logged_files += [self.set_dic['output_file_swi_eos']]
        if self.output_dic['eos_lgp_phenology']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_lgp_phen_eos'],
                self.test_lgp_phen_color_file)
            logged_files += [self.set_dic['output_file_lgp_phen_eos']]
        if self.output_dic['eos_water_req_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_water_req_tot_eos'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['output_file_water_req_tot_eos']]
        if self.output_dic['eos_max_water_deficits']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_max_water_def_eos'],
                self.max_water_testing_color_file)
            logged_files += [self.set_dic['output_file_max_water_def_eos']]
        if self.output_dic['eos_water_deficit_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_water_def_tot_eos'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['output_file_water_def_tot_eos']]
        if self.output_dic['eos_max_water_surplus']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_max_water_surp_eos'],
                self.max_water_testing_color_file)
            logged_files += \
                [self.set_dic['output_file_max_water_surp_eos']]
        if self.output_dic['eos_water_surplus_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_water_surp_tot_eos'],
                self.test_totals_color_file)
            logged_files += \
                [self.set_dic['output_file_water_surp_tot_eos']]
        if self.output_dic['eos_aet_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_aet_tot_eos'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['output_file_aet_tot_eos']]
        qgs_util.display_raster_layer(
            self.set_dic['output_file_sos'],
            self.reg_dic['SOSColor'])
        logged_files += [self.set_dic['output_file_sos']]
        if self.reg_dic['PeriodType'] == 'Dekadal':
            qgs_util.display_raster_layer(
                self.set_dic['output_file_sos_anom'],
                self.sos_anom_dekads_color_file)
            logged_files += [self.set_dic['output_file_sos_anom']]
        if self.reg_dic['PeriodType'] == 'Pentadal':
            qgs_util.display_raster_layer(
                self.set_dic['output_file_sos_anom'],
                self.sos_anom_pentads_color_file)
            logged_files += [self.set_dic['output_file_sos_anom']]

        if self.output_dic["save_every_wrsi"]:
            for file in self.set_dic['output_file_list_wrsi']:
                qgs_util.display_raster_layer(
                    file, self.test_wrsi_color_file)
            logged_files += self.set_dic['output_file_list_wrsi']

        if self.output_dic["save_every_lgp_phenology"]:
            for file in self.set_dic['output_file_list_lgp_phen']:
                qgs_util.display_raster_layer(
                    file, self.test_lgp_phen_color_file)
            logged_files += self.set_dic['output_file_list_lgp_phen']

        if self.output_dic["save_every_soil_water_index"]:
            for file in self.set_dic['output_file_list_swi']:
                qgs_util.display_raster_layer(
                    file, self.test_swi_color_file)
            logged_files += self.set_dic['output_file_list_swi']
        if self.output_dic['save_crop_stage_totals_eos']:
            for file in self.set_dic['output_file_list_crop_stages']:
                qgs_util.display_raster_layer(
                    file, self.test_totals_color_file)
            logged_files += self.set_dic['output_file_list_crop_stages']

        return logged_files

    def display_output_files_current(self):
        '''
        This will display the output files for current run based on the
        selected output options in the WRSI Output Settings
        returns(list) - logged_files - list of the files that get displayed
                        this will be used for the logging and grouping of
                        files
        '''
        logged_files = []
        if self.output_dic['current_wrsi']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_wrsi_cur'],
                self.test_wrsi_color_file)
            logged_files += [self.set_dic['output_file_wrsi_cur']]
        if self.output_dic['current_wrsi_anomaly']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_wrsi_anom_cur'],
                self.wrsi_anom_color_file)
            logged_files += [self.set_dic['output_file_wrsi_anom_cur']]
        if self.output_dic['current_soil_water_index']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_swi_cur'],
                self.test_swi_color_file)
            logged_files += [self.set_dic['output_file_swi_cur']]
        if self.output_dic['current_lgp_phenology']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_lgp_phen_cur'],
                self.test_lgp_phen_color_file)
            logged_files += [self.set_dic['output_file_lgp_phen_cur']]
        if self.output_dic['current_water_req_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_water_req_tot_cur'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['output_file_water_req_tot_cur']]
        if self.output_dic['current_max_water_deficits']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_max_water_def_cur'],
                self.max_water_testing_color_file)
            logged_files += [self.set_dic['output_file_max_water_def_cur']]
        if self.output_dic['current_water_deficit_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_water_def_tot_cur'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['output_file_water_def_tot_cur']]
        if self.output_dic['current_max_water_surplus']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_max_water_surp_cur'],
                self.max_water_testing_color_file)
            logged_files += [self.set_dic['output_file_max_water_surp_cur']]
        if self.output_dic['current_water_surplus_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_water_surp_tot_cur'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['output_file_water_surp_tot_cur']]
        if self.output_dic['current_aet_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_aet_tot_cur'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['output_file_aet_tot_cur']]
        qgs_util.display_raster_layer(
            self.set_dic['output_file_sos_cur'],
            self.reg_dic['SOSColor'])
        logged_files += [self.set_dic['output_file_sos_cur']]
        if self.reg_dic['PeriodType'] == 'Dekadal':
            qgs_util.display_raster_layer(
                self.set_dic['output_file_sos_anom_cur'],
                self.sos_anom_dekads_color_file)
            logged_files += [self.set_dic['output_file_sos_anom_cur']]
        if self.reg_dic['PeriodType'] == 'Pentadal':
            qgs_util.display_raster_layer(
                self.set_dic['output_file_sos_anom_cur'],
                self.sos_anom_pentads_color_file)
            logged_files += [self.set_dic['output_file_sos_anom_cur']]

        if self.output_dic["save_every_wrsi"]:
            for file in self.set_dic['output_file_list_wrsi_cur']:
                qgs_util.display_raster_layer(
                    file, self.test_wrsi_color_file)
            logged_files += self.set_dic['output_file_list_wrsi_cur']

        if self.output_dic["save_every_lgp_phenology"]:
            for file in self.set_dic['output_file_list_lgp_phen_cur']:
                qgs_util.display_raster_layer(
                    file, self.test_lgp_phen_color_file)
            logged_files += self.set_dic['output_file_list_lgp_phen_cur']

        if self.output_dic["save_every_soil_water_index"]:
            for file in self.set_dic['output_file_list_swi_cur']:
                qgs_util.display_raster_layer(
                    file, self.test_swi_color_file)
            logged_files += self.set_dic['output_file_list_swi_cur']

        return logged_files

    def display_output_files_forecast(self):
        '''
        This will display the output files for forecast run based on the
        selected output options in the WRSI Output Settings
        returns(list) - logged_files - list of the files that get displayed
                        this will be used for the logging and grouping of
                        files
        '''
        logged_files = []
        if self.output_dic['current_wrsi']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_wrsi_for'],
                self.test_wrsi_color_file)
            logged_files += [self.set_dic['output_file_wrsi_for']]
        if self.output_dic['current_wrsi_anomaly']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_wrsi_anom_for'],
                self.wrsi_anom_color_file)
            logged_files += [self.set_dic['output_file_wrsi_anom_for']]
        if self.output_dic['current_soil_water_index']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_swi_for'],
                self.test_swi_color_file)
            logged_files += [self.set_dic['output_file_swi_for']]
        if self.output_dic['current_lgp_phenology']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_lgp_phen_for'],
                self.test_lgp_phen_color_file)
            logged_files += [self.set_dic['output_file_lgp_phen_for']]
        if self.output_dic['current_water_req_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_water_req_tot_for'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['output_file_water_req_tot_for']]
        if self.output_dic['current_max_water_deficits']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_max_water_def_for'],
                self.max_water_testing_color_file)
            logged_files += [self.set_dic['output_file_max_water_def_for']]
        if self.output_dic['current_water_deficit_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_water_def_tot_for'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['output_file_water_def_tot_for']]
        if self.output_dic['current_max_water_surplus']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_max_water_surp_for'],
                self.max_water_testing_color_file)
            logged_files += [self.set_dic['output_file_max_water_surp_for']]
        if self.output_dic['current_water_surplus_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_water_surp_tot_for'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['output_file_water_surp_tot_for']]
        if self.output_dic['current_aet_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['output_file_aet_tot_for'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['output_file_aet_tot_for']]
        qgs_util.display_raster_layer(
            self.set_dic['output_file_sos_for'],
            self.reg_dic['SOSColor'])
        logged_files += [self.set_dic['output_file_sos_for']]
        if self.reg_dic['PeriodType'] == 'Dekadal':
            qgs_util.display_raster_layer(
                self.set_dic['output_file_sos_anom_for'],
                self.sos_anom_dekads_color_file)
            logged_files += [self.set_dic['output_file_sos_anom_for']]
        if self.reg_dic['PeriodType'] == 'Pentadal':
            qgs_util.display_raster_layer(
                self.set_dic['output_file_sos_anom_for'],
                self.sos_anom_pentads_color_file)
            logged_files += [self.set_dic['output_file_sos_anom_for']]

        if self.output_dic["save_every_wrsi"]:
            for file in self.set_dic['output_file_list_wrsi_for']:
                qgs_util.display_raster_layer(
                    file, self.test_wrsi_color_file)
            logged_files += self.set_dic['output_file_list_wrsi_for']

        if self.output_dic["save_every_lgp_phenology"]:
            for file in self.set_dic['output_file_list_lgp_phen_for']:
                qgs_util.display_raster_layer(
                    file, self.test_lgp_phen_color_file)
            logged_files += self.set_dic['output_file_list_lgp_phen_for']

        if self.output_dic["save_every_soil_water_index"]:
            for file in self.set_dic['output_file_list_swi_for']:
                qgs_util.display_raster_layer(
                    file, self.test_swi_color_file)
            logged_files += self.set_dic['output_file_list_swi_for']

        return logged_files

    def display_output_files_extended(self):
        '''
        This will display the output files for extended run based on the
        selected output options in the WRSI Output Settings
        returns(list) - logged_files - list of the files that get displayed
                        this will be used for the logging and grouping of
                        files
        '''
        logged_files = []
        run_year = str(self.set_dic['start_year'])
        if self.output_dic['eos_wrsi']:
            qgs_util.display_raster_layer(
                self.set_dic['ext_output_files_dict'][run_year]
                            ['output_file_wrsi_ext'],
                self.test_wrsi_color_file)
            logged_files += [self.set_dic['ext_output_files_dict'][run_year]
                             ['output_file_wrsi_ext']]

        if self.output_dic['eos_wrsi_anomaly']:
            qgs_util.display_raster_layer(
                self.set_dic['ext_output_files_dict'][run_year]
                            ['output_file_wrsi_anom_ext'],
                self.wrsi_anom_color_file)
            logged_files += [self.set_dic['ext_output_files_dict'][run_year]
                             ['output_file_wrsi_anom_ext']]

        if self.output_dic['eos_soil_water_index']:
            qgs_util.display_raster_layer(
                self.set_dic['ext_output_files_dict'][run_year]
                            ['output_file_swi_ext'],
                self.test_swi_color_file)
            logged_files += [self.set_dic['ext_output_files_dict'][run_year]
                             ['output_file_swi_ext']]

        if self.output_dic['eos_lgp_phenology']:
            qgs_util.display_raster_layer(
                self.set_dic['ext_output_files_dict'][run_year]
                            ['output_file_lgp_phen_ext'],
                self.test_lgp_phen_color_file)
            logged_files += [self.set_dic['ext_output_files_dict'][run_year]
                             ['output_file_lgp_phen_ext']]

        if self.output_dic['eos_water_req_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['ext_output_files_dict'][run_year]
                            ['output_file_water_req_tot_ext'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['ext_output_files_dict'][run_year]
                             ['output_file_water_req_tot_ext']]

        if self.output_dic['eos_max_water_deficits']:
            qgs_util.display_raster_layer(
                self.set_dic['ext_output_files_dict'][run_year]
                            ['output_file_max_water_def_ext'],
                self.max_water_testing_color_file)
            logged_files += [self.set_dic['ext_output_files_dict'][run_year]
                             ['output_file_max_water_def_ext']]

        if self.output_dic['eos_water_deficit_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['ext_output_files_dict'][run_year]
                            ['output_file_water_def_tot_ext'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['ext_output_files_dict'][run_year]
                             ['output_file_water_def_tot_ext']]

        if self.output_dic['eos_max_water_surplus']:
            qgs_util.display_raster_layer(
                self.set_dic['ext_output_files_dict'][run_year]
                            ['output_file_max_water_surp_ext'],
                self.max_water_testing_color_file)
            logged_files += \
                [self.set_dic['ext_output_files_dict'][run_year]
                 ['output_file_max_water_surp_ext']]

        if self.output_dic['eos_water_surplus_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['ext_output_files_dict'][run_year]
                            ['output_file_water_surp_tot_ext'],
                self.test_totals_color_file)
            logged_files += \
                [self.set_dic['ext_output_files_dict'][run_year]
                 ['output_file_water_surp_tot_ext']]

        if self.output_dic['eos_aet_totals']:
            qgs_util.display_raster_layer(
                self.set_dic['ext_output_files_dict'][run_year]
                            ['output_file_aet_tot_ext'],
                self.test_totals_color_file)
            logged_files += [self.set_dic['ext_output_files_dict'][run_year]
                             ['output_file_aet_tot_ext']]

        qgs_util.display_raster_layer(
            self.set_dic['ext_output_files_dict'][run_year]
            ['output_file_sos_ext'],
            self.reg_dic['SOSColor'])
        logged_files += [self.set_dic['ext_output_files_dict'][run_year]
                         ['output_file_sos_ext']]

        if self.reg_dic['PeriodType'] == 'Dekadal':
            qgs_util.display_raster_layer(
                self.set_dic['ext_output_files_dict'][run_year]
                ['output_file_sos_anom_ext'],
                self.sos_anom_dekads_color_file)
            logged_files += [self.set_dic['ext_output_files_dict'][run_year]
                             ['output_file_sos_anom_ext']]
        if self.reg_dic['PeriodType'] == 'Pentadal':
            qgs_util.display_raster_layer(
                self.set_dic['ext_output_files_dict'][run_year]
                ['output_file_sos_anom_ext'],
                self.sos_anom_pentads_color_file)
            logged_files += [self.set_dic['ext_output_files_dict'][run_year]
                             ['output_file_sos_anom_ext']]

        if self.output_dic["save_every_wrsi"]:
            for file in (self.set_dic['ext_output_files_dict'][run_year]
                         ['output_file_list_wrsi']):
                qgs_util.display_raster_layer(
                    file, self.test_wrsi_color_file)
            logged_files += (self.set_dic['ext_output_files_dict'][run_year]
                ['output_file_list_wrsi'])

        if self.output_dic["save_every_soil_water_index"]:
            for file in (self.set_dic['ext_output_files_dict'][run_year]
                         ['output_file_list_swi']):
                qgs_util.display_raster_layer(
                    file, self.test_swi_color_file)
            logged_files += (self.set_dic['ext_output_files_dict'][run_year]
                ['output_file_list_swi'])

        if self.output_dic["save_every_lgp_phenology"]:
            for file in (self.set_dic['ext_output_files_dict'][run_year]
                         ['output_file_list_lgp_phen']):
                qgs_util.display_raster_layer(
                    file, self.test_lgp_phen_color_file)
            logged_files += (self.set_dic['ext_output_files_dict'][run_year]
                ['output_file_list_lgp_phen'])

        if self.output_dic['save_crop_stage_totals_eos']:
            for file in (self.set_dic['ext_output_files_dict'][run_year]
                         ['output_file_list_crop_stages_ext']):
                qgs_util.display_raster_layer(
                    file, self.test_totals_color_file)
            logged_files += (self.set_dic['ext_output_files_dict'][run_year]
                ['output_file_list_crop_stages_ext'])

        return logged_files

    def group_and_animate_historical_outputs(self, root, logged_files,
                                             animated):
        '''
        This function will be used to group and animate the outputs for a
        historical run
        params(layerTreeRoot) - root - The layer tree root used for grouping
        params(list) - logged_files - list of all files that have been output
                    and logged
        params(bool) - animated - true or false based on response from the
                    user about whether they would like to setup the animator
        '''
        # make group for outputs in layer panel
        outputs_group = (self.output_dic['output_prefix']
                         + self.set_dic['analysis_region']
                         + "_" + self.set_dic['crop_type']
                         + "_(" + self.set_dic['ppt_dataset']
                         + ")_(" + self.set_dic['pet_dataset']
                         + ")_" + str(self.set_dic['start_year']))

        # move files to output group in layer panel
        self.group_wrsi_output(outputs_group, logged_files, root)
        root = root.findGroup(outputs_group)

        yr = self.set_dic['start_year']
        if self.set_dic['cross_year']:
            yr2 = yr + 1
            yr_str = str(yr) + "_" + str(yr2)
        else:
            yr_str = str(yr)
        out_suffix = " " + \
            self.output_dic['output_prefix'] + \
            self.set_dic['analysis_region'] + yr_str
        if self.output_dic["save_every_wrsi"]:
            if animated:
                setup_animation(
                    self.set_dic['output_file_list_wrsi'],
                    self.ds_dic_ppt['PERIODICITY'])
            name = "WRSI" + out_suffix
            self.group_files(name, self.set_dic["output_file_list_wrsi"], root)

        if self.output_dic["save_every_lgp_phenology"]:
            if animated:
                setup_animation(
                    self.set_dic['output_file_list_lgp_phen'],
                    self.ds_dic_ppt['PERIODICITY'])
            name = "LGP Phenology" + out_suffix
            self.group_files(
                name, self.set_dic["output_file_list_lgp_phen"], root)

        if self.output_dic["save_every_soil_water_index"]:
            if animated:
                setup_animation(
                    self.set_dic['output_file_list_swi'],
                    self.ds_dic_ppt['PERIODICITY'])
            name = "Soil Water Index" + out_suffix
            self.group_files(name, self.set_dic["output_file_list_swi"], root)

        if self.output_dic['save_crop_stage_totals_eos']:
            name = "Crop Stage Outputs" + out_suffix
            self.group_files(
                name, self.set_dic["output_file_list_crop_stages"], root)

        if animated:
            animation_instructions = 'To use the animator set' +\
                ' the step unit to "source timestamps" right after the ' +\
                'animation range and then press the "set to full' +\
                'range" directly to the left of the steps options. ' +\
                'Now the user can navigate forward and backward through ' +\
                'the periods by using the arrows on the temporal ' +\
                'controller and switch which output is displayed by ' +\
                'toggling on that group in the layer pannel.'
            QMessageBox.information(None, 'Animator instructions',
                                    animation_instructions, QMessageBox.Ok)

            QgsMessageLog.logMessage(animation_instructions,
                                     level=Qgis.Info)

            # Uncheck all groups by searching the root for
            # groups recursively
            for group in \
                    QgsProject.instance().layerTreeRoot().findGroups(True):
                group.setItemVisibilityChecked(False)

            # Loop through all the top level layer objects and uncheck
            # everything except what we have as the outputs_group
            for top_level_layer in \
                    QgsProject.instance().layerTreeRoot().children():
                if top_level_layer.name() != outputs_group:
                    top_level_layer.setItemVisibilityChecked(False)
                else:
                    top_level_layer.setItemVisibilityChecked(True)

            # Loop over the layers in the currently selected group
            # and hide every folder but one.
            for layer in root.children():
                if self.output_dic["save_every_wrsi"]:
                    if layer.name() != "WRSI" + out_suffix:
                        layer.setItemVisibilityChecked(False)
                    else:
                        layer.setItemVisibilityChecked(True)

                elif self.output_dic["save_every_lgp_phenology"]:
                    if layer.name() != "LGP Phenology" + out_suffix:
                        layer.setItemVisibilityChecked(False)
                    else:
                        layer.setItemVisibilityChecked(True)

                elif self.output_dic["save_every_soil_water_index"]:
                    if layer.name() != "Soil Water Index" + out_suffix:
                        layer.setItemVisibilityChecked(False)
                    else:
                        layer.setItemVisibilityChecked(True)

                else:
                    layer.setItemVisibilityChecked(False)

            # Enable all the groups from the newly made group to the root
            parent_node = root
            while parent_node != QgsProject.instance().layerTreeRoot():
                parent_node.setItemVisibilityChecked(True)
                parent_node = parent_node.parent()

            set_start_end(
                self.set_dic["output_file_list_wrsi"],
                self.ds_dic_ppt['PERIODICITY'])

    def group_and_animate_cur_ext_outputs(self, root, logged_files,
                                          animated, extended_run, forecast_run):
        '''
        This function will be used to group and animate the outputs for a
        current run
        params(layerTreeRoot) - root - The layer tree root used for grouping
        params(list) - logged_files - list of all files that have been output
                    and logged
        params(bool) - animated - true or false based on response from the
                    user about whether they would like to setup the animator
        params(bool) - extended_run - if this is an extended run
        params(bool) - forecast_run - if this is an forecast run
        '''
        # make group for outputs in layer panel
        outputs_group = (self.output_dic['output_prefix']
                         + self.set_dic['analysis_region']
                         + "_" + self.set_dic['crop_type']
                         + "_(" + self.set_dic['ppt_dataset']
                         + ")_(" + self.set_dic['pet_dataset']
                         + ")_" + str(self.set_dic['start_year']))

        # move files to output group in layer panel
        self.group_wrsi_output(outputs_group, logged_files, root)
        root = root.findGroup(outputs_group)

        yr = self.set_dic['start_year']
        if self.set_dic['cross_year']:
            yr2 = yr + 1
            yr_str = str(yr) + "_" + str(yr2)
        else:
            yr_str = str(yr)
        out_suffix = " " + \
            self.output_dic['output_prefix'] + \
            self.set_dic['analysis_region'] + yr_str
        if self.output_dic["save_every_wrsi"]:
            if animated:
                setup_animation(
                    self.set_dic['output_file_list_wrsi_cur'],
                    self.ds_dic_ppt['PERIODICITY'])
                if extended_run:
                    setup_animation(
                        self.set_dic['ext_output_files_dict'][str(yr)]
                                    ['output_file_list_wrsi'],
                        self.ds_dic_ppt['PERIODICITY'])
                if forecast_run:
                    setup_animation(
                        self.set_dic['output_file_list_wrsi_for'],
                        self.ds_dic_ppt['PERIODICITY'])
            name = "WRSI" + out_suffix
            self.group_files(name,
                             self.set_dic["output_file_list_wrsi_cur"],
                             root)
            if forecast_run:
                self.group_files(name,
                                 self.set_dic["output_file_list_wrsi_for"],
                                 root)
            if extended_run:
                self.group_files(name,
                                 self.set_dic['ext_output_files_dict']
                                 [str(yr)]['output_file_list_wrsi'],
                                 root)

        if self.output_dic["save_every_lgp_phenology"]:
            if animated:
                setup_animation(
                    self.set_dic['output_file_list_lgp_phen_cur'],
                    self.ds_dic_ppt['PERIODICITY'])
                if extended_run:
                    setup_animation(
                        self.set_dic['ext_output_files_dict'][str(yr)]
                                    ['output_file_list_lgp_phen'],
                        self.ds_dic_ppt['PERIODICITY'])
                if forecast_run:
                    setup_animation(
                        self.set_dic['output_file_list_lgp_phen_for'],
                        self.ds_dic_ppt['PERIODICITY'])
            name = "LGP Phenology" + out_suffix
            self.group_files(
                name, self.set_dic["output_file_list_lgp_phen_cur"], root)
            if forecast_run:
                self.group_files(name,
                                 self.set_dic["output_file_list_lgp_phen_for"],
                                 root)
            if extended_run:
                self.group_files(name,
                                 self.set_dic['ext_output_files_dict']
                                 [str(yr)]['output_file_list_lgp_phen'],
                                 root)

        if self.output_dic["save_every_soil_water_index"]:
            if animated:
                setup_animation(
                    self.set_dic['output_file_list_swi_cur'],
                    self.ds_dic_ppt['PERIODICITY'])
                if extended_run:
                    setup_animation(
                        self.set_dic['ext_output_files_dict'][str(yr)]
                                    ['output_file_list_swi'],
                        self.ds_dic_ppt['PERIODICITY'])
                if forecast_run:
                    setup_animation(
                        self.set_dic['output_file_list_swi_for'],
                        self.ds_dic_ppt['PERIODICITY'])
            name = "Soil Water Index" + out_suffix
            self.group_files(
                name, self.set_dic["output_file_list_swi_cur"], root)
            if forecast_run:
                self.group_files(name,
                                 self.set_dic["output_file_list_swi_for"],
                                 root)
            if extended_run:
                self.group_files(name,
                                 self.set_dic['ext_output_files_dict']
                                 [str(yr)]['output_file_list_swi'],
                                 root)

        if extended_run and self.output_dic['save_crop_stage_totals_eos']:
            name = "Crop Stage Outputs" + out_suffix
            self.group_files(
                name, self.set_dic['ext_output_files_dict'][str(yr)]
                ['output_file_list_crop_stages_ext'], root)

        if animated:
            animation_instructions = 'To use the animator set' +\
                ' the step unit to "source timestamps" right after the ' +\
                'animation range and then press the "set to full' +\
                'range" directly to the left of the steps options. ' +\
                'Now the user can navigate forward and backward through ' +\
                'the periods by using the arrows on the temporal ' +\
                'controller and switch which output is displayed by ' +\
                'toggling on that group in the layer pannel.'
            QMessageBox.information(None, 'Animator instructions',
                                    animation_instructions, QMessageBox.Ok)

            QgsMessageLog.logMessage(animation_instructions,
                                     level=Qgis.Info)

            # Uncheck all groups by searching the root for
            # groups recursively
            for group in \
                    QgsProject.instance().layerTreeRoot().findGroups(True):
                group.setItemVisibilityChecked(False)

            # Loop through all the top level layer objects and uncheck
            # everything except what we have as the outputs_group
            for top_level_layer in \
                    QgsProject.instance().layerTreeRoot().children():
                if top_level_layer.name() != outputs_group:
                    top_level_layer.setItemVisibilityChecked(False)
                else:
                    top_level_layer.setItemVisibilityChecked(True)

            # Loop over the layers in the currently selected group
            # and hide every folder but one.
            for layer in root.children():
                if self.output_dic["save_every_wrsi"]:
                    if layer.name() != "WRSI" + out_suffix:
                        layer.setItemVisibilityChecked(False)
                    else:
                        layer.setItemVisibilityChecked(True)

                elif self.output_dic["save_every_lgp_phenology"]:
                    if layer.name() != "LGP Phenology" + out_suffix:
                        layer.setItemVisibilityChecked(False)
                    else:
                        layer.setItemVisibilityChecked(True)

                elif self.output_dic["save_every_soil_water_index"]:
                    if layer.name() != "Soil Water Index" + out_suffix:
                        layer.setItemVisibilityChecked(False)
                    else:
                        layer.setItemVisibilityChecked(True)

                else:
                    layer.setItemVisibilityChecked(False)

            # Enable all the groups from the newly made group to the root
            parent_node = root
            while parent_node != QgsProject.instance().layerTreeRoot():
                parent_node.setItemVisibilityChecked(True)
                parent_node = parent_node.parent()

            set_start_end(
                self.set_dic["output_file_list_wrsi_cur"],
                self.ds_dic_ppt['PERIODICITY'])

    def get_forecast_pet_mean_file(self):
        '''
        This function will be used to get the PET mean file that is needed
        for the forecast run, to replace the first file in the missing PET
        file list. It will set it in the self.set_dic['forecast_pet_avg_file']

        returns(bool) - True if error, else false.
        returns(string) - message with reason for error
        '''
        err_msg = ""
        error = True
        # Get missing PET file for same index as forecast PPT from basenames
        # list
        forecast_basename = os.path.basename(
            self.set_dic['forecast_ppt_file'])
        forecast_index = \
            self.set_dic['needed_basenames_list_ppt'].index(
                forecast_basename)
        missing_pet_file = \
            self.set_dic['needed_basenames_list_pet'][forecast_index]
        
        avg_file = self.get_clim_avg_file(missing_pet_file, self.ds_dic_pet)

        if avg_file is None:
            err_msg = f"Issue in dataset for finding clim avg file for: {missing_pet_file}"
        elif not os.path.exists(avg_file):
            err_msg - f"File does not exist {avg_file}"
        else:
            self.set_dic['forecast_pet_avg_file'] = avg_file
            error = False
        # If we get to this point no avg file was found and return True
        return error, err_msg

    def set_extended_output_files_dict(self):
        '''
        Function to fill the ext_output_files_dict with the needed filenames
        for each selected year
        '''
        # set the intermediate file lists for each ext year
        for year in self.set_dic['ext_years_list']:
            self.set_dic['ext_output_files_dict'][year] =\
                self.get_ext_files_for_year(year, "_ext")

        # set the final file list for the set_dic['start_year']
        start_yr_str = str(self.set_dic['start_year'])
        if self.set_dic['ext_avg_run']:
            self.set_dic['ext_output_files_dict'][start_yr_str] =\
                self.get_ext_files_for_year(start_yr_str, "_avg_ext")
        elif self.set_dic['ext_med_run']:
            self.set_dic['ext_output_files_dict'][start_yr_str] =\
                self.get_ext_files_for_year(start_yr_str, "_med_ext")

    def get_ext_files_for_year(self, year, suffix):
        '''
        Function to create a dictionary of intermediate or final output files
        for a given year
        params(string) - year - year to generate the filenames for
        params(string) - suffix - _ext for example
        returns(dict) - yr_dict - dictionary of the provided year's extended
            intermediate files
        '''
        yr_dict = {}
        yr_dict["output_file_list_wrsi"] =\
            self.get_output_file_list_extended("wrsi", suffix, year)
        yr_dict["output_file_list_swi"] =\
            self.get_output_file_list_extended("swi", suffix, year)
        yr_dict["output_file_list_lgp_phen"] =\
            self.get_output_file_list_extended("lgp_phen", suffix, year)
        yr_dict['output_file_wrsi_ext'] = self.get_output_file_name(
            "wrsi", suffix, year)
        yr_dict['output_file_swi_ext'] = self.get_output_file_name(
            "swi", suffix, year)
        yr_dict['output_file_lgp_phen_ext'] = self.get_output_file_name(
            "lgp_phen", suffix, year)
        yr_dict['output_file_water_req_tot_ext'] =\
            self.get_output_file_name("twr", suffix, year)
        yr_dict['output_file_max_water_def_ext'] =\
            self.get_output_file_name("mwd", suffix, year)
        yr_dict['output_file_water_def_tot_ext'] =\
            self.get_output_file_name("twd", suffix, year)
        yr_dict['output_file_max_water_surp_ext'] =\
            self.get_output_file_name("mws", suffix, year)
        yr_dict['output_file_water_surp_tot_ext'] =\
            self.get_output_file_name("tws", suffix, year)
        yr_dict['output_file_aet_tot_ext'] = self.get_output_file_name(
            "te", suffix, year)
        yr_dict['output_file_wrsi_anom_ext'] = self.get_output_file_name(
            "wrsi_anom", suffix, year)
        yr_dict['output_file_sos_ext'] = self.get_output_file_name(
            "sos", suffix, year)
        yr_dict['output_file_sos_anom_ext'] = self.get_output_file_name(
            "sos_anom", suffix, year)
        yr_dict['output_file_list_crop_stages_ext'] =\
            self.get_crop_stages_file_list(int(year), suffix)
        return yr_dict

    def get_output_file_list_extended(self, str_name, suffix, year):
        '''
        Function to generate the list of output names for the wrsi
        outputs should only be from region initial period to the
        region final period + 6 or 12 depending on Dekad or Pentad
        params(string) - str_name - pass in "do", "md", or
                        "mx", etc.
        params(string) - suffix - _ext for example
        params(string) - year - the year to generate the files for
        returns(list) - output_list - returns the output file list
                        that was generated
        '''
        output_list = []
        start_per = self.set_dic['initial_period']
        yr = int(year)
        end_per = self.set_dic['final_period']
        if self.reg_dic['PeriodType'] == 'Dekadal':
            if start_per > end_per:
                end_per += config.DEKADS_PER_YEAR
            # Add 6 to include 6 dekads after final_period
            end_per += config.DEKAD_SHIFT
            last_per = config.DEKADS_PER_YEAR
            period = "_d"

        elif self.reg_dic['PeriodType'] == 'Pentadal':
            if start_per > end_per:
                end_per += config.PENTADS_PER_YEAR
            # Add 12 to include 12 pentads after final_period
            end_per += config.PENTAD_SHIFT
            last_per = config.PENTADS_PER_YEAR
            period = "_p"

        # To only make output files for periods after the current files
        start_per += self.set_dic['num_current_periods']

        # Add all files to the output_directory path in the output options
        out_folder_path = os.path.join(self.output_dic['output_directory'])
        if not os.path.exists(out_folder_path):
            os.makedirs(out_folder_path)

        per = start_per
        for _ in range(start_per, end_per + 1):
            if per > last_per:
                per -= last_per
                yr += 1
            out_file = os.path.join(out_folder_path,
                                    self.output_dic['output_prefix']
                                    + self.set_dic['region_name']
                                    + "_w" + str(yr) + str_name
                                    + period + str(per).zfill(2)
                                    + suffix
                                    + self.ds_dic_ppt['DATASUFFIX'])
            output_list.append(out_file)
            per += 1

        return output_list

    def set_multi_historical_output_files_dict(self):
        '''
        Function to fill the hist_output_files_dict with the needed filenames
        for each selected year
        '''
        # set the intermediate file lists for each ext year
        for year in self.set_dic['hist_years_list']:
            self.set_dic['hist_output_files_dict'][year] =\
                self.get_hist_files_for_year(year)

    def get_hist_files_for_year(self, year):
        '''
        Function to create a dictionary of final historical output files
        for a given year
        params(string) - year - year to generate the filenames for
        returns(dict) - yr_dict - dictionary of the provided year's extended
            intermediate files
        '''
        yr_dict = {}
        yr_dict["output_file_list_wrsi"] =\
            self.get_output_file_list_historical("wrsi", year)
        yr_dict['output_file_list_swi'] = \
            self.get_output_file_list_historical("swi", year)
        yr_dict['output_file_list_lgp_phen'] = \
            self.get_output_file_list_historical("lgp_phen", year)
        yr_dict['output_file_wrsi_eos'] = self.get_output_file_name(
            "wrsi", "_eos", year)
        yr_dict['output_file_swi_eos'] = self.get_output_file_name(
            "swi", "_eos", year)
        yr_dict['output_file_lgp_phen_eos'] = self.get_output_file_name(
            "lgp_phen", "_eos", year)
        yr_dict['output_file_water_req_tot_eos'] =\
            self.get_output_file_name("twr", "_eos", year)
        yr_dict['output_file_max_water_def_eos'] =\
            self.get_output_file_name("mwd", "_eos", year)
        yr_dict['output_file_water_def_tot_eos'] =\
            self.get_output_file_name("twd", "_eos", year)
        yr_dict['output_file_max_water_surp_eos'] =\
            self.get_output_file_name("mws", "_eos", year)
        yr_dict['output_file_water_surp_tot_eos'] =\
            self.get_output_file_name("tws", "_eos", year)
        yr_dict['output_file_aet_tot_eos'] = self.get_output_file_name(
            "te", "_eos", year)
        yr_dict['output_file_wrsi_anom_eos'] = self.get_output_file_name(
            "wrsi_anom", "_eos", year)
        yr_dict['output_file_sos'] = self.get_output_file_name(
            "sos", "", year)
        yr_dict['output_file_sos_anom'] = self.get_output_file_name(
            "sos_anom", "", year)
        yr_dict['output_file_list_crop_stages'] =\
            self.get_crop_stages_file_list(int(year))

        return yr_dict

    def set_historical_output_files(self):
        '''
        Function to setup the needed historical output files
        '''
        # Generate output file list for historical run
        self.set_dic['output_file_list_wrsi'] = \
            self.get_output_file_list_historical("wrsi",
                                                 self.set_dic['start_year'])
        self.set_dic['output_file_list_swi'] = \
            self.get_output_file_list_historical("swi",
                                                 self.set_dic['start_year'])
        self.set_dic['output_file_list_lgp_phen'] = \
            self.get_output_file_list_historical("lgp_phen",
                                                 self.set_dic['start_year'])
        self.set_dic['output_file_wrsi_eos'] = self.get_output_file_name(
            "wrsi", "_eos", self.set_dic['start_year'])
        self.set_dic['output_file_swi_eos'] = self.get_output_file_name(
            "swi", "_eos", self.set_dic['start_year'])
        self.set_dic['output_file_lgp_phen_eos'] = self.get_output_file_name(
            "lgp_phen", "_eos", self.set_dic['start_year'])
        self.set_dic['output_file_water_req_tot_eos'] =\
            self.get_output_file_name("twr", "_eos",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_max_water_def_eos'] =\
            self.get_output_file_name("mwd", "_eos",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_water_def_tot_eos'] =\
            self.get_output_file_name("twd", "_eos",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_max_water_surp_eos'] =\
            self.get_output_file_name("mws", "_eos",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_water_surp_tot_eos'] =\
            self.get_output_file_name("tws", "_eos",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_aet_tot_eos'] = self.get_output_file_name(
            "te", "_eos", self.set_dic['start_year'])
        self.set_dic['output_file_wrsi_anom_eos'] = self.get_output_file_name(
            "wrsi_anom", "_eos", self.set_dic['start_year'])
        self.set_dic['output_file_sos'] = self.get_output_file_name(
            "sos", "", self.set_dic['start_year'])
        self.set_dic['output_file_sos_anom'] = self.get_output_file_name(
            "sos_anom", "", self.set_dic['start_year'])
        self.set_dic['output_file_list_crop_stages'] =\
            self.get_crop_stages_file_list(self.set_dic['start_year'])

    def set_current_output_files(self):
        '''
        Function to setup the needed current output files
        '''
        # Generate output file list for current run
        self.set_dic['output_file_list_wrsi_cur'] =\
            self.get_output_file_list_current("wrsi")
        self.set_dic['output_file_list_swi_cur'] =\
            self.get_output_file_list_current("swi")
        self.set_dic['output_file_list_lgp_phen_cur'] =\
            self.get_output_file_list_current("lgp_phen")
        self.set_dic['output_file_wrsi_cur'] =\
            self.get_output_file_name("wrsi", "_cur",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_swi_cur'] =\
            self.get_output_file_name("swi", "_cur",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_lgp_phen_cur'] =\
            self.get_output_file_name("lgp_phen", "_cur",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_water_req_tot_cur'] =\
            self.get_output_file_name("twr", "_cur",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_max_water_def_cur'] =\
            self.get_output_file_name("mwd", "_cur",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_water_def_tot_cur'] =\
            self.get_output_file_name("twd", "_cur",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_max_water_surp_cur'] =\
            self.get_output_file_name("mws", "_cur",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_water_surp_tot_cur'] =\
            self.get_output_file_name("tws", "_cur",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_aet_tot_cur'] =\
            self.get_output_file_name("te", "_cur",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_wrsi_anom_cur'] =\
            self.get_output_file_name("wrsi_anom", "_cur",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_sos_cur'] =\
            self.get_output_file_name("sos", "_cur",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_sos_anom_cur'] =\
            self.get_output_file_name("sos_anom", "_cur",
                                      self.set_dic['start_year'])

    def set_forecast_output_files(self):
        '''
        Function to setup the needed forecast output files
        '''
        # Generate output file list for current run
        self.set_dic['output_file_list_wrsi_for'] =\
            self.get_output_file_list_forecast("wrsi", "_for")
        self.set_dic['output_file_list_swi_for'] =\
            self.get_output_file_list_forecast("swi", "_for")
        self.set_dic['output_file_list_lgp_phen_for'] =\
            self.get_output_file_list_forecast("lgp_phen", "_for")
        self.set_dic['output_file_wrsi_for'] = \
            self.get_output_file_name("wrsi", "_for",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_swi_for'] = \
            self.get_output_file_name("swi", "_for",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_lgp_phen_for'] = \
            self.get_output_file_name("lgp_phen", "_for",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_water_req_tot_for'] =\
            self.get_output_file_name("twr", "_for",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_max_water_def_for'] =\
            self.get_output_file_name("mwd", "_for",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_water_def_tot_for'] =\
            self.get_output_file_name("twd", "_for",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_max_water_surp_for'] =\
            self.get_output_file_name("mwx", "_for",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_water_surp_tot_for'] =\
            self.get_output_file_name("twx", "_for",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_aet_tot_for'] = \
            self.get_output_file_name("te", "_for",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_wrsi_anom_for'] = \
            self.get_output_file_name("wrsi_anom", "_for",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_sos_for'] = \
            self.get_output_file_name("sos", "_for",
                                      self.set_dic['start_year'])
        self.set_dic['output_file_sos_anom_for'] = \
            self.get_output_file_name("sos_anom", "_for",
                                      self.set_dic['start_year'])

    def set_extended_data_dicts(self):
        '''
        Function to fill the ext_ppt_data_dict and ext_pet_data_dict with
        the needed filenames for each selected year
        '''
        # set the intermediate file lists for each ext year
        for year in self.set_dic['ext_years_list']:
            (self.set_dic['ext_ppt_data_dict'][year],
             self.set_dic['ext_pet_data_dict'][year]) =\
                self.get_ext_data_files_for_year(
                    year)

    def set_multi_historical_data_dicts(self):
        '''
        Function to fill the hist_ppt_data_dict and hist_pet_data_dict with
        the needed filenames for each selected year
        '''
        for year in self.set_dic['hist_years_list']:
            (self.set_dic['hist_ppt_data_dict'][year],
             self.set_dic['hist_pet_data_dict'][year]) =\
                self.get_hist_data_files_for_year(
                    year)

    def get_ext_data_files_for_year(self, year):
        '''
        Function to add the needed historical data for each missing data file
        to the available file list for each extended year and return a
        dictionary with the file list

        params(string) - year - year to generate the filenames for
        returns(list) - yr_ppt_list - list of the provided year's files
            combined with available files for current year as needed for
            extended runs
        returns(list) - yr_pet_list - list of the provided year's files
            combined with available files for current year as needed for
            extended runs
        '''
        yr_ppt_list = self.set_dic['available_files_list_ppt'][:]
        yr_pet_list = self.set_dic['available_files_list_pet'][:]

        # We already checked for no missing files when loading the
        # possible selections for extended years
        (_,
         available_files_list_ppt,
         _,
         _,
         available_files_list_pet,
         _) =\
            self.get_ppt_pet_file_lists_wrsi_historical(
            self.set_dic['initial_period'],
            self.set_dic['final_period'],
            int(year),
            self.set_dic['cross_year'])

        # PPT and PET missing number should match by this point in the code
        missing_ppt_len = len(self.set_dic['needed_basenames_list_ppt']) - \
            len(self.set_dic['available_files_list_ppt'])

        yr_ppt_list.extend(available_files_list_ppt[-missing_ppt_len:])
        yr_pet_list.extend(available_files_list_pet[-missing_ppt_len:])

        return yr_ppt_list, yr_pet_list

    def get_hist_data_files_for_year(self, year):
        '''
        Function to add the needed historical data for each missing data file
        to the available file list for each extended year and return a
        dictionary with the file list

        params(string) - year - year to generate the filenames for
        returns(list) - yr_ppt_list - list of the provided year's files
            combined with available files for current year as needed for
            extended runs
        returns(list) - yr_pet_list - list of the provided year's files
            combined with available files for current year as needed for
            extended runs
        '''
        # We already checked for no missing files when loading the
        # possible selections for extended years
        (_,
         yr_ppt_list,
         _,
         _,
         yr_pet_list,
         _) =\
            self.get_ppt_pet_file_lists_wrsi_historical(
            self.set_dic['initial_period'],
            self.set_dic['final_period'],
            int(year),
            self.set_dic['cross_year'])

        return yr_ppt_list, yr_pet_list
