'''
/***************************************************************************
Name	   : basiics_controller.py
Description: BASIICS (Blend, validate and interpolate included)for FEWS plugin
copyright  : (C) 2020 - 2023 by FEWS
email      : minxuansun@contractor.usgs.gov
Created    : 01/22/2020
Modified   : 04/10/2020 cholen - Updated to not clear map panel and fix
                                   problem with notify of loaded file.
             04/17/2020 cholen - Fix csv col header read index, fixed bug
                                   when dataset was not one of the three
                                   defined 'extents'
             05/12/2020 cholen - Add checks for open outputs
             06/08/2020 cholen - Adjust form 2 label for Interpolate
             06/15/2020 cholen - Adjust check for getSaveFileName result
             06/18/2020 cholen - Replaced sample_dates with a bat_dic element
                                   added check for outputs in Map panel
             06/30/2020 cholen - Remove option for setting new dataset
                                   to default, fix form2 load for correct
                                   dataset and region from scratch and file
             07/09/2020 cholen - Update color files
             07/14/2020 cholen - Adjust error
             08/28/2020 cholen - Notify on completion, general cleanup to
                                   match patterns in other tools
             08/29/2020 cholen - Add functions for common finish tasks
             10/08/2020 cholen - Fix os sep in browse
             10/13/2020 cholen - Change to display_map_layer function
             10/23/2020 cholen - Make advanced options and stats out always on,
                                 process ds extents
             11/04/2020 cholen - Adjust missing file check for new util func
             12/03/2020 cholen - Handle OSError
             03/08/2021 cholen - Check mask vs region adjusted
             03/31/2021 cholen - Add DATASCALEFACTOR to create_new_dataset
             06/04/2021 cholen - Add message box on failure to complete
             09/29/2021 cholen - Fix delimiter not being set correctly
             01/18/2022 cholen - New gdal utils, refactor.
             04/25/2022 cholen - Add tif and new date format support
             06/23/2022 cholen - Fix path for region mask and map files
             09/17/2025 dhackman - Updates to BASIICS blending tool: masking,
                                stats.csv info updates to have final basiics
                                value now, extracting point stats bug fix,
                                max eff dist changed to BED, path fixes for
                                creating new dataset from Basiics, write_file
                                function fix.

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

/***************************************************************************
 *                                                                         *
 *   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 csv
import datetime
import os
import subprocess
import sys

from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QMessageBox, QDialog, QFileDialog

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

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 import fews_tools_config as config
from fews_tools.utilities import geoclim_gdal_utilities as g_util
from fews_tools.utilities import geoclim_utilities as util
from fews_tools.utilities import geoclim_qgs_utilities as qgs_util
from fews_tools.utilities import logging_utilities as log_util
from fews_tools.utilities.help_utilities import view_manual
from fews_tools.controllers.new_dataset_controller import NewDatasetController

from fews_tools.forms.Ui_BatchAssistant1 import Ui_BatchAssistant1
from fews_tools.forms.Ui_BatchAssistant2 import Ui_BatchAssistant2
from fews_tools.forms.Ui_BatchAssistant3 import Ui_BatchAssistant3
from fews_tools.forms.Ui_BatchAssistant4 import Ui_BatchAssistant4

from fews_tools.workers.blender_worker import BlenderWorker
from fews_tools.workers.validate_rfe_worker import ValidateRFEWorker
from fews_tools.workers.interpolate_stations_worker import InterpolateStationsWorker
from fews_tools.workers.under_catch_worker import UnderCatchWorker


class BasiicsController(QDialog):
    '''
    Class to set up BASIICS parameter entry.
    '''

    def __init__(self):
        QDialog.__init__(self)
        self.ui1 = Ui_BatchAssistant1()
        self.ui1.setupUi(self)
        self.wrksp_setup = WorkspaceSetupModel()

        # Data structure for BASIICS GUI parameters.
        self.bat_dic = {
            'analysis_type': 1,  # 1 = Blend, 2 = Vaidate RF, 3 = Stations only
            'station_filename': '',
            'delimiter': ',',
            'stn_missing_val': -9999,
            'csv_long_col': 0,
            'csv_lat_col': 0,
            'csv_year_col': 0,
            'csv_hdr_row': 1,
            'csv_stn_id_col': 0,
            'csv_beg_period_col': 0,
            'csv_end_period_col': 0,
            'input_file_list': [],
            'input_prefix': 'temp_prefix',
            'start_period': '',
            'start_year': '',
            'end_period': '',
            'end_year': '',
            'dates_list': None,
            'good_files_list': [],
            'wt_power': 2.0,
            'min_stations': 0,
            'max_stations': 10,
            'search_radius': 500,
            'fuzz_factor': 1,
            'back_eq_distance': 100.0,
            'lr_value': 1.0,
            'max_ratio': 3.0,
            'interp_type': 'Simple',
            'co_interp_type': 'ratio_and_anom',  # always
            'curr_output_path': self.wrksp_setup.get_output_path(),
            'output_prefix': 'basiics_v2p0chirps',
            'stats_out_file': '',
            'output_stats_flag': True,
            'adv_options_flag': True,
            'ds_avg_flag': False,
            'geotransform': None,
            'selected_ds': '',  # for reading/writing text files
            'selected_reg': '',  # for reading/writing text files
            'ds_dic': {},
            'reg_dic': {},
            'stn_file_col_list': None,
            'stn_file_year_list': None,
            'dst_filename_list': None,
            # Things for 4th step options
            'periodicity': None,
            'corrected_station_filename': '',
            'update_existing_corrected_file': False,
            'delimiter2': ',',
            'stn_missing_val2': -9999,
            'csv_long_col2': 0,
            'csv_lat_col2': 0,
            'csv_year_col2': 0,
            'csv_hdr_row2': 1,
            'csv_stn_id_col2': 0,
            'csv_beg_period_col2': 0,
            'csv_end_period_col2': 0,
            'stn_file_col_list2': None,
            'stn_file_year_list2': None,
            }

        self.ui1.blendRadioButton.setChecked(True)
        self.ui1.nextButton.clicked.connect(self.step2)
        self.ui1.helpButton.clicked.connect(self.view_manual)
        self.ui1.loadFileButton.clicked.connect(self.load_param_file)

    def view_manual(self):
        '''
        Function to browse to output directory and set line edit control.
        '''
        clim_manual_webpage_section = "chapter-9-background-assisted-station-interpolatio"
        view_manual(clim_manual_webpage_section)

    def load_param_file(self):
        '''
        Method to load params from a file.
        '''
        file_name = QFileDialog.getOpenFileName(
            self,
            u'Select Filename',
            self.bat_dic['curr_output_path'],
            '*.txt')
        if self.wrksp_setup.fix_os_sep_in_path(file_name[0]):
            # read the information from file
            with open(file_name[0], 'r') as read_obj:
                for row in read_obj:
                    temp_string = row.strip()
                    if 'Analysis type' in temp_string:
                        if 'Under Catch Correct Stations' in temp_string:
                            self.ui1.underCatchCorrectRadioButton.setChecked(True)
                            self.bat_dic['analysis_type'] = 4
                        elif 'Interpolate Stations' in temp_string:
                            self.ui1.interpolateRadioButton.setChecked(True)
                            self.bat_dic['analysis_type'] = 3
                        elif 'Validate RF' in temp_string:
                            self.ui1.validateRadioButton.setChecked(True)
                            self.bat_dic['analysis_type'] = 2
                        else:  # 'Blend' in temp_string:
                            self.ui1.blendRadioButton.setChecked(True)
                            self.bat_dic['analysis_type'] = 1
                    elif 'Station File:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Station File:  ', '')
                        self.bat_dic['station_filename'] = temp_string2
                    elif 'Delimiter:' in temp_string:
                        temp_string2 = temp_string.replace('Delimiter:  ', '')
                        self.bat_dic['delimiter'] = temp_string2
                    elif 'Station Missing Value:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Station Missing Value:  ', '')
                        self.bat_dic['stn_missing_val'] = int(temp_string2)
                    elif 'Lat Column:' in temp_string:
                        temp_string2 = temp_string.replace('Lat Column:  ', '')
                        self.bat_dic['csv_lat_col'] = int(temp_string2)
                    elif 'Long Column:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Long Column:  ', '')
                        self.bat_dic['csv_long_col'] = int(temp_string2)
                    elif 'Year Column:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Year Column:  ', '')
                        self.bat_dic['csv_year_col'] = int(temp_string2)
                    elif 'Header Rows:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Header Rows:  ', '')
                        self.bat_dic['csv_hdr_row'] = int(temp_string2)
                    elif 'Station ID Column:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Station ID Column:  ', '')
                        self.bat_dic['csv_stn_id_col'] = int(temp_string2)
                    elif 'Beginning Period Column:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Beginning Period Column:  ',
                                                '')
                        self.bat_dic['csv_beg_period_col'] =\
                            int(temp_string2)
                    elif 'Ending Period Column:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Ending Period Column:  ', '')
                        self.bat_dic['csv_end_period_col'] =\
                            int(temp_string2)
                    elif 'Start Period:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Start Period:  ', '')
                        self.bat_dic['start_period'] = temp_string2
                    elif 'Start Year:' in temp_string:
                        temp_string2 = temp_string.replace('Start Year:  ', '')
                        self.bat_dic['start_year'] = temp_string2
                    elif 'End Period:' in temp_string:
                        temp_string2 = temp_string.replace('End Period:  ', '')
                        self.bat_dic['end_period'] = temp_string2
                    elif 'End Year:' in temp_string:
                        temp_string2 = temp_string.replace('End Year:  ', '')
                        self.bat_dic['end_year'] = temp_string2
                    elif 'Weight Power:' in temp_string:
                        temp_string2 = temp_string.replace('Weight Power:  ',
                                                           '')
                        if "None" in temp_string2:
                            self.bat_dic['wt_power'] = None
                        else:
                            self.bat_dic['wt_power'] = float(temp_string2)
                    elif 'Minimum Station Count:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Minimum Station Count:  ', '')
                        if "None" in temp_string2:
                            self.bat_dic['min_stations'] = None
                        else:
                            self.bat_dic['min_stations'] = int(temp_string2)
                    elif 'Maximum Station Count:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Maximum Station Count:  ', '')
                        if "None" in temp_string2:
                            self.bat_dic['max_stations'] = None
                        else:
                            self.bat_dic['max_stations'] = int(temp_string2)
                    elif 'Search Radius:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Search Radius:  ', '')
                        if "None" in temp_string2:
                            self.bat_dic['search_radius'] = None
                        else:
                            self.bat_dic['search_radius'] = int(temp_string2)
                    elif 'Fuzz Factor:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Fuzz Factor:  ', '')
                        if "None" in temp_string2:
                            self.bat_dic['fuzz_factor'] = None
                        else:
                            self.bat_dic['fuzz_factor'] = int(temp_string2)
                    elif 'Background Equivalent Distance:' in temp_string:
                        temp_string2 =\
                            temp_string.replace(
                                'Background Equivalent Distance:  ', '')
                        if "None" in temp_string2:
                            self.bat_dic['back_eq_distance'] = None
                        else:
                            self.bat_dic['back_eq_distance'] =\
                                float(temp_string2)
                    elif 'Long Range Value:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Long Range Value:  ', '')
                        if "None" in temp_string2:
                            self.bat_dic['lr_value'] = None
                        else:
                            self.bat_dic['lr_value'] = float(temp_string2)
                    elif 'Maximum Ratio:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Maximum Ratio:  ', '')
                        if "None" in temp_string2:
                            self.bat_dic['max_ratio'] = None
                        else:
                            self.bat_dic['max_ratio'] = float(temp_string2)
                    elif 'Interpolation Type:  ' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Interpolation Type:  ', '')
                        self.bat_dic['interp_type'] = temp_string2
                    elif 'Cointerpolation Type:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Cointerpolation Type:  ', '')
                        self.bat_dic['co_interp_type'] = temp_string2
                    elif 'Statistics Output:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Statistics Output:  ', '')
                        self.bat_dic['stats_out_file'] = temp_string2
                    elif 'Output Path:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Output Path:  ', '')
                        self.bat_dic['curr_output_path'] = temp_string2
                    elif 'Output Prefix:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Output Prefix:  ', '')
                        self.bat_dic['output_prefix'] = temp_string2
                    elif 'Output Stats Flag:' in temp_string:  # for now, always on
                        self.bat_dic['output_stats_flag'] = True
#                        temp_string2 =\
#                            temp_string.replace('Output Stats Flag:  ', '')
#                        if temp_string2 == 'True':
#                            self.bat_dic['output_stats_flag'] = True
#                        else:
#                            self.bat_dic['output_stats_flag'] = False
                    elif 'Advanced Options Flag:' in temp_string:  # for now, always on
                        self.bat_dic['adv_options_flag'] = True
#                        temp_string2 =\
#                            temp_string.replace('Advanced Options Flag:  ', '')
#                        if temp_string2 == 'True':
#                            self.bat_dic['adv_options_flag'] = True
#                        else:
#                            self.bat_dic['adv_options_flag'] = False
                    elif 'Selected Dataset:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Selected Dataset:  ', '')
                        self.bat_dic['selected_ds'] = temp_string2
                    elif 'Input Prefix:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Input Prefix:  ', '')
                        self.bat_dic['input_prefix'] = temp_string2
                    elif 'Dataset Average Flag:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Dataset Average Flag:  ', '')
                        if temp_string2 == 'True':
                            self.bat_dic['ds_avg_flag'] = True
                        else:
                            self.bat_dic['ds_avg_flag'] = False
                    elif 'Selected Region:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Selected Region:  ', '')
                        self.bat_dic['selected_reg'] = temp_string2
                    elif 'Periodicity:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Periodicity:  ', '')
                        self.bat_dic['periodicity'] = temp_string2
                    elif 'Corrected Station File Output:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Corrected Station File Output:  ', '')
                        self.bat_dic['corrected_station_filename'] = temp_string2
                    elif 'Update Station File Output:' in temp_string:  # for now, always on
                        temp_string2 =\
                            temp_string.replace('Update Station File Output:  ', '')
                        if temp_string2 == 'True':
                            self.bat_dic['update_existing_corrected_file'] = True
                        else:
                            self.bat_dic['update_existing_corrected_file'] = False
                    elif 'Delimiter2:' in temp_string:
                        temp_string2 = temp_string.replace('Delimiter2:  ', '')
                        self.bat_dic['delimiter2'] = temp_string2
                    elif 'Station Missing Value2:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Station Missing Value2:  ', '')
                        self.bat_dic['stn_missing_val2'] = int(temp_string2)
                    elif 'Lat Column2:' in temp_string:
                        temp_string2 = temp_string.replace('Lat Column2:  ', '')
                        self.bat_dic['csv_lat_col2'] = int(temp_string2)
                    elif 'Long Column2:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Long Column2:  ', '')
                        self.bat_dic['csv_long_col2'] = int(temp_string2)
                    elif 'Year Column2:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Year Column2:  ', '')
                        self.bat_dic['csv_year_col2'] = int(temp_string2)
                    elif 'Header Rows2:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Header Rows2:  ', '')
                        self.bat_dic['csv_hdr_row2'] = int(temp_string2)
                    elif 'Station ID Column2:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Station ID Column2:  ', '')
                        self.bat_dic['csv_stn_id_col2'] = int(temp_string2)
                    elif 'Beginning Period Column2:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Beginning Period Column2:  ',
                                                '')
                        self.bat_dic['csv_beg_period_col2'] =\
                            int(temp_string2)
                    elif 'Ending Period Column2:' in temp_string:
                        temp_string2 =\
                            temp_string.replace('Ending Period Column2:  ', '')
                        self.bat_dic['csv_end_period_col2'] =\
                            int(temp_string2)
            self.step2()

    def step2(self):
        '''
        Method to setup the second form for BASIICS parameter entry.
        '''
        if self.ui1.blendRadioButton.isChecked():
            self.bat_dic['analysis_type'] = 1
        elif self.ui1.validateRadioButton.isChecked():
            self.bat_dic['analysis_type'] = 2
        elif self.ui1.interpolateRadioButton.isChecked():
            self.bat_dic['analysis_type'] = 3
        elif self.ui1.underCatchCorrectRadioButton.isChecked():
            self.bat_dic['analysis_type'] = 4

        if self.bat_dic['analysis_type'] != 4:
            dlg_2 = BasiicsController2(
                self.bat_dic, self.wrksp_setup, None, None)
            self.close()
            dlg_2.exec_()
        else:
            dlg_2 = BasiicsController4(
                self.bat_dic, self.wrksp_setup)
            self.close()
            dlg_2.exec_()


class BasiicsController2(QDialog):
    '''
    Class to for BASIICS parameter entry - Step 2.
    '''

    STATS_CSV = 'stats.csv'
    DELIMITER_DIC = {'Comma (,)': ",",
                     'Semi-colon (;)': ";",
                     'Period (.)': ".",
                     'Space ( )': " ",
                     'Tab': "\t"}

    def __init__(self, batch_dic, wrksp_setup, ds_dic, reg_dic):
        '''
        Initialize class with params
        Args:
        batch_dic(dic) - GUI parameters for BASIICS.
        wrksp_setup(dic) - Database parameters.
        ds_dic
        eg_dic
        '''
        QDialog.__init__(self)
        self.ui = Ui_BatchAssistant2()
        self.ui.setupUi(self)

        self.bat_dic = batch_dic
        self.wrksp_setup = wrksp_setup
        self.ds_info = DatasetsModel()
        self.reg_info = RegionModel()
        ds_names = self.ds_info.get_all_dataset_names()
        default_ds = self.ds_info.query_default_dataset_name()
        reg_names = self.reg_info.get_all_region_names()
        default_reg = self.reg_info.query_default_region_name()
        if ds_dic is None or reg_dic is None:
            self.ds_info.query_named_dataset(default_ds)
            self.ds_dic = self.ds_info.get_ds_dictionary()
            self.reg_info.query_named_region(default_reg)
            self.reg_dic = self.reg_info.get_region_dictionary()
        else:
            self.ds_dic = ds_dic
            self.reg_dic = reg_dic
        self.col_offset = 0
        self.row_offset = 0
        self.region_no_space = ''
        # fill these widgets before connecting anything
        util.fill_misc_widget(self.ui.delimComboBox, self.DELIMITER_DIC.keys())
        # get the key that goes with the self.bat_dic[delimiter]
        key_l = [key for key, val in self.DELIMITER_DIC.items()
                 if val == self.bat_dic['delimiter']][0]
        self.ui.delimComboBox.setCurrentText(key_l)
        util.fill_misc_widget(self.ui.interpComboBox,
                              ['Simple', 'Ordinary'])
        self.input_files_list = []
        util.fill_dataset_combo(self.ui.datasetComboBox)
        self.ui.datasetComboBox.currentIndexChanged.connect(
            self.dataset_selection_changed)
        self.ui.regionComboBox.currentIndexChanged.connect(
            self.region_selection_changed)
        self.ui.dsAvgCheckBox.stateChanged.connect(self.toggle_data_avg_prefix)

        self.ui.previousButton.clicked.connect(self.step1)
        self.ui.nextButton.clicked.connect(self.step3)
#        self.ui.advOptionsCheckBox.stateChanged.connect(
#            self.toggle_advanced_options)
        self.ui.stationBrowseButton.clicked.connect(self.browse_station_file)
        self.ui.outputBrowseButton.clicked.connect(self.browse_output_folder)
        self.ui.statsBrowseButton.clicked.connect(self.browse_stats_file)
#        self.ui.statsOutCheckBox.stateChanged.connect(self.write_stats_out)
        self.ui.interpComboBox.currentIndexChanged.connect(
            self.interpolation_selection_changed)
        # Hide these until they get implemented
        self.ui.showButton.setVisible(False)
        self.ui.newPixelSizeCheckBox.setVisible(False)
        self.ui.stnLocSepFileCheckBox.setVisible(False)
        self.ui.showButton.clicked.connect(self.not_implemented)
        self.ui.newPixelSizeCheckBox.stateChanged.connect(
            self.not_implemented)
        self.ui.stnLocSepFileCheckBox.stateChanged.connect(
            self.not_implemented)
        self.ui.stationFileLineEdit.textChanged.connect(
            self.update_station_file_path)
        self.ui.prefixLineEdit.textChanged.connect(self.update_output_prefix)
        self.ui.delimComboBox.currentIndexChanged.connect(
            self.delimiter_selection_changed)
        self.ui.hdrRowSpinBox.valueChanged.connect(
            self.header_row_count_selection_changed)
        # verify any existing selected dataset exists
        if self.bat_dic['selected_ds'] and\
                self.bat_dic['selected_ds'] not in ds_names:
            self.bat_dic['selected_ds'] = ''

        # verify any existing selected region exists
        if self.bat_dic['selected_reg'] and\
                self.bat_dic['selected_reg'] not in reg_names:
            self.bat_dic['selected_reg'] = ''

        # set per batch dictionary if it's not default
        if self.bat_dic['selected_ds'] != '' and\
                self.bat_dic['selected_ds'] != default_ds:
            sel_ds_idx = self.ui.datasetComboBox.findText(
                self.bat_dic['selected_ds'])
            if sel_ds_idx == -1:
                sel_ds_idx = 0
            self.ui.datasetComboBox.setCurrentIndex(sel_ds_idx)
        self.dataset_selection_changed()
        if not self.input_files_list:
            # fill in region control, set per batch dictionary
            if self.ds_dic['DATAEXTENT'] == config.DATA_EXTENTS[0]:
                param_file = config.DEFAULT_AFRICA_MASK
            elif self.ds_dic['DATAEXTENT'] == config.DATA_EXTENTS[1]:
                param_file = config.DEFAULT_CAMCAR_MASK
            else:  # if self.ds_dic['DATAEXTENT'] == config.DATA_EXTENTS[2]:
                param_file = config.DEFAULT_GLOBAL_MASK
            param_file = os.path.join(self.wrksp_setup.get_static_data_path(),
                                      param_file)
            if not os.path.exists(param_file):
                QMessageBox.warning(
                    self,
                    'The mask file isn\'t available.',
                    'The mask file - ' + param_file + ' isn\'t available',
                    QMessageBox.Ok)
                return
        else:
            param_file = self.input_files_list[0]
        util.fill_region_combo_for_dataset_selection(
            self.ui.regionComboBox, param_file)

        if self.bat_dic['selected_reg'] != '' and\
                self.bat_dic['selected_reg'] != default_reg:
            sel_reg_idx = self.ui.regionComboBox.findText(
                self.bat_dic['selected_reg'])
            if sel_reg_idx == -1:
                sel_reg_idx = 0
            self.ui.regionComboBox.setCurrentIndex(sel_reg_idx)
        self.region_selection_changed()

        if self.bat_dic['ds_avg_flag']:
            self.ui.dsAvgCheckBox.setChecked(True)
        else:
            self.ui.dsAvgCheckBox.setChecked(False)

        self.ui.advOptGroupBox.setTitle('Interpolation Parameters')
        # hide adv controls for Validate RFE or if flag is False
#        if self.bat_dic['analysis_type'] == 2 or not\
#                self.bat_dic['adv_options_flag']:
        if self.bat_dic['analysis_type'] == 2:
            self.ui.advOptGroupBox.hide()

        if self.bat_dic['analysis_type'] == 3:
            self.bat_dic['min_stations'] = 3
            self.ui.dsGroupBox.setTitle('Example grid dataset')

        # check if station file info exists
        if self.bat_dic['station_filename'] != '':
            self.ui.stationFileLineEdit.setText(
                self.bat_dic['station_filename'])

            # make sure we have the correct selections
            self.ui.stnMissValSpinBox.setValue(
                self.bat_dic['stn_missing_val'])
            self.ui.stnIDColComboBox.setCurrentIndex(
                self.bat_dic['csv_stn_id_col'])
            self.ui.latColComboBox.setCurrentIndex(
                self.bat_dic['csv_lat_col'])
            self.ui.longColComboBox.setCurrentIndex(
                self.bat_dic['csv_long_col'])
            self.ui.yrColComboBox.setCurrentIndex(
                self.bat_dic['csv_year_col'])
            self.ui.fstIntvlColComboBox.setCurrentIndex(
                self.bat_dic['csv_beg_period_col'])
            self.ui.lastIntvlColComboBox.setCurrentIndex(
                self.bat_dic['csv_end_period_col'])
            self.ui.hdrRowSpinBox.setValue(self.bat_dic['csv_hdr_row'])

        # fill in other controls from batch_dic
        self.ui.outputFolderLineEdit.setText(self.bat_dic['curr_output_path'])
        self.ui.prefixLineEdit.setText(self.bat_dic['output_prefix'])

        # put in defaults for adv parameters
        self.ui.wtSpinBox.setValue(self.bat_dic['wt_power'])
        self.ui.minStnsSpinBox.setValue(self.bat_dic['min_stations'])
        self.ui.maxStnsSpinBox.setValue(self.bat_dic['max_stations'])
        self.ui.srchRadSpinBox.setValue(self.bat_dic['search_radius'])
        self.ui.fuzzSpinBox.setValue(self.bat_dic['fuzz_factor'])
        self.ui.maxEffDistSpinBox.setValue(self.bat_dic['back_eq_distance'])
        self.ui.lrValSpinBox.setValue(self.bat_dic['lr_value'])
        self.ui.maxRatioSpinBox.setValue(self.bat_dic['max_ratio'])

        sel_int_type_idx = self.ui.interpComboBox.findText(
            self.bat_dic['interp_type'])
        if sel_int_type_idx == -1:
            sel_int_type_idx = 0
        self.ui.interpComboBox.setCurrentIndex(sel_int_type_idx)
        self.interpolation_selection_changed()
        self.ui.advOptionsCheckBox.setChecked(True)  # true always for now
        self.ui.advOptionsCheckBox.setVisible(False)

#        if self.bat_dic['adv_options_flag']:
#            self.ui.advOptionsCheckBox.setChecked(True)
#            self.ui.extentsGroupBox.setVisible(True)
#        else:
#            self.ui.advOptionsCheckBox.setChecked(False)
#            self.ui.extentsGroupBox.setVisible(False)

        self.bat_dic['stats_out_file'] =\
            os.path.join(self.bat_dic['curr_output_path'], self.STATS_CSV)
        self.ui.statsOutputLineEdit.setText(self.bat_dic['stats_out_file'])
        self.ui.statsOutCheckBox.setChecked(True)
        self.ui.statsOutCheckBox.setVisible(False)
        self.ui.statsBrowseButton.setVisible(True)
        self.ui.statsOutputLineEdit.setVisible(True)
        self.ui.lblStatsFile.setVisible(True)
#        if self.bat_dic['output_stats_flag']:
#            self.ui.statsOutCheckBox.setChecked(True)
#        else:
#            self.ui.statsOutCheckBox.setChecked(False)

        self.ui.ulxSpinBox.setEnabled(False)
        self.ui.ulySpinBox.setEnabled(False)
        self.ui.lrxSpinBox.setEnabled(False)
        self.ui.lrySpinBox.setEnabled(False)

#        if self.bat_dic['output_stats_flag']:
#            self.ui.statsBrowseButton.setVisible(True)
#            self.ui.statsOutputLineEdit.setVisible(True)
#            self.ui.lblStatsFile.setVisible(True)
#        else:
#            self.ui.statsBrowseButton.setVisible(False)
#            self.ui.statsOutputLineEdit.setVisible(False)
#            self.ui.lblStatsFile.setVisible(False)

        # set or hide controls not used for validate RFE
        if self.bat_dic['analysis_type'] == 2:
            self.ui.advOptionsCheckBox.setChecked(False)
            self.ui.advOptionsCheckBox.setVisible(False)
            self.ui.regionComboBox.setVisible(False)
            self.ui.ulxSpinBox.setVisible(False)
            self.ui.ulySpinBox.setVisible(False)
            self.ui.lrxSpinBox.setVisible(False)
            self.ui.lrySpinBox.setVisible(False)
            self.ui.stnLocSepFileCheckBox.setVisible(False)
            self.ui.newPixelSizeCheckBox.setVisible(False)
            # set output stats to true and hide control
            self.ui.statsOutCheckBox.setChecked(True)
            self.ui.statsOutCheckBox.setVisible(False)
            self.ui.lblStatsFile.setVisible(True)
            self.ui.statsOutputLineEdit.setVisible(True)
            self.ui.statsBrowseButton.setVisible(True)
            self.ui.extentsGroupBox.setVisible(False)
            self.ui.showButton.setVisible(False)
        # ds averages not working - hide for now
        self.ui.dsAvgCheckBox.setChecked(False)
        self.ui.dsAvgCheckBox.setVisible(False)
        # hide controls not used for interpolate stations
        if self.bat_dic['analysis_type'] == 3:
            self.ui.maxRatioSpinBox.setVisible(False)
            self.ui.lblMaxRatio.setVisible(False)
            self.ui.dsAvgCheckBox.setChecked(False)
            self.ui.dsAvgCheckBox.setVisible(False)

    def browse_output_folder(self):
        '''
        Method to browse to and select an output location.
        '''
        sel_output_path =\
            QFileDialog.getExistingDirectory(
                self, u'Output folder',
                self.bat_dic['curr_output_path'])
        if sel_output_path:
            self.ui.outputFolderLineEdit.setText(
                self.wrksp_setup.fix_os_sep_in_path(sel_output_path))
            self.bat_dic['curr_output_path'] = sel_output_path
            filename = self.wrksp_setup.fix_os_sep_in_path(
                os.path.join(self.bat_dic['curr_output_path'], self.STATS_CSV))
            self.ui.statsOutputLineEdit.setText(filename)

    def browse_station_file(self):
        '''
        Method to browse to and open a station file.
        '''
        file_name = QFileDialog.getOpenFileName(
            self,
            u'Select station  file',
            self.wrksp_setup.get_workspace(),
            '*.csv')
        if file_name[0]:
            self.ui.stationFileLineEdit.setText(
                self.wrksp_setup.fix_os_sep_in_path(file_name[0]))

    def browse_stats_file(self):
        '''
        Method for browsing to and select a statitical output file location.
        '''
        filename =\
            QFileDialog.getSaveFileName(self,
                                        u'Statistical output location',
                                        self.bat_dic['curr_output_path'],
                                        '*.csv')
        if filename[0]:
            self.ui.statsOutputLineEdit.setText(
                self.wrksp_setup.fix_os_sep_in_path(filename[0]))

    def create_new_dataset(self):
        '''
        Function to create a new dataset from the process.
        Return
        string -- The new dataset name
        '''
        new_dataset_dlg = NewDatasetController()
        new_dataset_dlg.exec_()
        new_ds_name, new_ds_type, new_ds_extent =\
            new_dataset_dlg.get_new_params()

        if new_ds_name:
            # build values list from batch_dic and responses
            qry_vals = [new_ds_name, new_ds_type, new_ds_extent,
                        self.ds_dic['PERIODICITY'],
                        os.path.join(self.bat_dic['curr_output_path'], new_ds_name),
                        self.bat_dic['output_prefix'],
                        self.ds_dic['DATADATEFORMAT'],
                        self.ds_dic['DATASUFFIX'],
                        self.ds_dic['DATAMISSINGVALUE'],
                        os.path.join(self.bat_dic['curr_output_path'], new_ds_name),
                        self.ds_dic['AVGDATAPREFIX'],
                        self.ds_dic['AVGDATADATEFORMAT'],
                        self.ds_dic['AVGDATASUFFIX'],
                        self.ds_dic['AVGDATAMISSINGVALUE'],
                        '', '', '', '', '', '', '',
                        '', '', '', '', 'No', '1']  # 1 is DATASCALEFACTOR
            self.ds_info.write_dataset_record(qry_vals)

        return new_ds_name

    def dataset_selection_changed(self):
        '''
        Method to update database object and dependent gui widgets.
        '''
        err = self.ds_info.query_named_dataset(
            self.ui.datasetComboBox.currentText())
        if err:
            raise IOError('Database read failed')
        self.ds_dic = self.ds_info.get_ds_dictionary()
        self.input_files_list = util.get_input_file_list(self.ds_dic)
        if self.input_files_list == []:
            QMessageBox.information(
                self,
                'No data for selected dataset',
                'The dataset you selected has no data available.',
                QMessageBox.Ok)
            self.ui.regionComboBox.clear()
            return
        util.fill_region_combo_for_dataset_selection(
            self.ui.regionComboBox, self.input_files_list[0])
        self.toggle_data_avg_prefix()
        # if we need extents, row, col counts etc then do that here

    def delimiter_selection_changed(self):
        '''
        Method called when csv delimiter selection changes
        '''
        self.bat_dic["delimiter"] = self.DELIMITER_DIC[
            self.ui.delimComboBox.currentText()]
        if self.ui.stationFileLineEdit.text() != "":
            self.extract_station_file_header_info(
                self.ui.stationFileLineEdit.text())

    def header_row_count_selection_changed(self):
        '''
        Method called when the header row value changes
        '''
        self.bat_dic['csv_hdr_row'] = self.ui.hdrRowSpinBox.value()
        if self.ui.stationFileLineEdit.text() != "":
            self.extract_station_file_header_info(
                self.ui.stationFileLineEdit.text())

    def extract_station_file_header_info(self, csv_filename):
        '''
        Method to extract column header information from a station file (.csv).
        params(string) - csv_filename - Station filename.
        '''
        try:
            self.bat_dic['stn_file_col_list'] = []
            self.bat_dic['stn_file_year_list'] = []
            self.ui.stnIDColComboBox.clear()
            self.ui.latColComboBox.clear()
            self.ui.longColComboBox.clear()
            self.ui.yrColComboBox.clear()
            self.ui.fstIntvlColComboBox.clear()
            self.ui.lastIntvlColComboBox.clear()

            # open the csv
            if not os.path.exists(csv_filename):
                QgsMessageLog.logMessage(
                    'Station file is missing:  ' + csv_filename,
                    level=Qgis.Critical)
                return

            with open(csv_filename) as csv_file:
                reader1 = csv.reader(csv_file,
                                     delimiter=self.bat_dic['delimiter'])
                i = 0
                # get the column headings(last row before data or the first row
                for rowa in reader1:
                    if i == self.bat_dic['csv_hdr_row'] - 1:
                        self.bat_dic['stn_file_col_list'] = rowa
                        break
                    i += 1
                self.ui.stnIDColComboBox.addItem(' ')
                self.ui.latColComboBox.addItem(' ')
                self.ui.longColComboBox.addItem(' ')
                self.ui.yrColComboBox.addItem(' ')
                self.ui.fstIntvlColComboBox.addItem(' ')
                self.ui.lastIntvlColComboBox.addItem(' ')

                # fill in the drop down lists for the station file
                i = 1
                for entry in self.bat_dic['stn_file_col_list']:
                    temp_string = 'Col ' + str(i) + ' - ' + entry
                    self.ui.stnIDColComboBox.addItem(temp_string)
                    self.ui.latColComboBox.addItem(temp_string)
                    self.ui.longColComboBox.addItem(temp_string)
                    self.ui.yrColComboBox.addItem(temp_string)
                    self.ui.fstIntvlColComboBox.addItem(temp_string)
                    self.ui.lastIntvlColComboBox.addItem(temp_string)
                    temp = entry
                    # test guesses
                    if 'id' in temp.lower():
                        self.ui.stnIDColComboBox.setCurrentIndex(i)
                    elif 'year' in temp.lower():
                        self.ui.yrColComboBox.setCurrentIndex(i)
                        yr_index = i - 1
                    elif 'lon' in temp.lower():
                        self.ui.longColComboBox.setCurrentIndex(i)
                    elif 'lat' in temp.lower():
                        self.ui.latColComboBox.setCurrentIndex(i)
                    i = i + 1
                # overwrite any that are specified by bat dic
                if self.bat_dic['csv_stn_id_col'] != 0:
                    self.ui.stnIDColComboBox.setCurrentIndex(
                        self.bat_dic['csv_stn_id_col'])
                if self.bat_dic['csv_year_col'] != 0:
                    self.ui.yrColComboBox.setCurrentIndex(
                        self.bat_dic['csv_year_col'])
                    yr_index = self.bat_dic['csv_year_col'] - 1
                if self.bat_dic['csv_long_col'] != 0:
                    self.ui.longColComboBox.setCurrentIndex(
                        self.bat_dic['csv_long_col'])
                if self.bat_dic['csv_lat_col'] != 0:
                    self.ui.latColComboBox.setCurrentIndex(
                        self.bat_dic['csv_lat_col'])
                if self.bat_dic['csv_beg_period_col'] != 0:
                    self.ui.fstIntvlColComboBox.setCurrentIndex(
                        self.bat_dic['csv_beg_period_col'])
                if self.bat_dic['csv_end_period_col'] != 0:
                    self.ui.lastIntvlColComboBox.setCurrentIndex(
                        self.bat_dic['csv_end_period_col'])

                # get the years
                for rowb in reader1:
                    self.bat_dic['stn_file_year_list'].append(rowb[yr_index])
            self.bat_dic['stn_file_year_list'] =\
                sorted(set(self.bat_dic['stn_file_year_list']),
                       key=None,
                       reverse=False)
            self.ui.stnMissValSpinBox.setValue(
                self.bat_dic['stn_missing_val'])

        except BaseException:
            QgsMessageLog.logMessage(
                'Check the columns and delimiter in the csv: ' + csv_filename,
                level=Qgis.Warning)

    def interpolation_selection_changed(self):
        '''
        Method to update parameters when the interpolation type changes.
        '''
        self.bat_dic['interp_type'] = self.ui.interpComboBox.currentText()

        if self.bat_dic['interp_type'] == 'Simple':
            self.ui.lrValSpinBox.setVisible(True)
            self.ui.maxEffDistSpinBox.setVisible(True)
            self.ui.lblLRVal.setVisible(True)
            self.ui.lblMaxEffDist.setVisible(True)
        else:  # Ordinary
            self.ui.lrValSpinBox.setVisible(False)
            self.ui.maxEffDistSpinBox.setVisible(False)
            self.ui.lblLRVal.setVisible(False)
            self.ui.lblMaxEffDist.setVisible(False)

    def not_implemented(self):
        '''
        Method to indicate an unimplemented option..
        '''
        dlg_string = ' That feature has not been implemented yet.'
        QMessageBox.information(self,
                                u'Feature Not Currently Implemented',
                                dlg_string,
                                QMessageBox.Ok)

    def region_selection_changed(self):
        '''
        Method to handle a region change
        '''
        if not self.ui.regionComboBox.currentText() == '':
            self.reg_info.query_named_region(
                self.ui.regionComboBox.currentText())
            self.reg_dic = self.reg_info.get_region_dictionary()
            self.ui.ulxSpinBox.setValue(
                round(self.reg_dic['MinimumLongitude'], 5))
            self.ui.ulySpinBox.setValue(
                round(self.reg_dic['MaximumLatitude'], 5))
            self.ui.lrxSpinBox.setValue(
                round(self.reg_dic['MaximumLongitude'], 5))
            self.ui.lrySpinBox.setValue(
                round(self.reg_dic['MinimumLatitude'], 5))

    def check_region(self) -> bool:
        '''
        Method to update region object.
        :return: Boolean. False means invalid region selected.
        True means valid region selected. It's used like a flag to indicate
        if the region is able to be used.
        '''
        # when you clear the control it counts as a selection change
        # so if nothing is in the text, do nothing
        if not self.ui.regionComboBox.currentText() == '':
            err = self.reg_info.query_named_region(
                self.ui.regionComboBox.currentText())
            if err:
                raise IOError('Database read failed')
            self.reg_dic = self.reg_info.get_region_dictionary()
            if self.input_files_list:
                self.col_offset, self.row_offset = qgs_util.get_offsets(
                    self.input_files_list[0], self.reg_dic)
            else:
                self.col_offset = 0
                self.row_offset = 0
            self.region_no_space =\
                self.ui.regionComboBox.currentText().replace(' ', '')
            if not os.path.exists(self.reg_dic['Mask']):
                QMessageBox.warning(
                    self,
                    'Missing Mask File for region ' +
                    self.ui.regionComboBox.currentText(),
                    'Missing mask file: ' + self.reg_dic['Mask'],
                    QMessageBox.Ok
                )
                if self.ui.regionComboBox.count() == 1:
                    self.ui.regionComboBox.clear()
                else:
                    self.ui.regionComboBox.setCurrentIndex(0)
                return False

            msk_extent, _, _, _ =\
                qgs_util.extract_raster_file_params(self.reg_dic['Mask'])
            # this should have been done when region was created, but just in
            # case someone has messed with the mask file afterwards
            mask_ok = qgs_util.check_dataset_vs_region_extents(
                self, msk_extent, qgs_util.get_region_extent(self.reg_dic))

            if not mask_ok:
                return False
        return True

    def step1(self):
        '''
        Method to return to form for BASIICS - Step 1 parameter entry.
        '''
        reply = QMessageBox.information(self,
                                        u'Confirm - start over?',
                                        u'Going back will undo entries '
                                        'made in this step.  Do you '
                                        'want to proceed?',
                                        QMessageBox.Cancel,
                                        QMessageBox.Ok)
        if reply == QMessageBox.Ok:
            dlg_1 = BasiicsController()
            self.close()
            dlg_1.exec_()

    def step3(self):
        '''
        Method to move on to the form for BASIICS Step 3 parameter entry.
        '''
        if not self.check_region():
            return
        self.ui.ulxSpinBox.setValue(
            round(self.reg_dic['MinimumLongitude'], 4))
        self.ui.ulySpinBox.setValue(round(self.reg_dic['MaximumLatitude'], 4))
        self.ui.lrxSpinBox.setValue(round(self.reg_dic['MaximumLongitude'], 4))
        self.ui.lrySpinBox.setValue(round(self.reg_dic['MinimumLatitude'], 4))
        # save parameters into info structure, before calling the next screen
        if self.ui.stationFileLineEdit.text() == '':
            QMessageBox.information(self,
                                    u'Station File required to proceed!',
                                    u'Enter a station file to proceed!!',
                                    QMessageBox.Ok)
            return

        if self.input_files_list == []:
            QMessageBox.information(
                self,
                'No data for selected dataset',
                'The dataset you selected has no data available, \n' \
                'please select a dataset with data available.',
                QMessageBox.Ok)
            self.ui.regionComboBox.clear()
            return

        # interpolate has option to create a new dataset
        if self.bat_dic['analysis_type'] == 1 or\
                self.bat_dic['analysis_type'] == 3:
            reply = QMessageBox.question(
                self,
                'Do you want to create a new dataset?',
                'Do you want to create a new dataset from outputs? '
                'By doing this, the output folder will be updated to {}'.format(os.path.join(self.bat_dic['curr_output_path'], '<new_dataset_name>')),
                QMessageBox.Yes, QMessageBox.No)
            if reply == QMessageBox.Yes:
                new_ds_name = self.create_new_dataset()
                if new_ds_name:
                    self.bat_dic['curr_output_path'] = \
                        os.path.join(self.bat_dic['curr_output_path'], new_ds_name)
                    self.bat_dic['stats_out_file'] =\
                        os.path.join(self.bat_dic['curr_output_path'], self.STATS_CSV)
                    self.ui.statsOutputLineEdit.setText(self.bat_dic['stats_out_file'])

        if self.ui.stnIDColComboBox.currentIndex() == 0 or \
           self.ui.latColComboBox.currentIndex() == 0 or \
           self.ui.longColComboBox.currentIndex() == 0 or \
           self.ui.yrColComboBox.currentIndex() == 0 or \
           self.ui.fstIntvlColComboBox.currentIndex() == 0 or \
           self.ui.lastIntvlColComboBox.currentIndex() == 0:
            QMessageBox.information(self,
                                    u'Missing Station File column!!',
                                    u'All column indexes must be '
                                    'identified to proceed!!',
                                    QMessageBox.Ok)
            return
        self.bat_dic['selected_ds'] =\
            self.ui.datasetComboBox.currentText()
        self.bat_dic['selected_reg'] =\
            self.ui.regionComboBox.currentText()

        self.bat_dic['station_filename'] = self.ui.stationFileLineEdit.text()
        self.bat_dic['stn_missing_val'] = self.ui.stnMissValSpinBox.value()
        self.bat_dic['csv_hdr_row'] = self.ui.hdrRowSpinBox.value()
        # these column indexes are ones based...
        self.bat_dic['csv_stn_id_col'] =\
            self.ui.stnIDColComboBox.currentIndex()
        self.bat_dic['csv_lat_col'] = self.ui.latColComboBox.currentIndex()
        self.bat_dic['csv_long_col'] = self.ui.longColComboBox.currentIndex()
        self.bat_dic['csv_year_col'] = self.ui.yrColComboBox.currentIndex()
        self.bat_dic['csv_beg_period_col'] =\
            self.ui.fstIntvlColComboBox.currentIndex()
        self.bat_dic['csv_end_period_col'] =\
            self.ui.lastIntvlColComboBox.currentIndex()

        self.bat_dic['wt_power'] = self.ui.wtSpinBox.value()
        self.bat_dic['min_stations'] = self.ui.minStnsSpinBox.value()
        self.bat_dic['max_stations'] = self.ui.maxStnsSpinBox.value()
        self.bat_dic['search_radius'] = self.ui.srchRadSpinBox.value()
        self.bat_dic['fuzz_factor'] = self.ui.fuzzSpinBox.value()
        self.bat_dic['back_eq_distance'] = self.ui.maxEffDistSpinBox.value()
        self.bat_dic['lr_value'] = self.ui.lrValSpinBox.value()
        self.bat_dic['max_ratio'] = self.ui.maxRatioSpinBox.value()
        self.bat_dic['interp_type'] = self.ui.interpComboBox.currentText()
        self.bat_dic['output_stats_flag'] =\
            self.ui.statsOutCheckBox.isChecked()
        self.bat_dic['stats_out_file'] = self.ui.statsOutputLineEdit.text()
        self.bat_dic['selected_ds'] = self.ui.datasetComboBox.currentText()
        self.bat_dic['selected_reg'] = self.ui.regionComboBox.currentText()

        dlg_3 = BasiicsController3(
            self.bat_dic, self.wrksp_setup, self.ds_dic, self.reg_dic)
        self.close()
        dlg_3.exec_()

#    def toggle_advanced_options(self):
#        '''
#        Method to enable BASIICS blending advanced options.
#        '''
#        if self.ui.advOptionsCheckBox.isChecked():
#            self.ui.extentsGroupBox.setVisible(True)
#            self.bat_dic['adv_options_flag'] = True
#            self.ui.advOptGroupBox.setVisible(True)
#        else:
#            self.ui.extentsGroupBox.setVisible(False)
#            self.bat_dic['adv_options_flag'] = False
#            self.ui.advOptGroupBox.setVisible(False)

    def toggle_data_avg_prefix(self):
        '''
        Method to specify dataset averges as input grids.
        '''
        if self.ui.dsAvgCheckBox.isChecked():
            self.bat_dic['input_prefix'] = self.ds_dic['AVGDATAPREFIX']
            self.bat_dic['input_file_list'] =\
                util.get_input_file_list(self.ds_dic, True)
            self.bat_dic['ds_avg_flag'] = True
        else:
            self.bat_dic['input_prefix'] = self.ds_dic['DATAPREFIX']
            self.bat_dic['input_file_list'] =\
                util.get_input_file_list(self.ds_dic)
            self.bat_dic['ds_avg_flag'] = False
        self.bat_dic['input_file_list'].sort()

    def update_output_prefix(self):
        '''
        Method to update the output prefix.
        '''
        self.bat_dic['output_prefix'] = self.ui.prefixLineEdit.text()

    def update_station_file_path(self):
        '''
        Method to update the station file and controls.
        '''
        if self.ui.stationFileLineEdit.text() != '':
            self.extract_station_file_header_info(
                self.ui.stationFileLineEdit.text())
        else:
            self.ui.stnIDColComboBox.clear()
            self.ui.latColComboBox.clear()
            self.ui.longColComboBox.clear()
            self.ui.yrColComboBox.clear()
            self.ui.fstIntvlColComboBox.clear()
            self.ui.lastIntvlColComboBox.clear()
            self.ui.hdrRowSpinBox.setValue(1)
            self.ui.stnMissValSpinBox.setValue(
                self.bat_dic['stn_missing_val'])

#    def write_stats_out(self):
#        '''
#        Method to enable BASIICS statitical output generation.
#        '''
#        if self.ui.statsOutCheckBox.isChecked() is True:
#            self.bat_dic['output_stats_flag'] = True
#            self.ui.statsBrowseButton.setVisible(True)
#            self.ui.statsOutputLineEdit.setVisible(True)
#            self.ui.statsOutputLineEdit.setText(self.bat_dic['stats_out_file'])
#            self.ui.lblStatsFile.setVisible(True)
#        else:
#            self.bat_dic['output_stats_flag'] = False
#            self.ui.statsBrowseButton.setVisible(False)
#            self.ui.statsOutputLineEdit.setVisible(False)
#            self.ui.lblStatsFile.setVisible(False)


class BasiicsController3(QDialog):
    '''
    Class to for BASIICS parameter entry - Step 3.
    Args:
       batch_dic(object) - GUI parameters for BASIICS.
       wrksp_setup(object) - Workspace parameters.
       ds_dic(object) - Dataset parameters.
       reg_dic(object) - Region parameters.
    '''

    def __init__(self, batch_dic, wrksp_setup, ds_dic, reg_dic):

        QDialog.__init__(self)
        self.ui = Ui_BatchAssistant3()
        self.ui.setupUi(self)
        self.bat_dic = batch_dic
        self.wrksp_setup = wrksp_setup
        self.ds_dic = ds_dic
        self.reg_dic = reg_dic
        self.selected_years_list = []
        self.selected_periods_list = []

        self.ui.intRangeLineEdit.setText('0')
        self.progress = 0
        self.ui.progressBar.setValue(self.progress)
        self.dates_list = []
        if sys.platform.startswith('win'):
            self.start_cmd = 'start '
        elif sys.platform.startswith('linux'):
            self.start_cmd = 'xgd-open '
        t_val = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
        t_val_nm = 'basiics_batch_' + t_val + '.bat'
        self.bat_filename = os.path.join(self.bat_dic['curr_output_path'],
                                         t_val_nm)
        try:
            if os.path.exists(self.bat_filename):
                os.remove(self.bat_filename)
        except OSError:
            msg = 'ERROR:  File open in another process' + str(
                self.bat_filename)
            iface.mainWindow().statusBar().showMessage(msg)
            iface.messageBar().pushMessage(msg, level=Qgis.Critical)
            return
        if self.bat_dic['analysis_type'] != 4:
            self.reg_shapefile = reg_dic['Map'].split(',')[0]
            # fill in period and year controls
            util.fill_period_widget(self.ui.frmPerComboBox, False, self.ds_dic['PERIODICITY'])
            util.fill_period_widget(self.ui.toPerComboBox, False, self.ds_dic['PERIODICITY'])

            self.available_input_files = util.get_input_file_list(self.ds_dic)
            self.available_years_list = util.extract_data_years(
                self.ds_dic, self.available_input_files)
            self.interval_dic = util.get_interval_dic(self.ds_dic['PERIODICITY'])
            self.ui.intervalLineEdit.setText(self.ds_dic['PERIODICITY'])
            mid_point = util.get_midpoint(self.ds_dic)
        else:
            # fill in period and year controls
            util.fill_period_widget(self.ui.frmPerComboBox, False, self.bat_dic['periodicity'])
            util.fill_period_widget(self.ui.toPerComboBox, False, self.bat_dic['periodicity'])
            self.interval_dic = util.get_interval_dic(self.bat_dic['periodicity'])
            self.ui.intervalLineEdit.setText(self.bat_dic['periodicity'])
            self.available_years_list = self.bat_dic['stn_file_year_list']
            mid_point = None

        self.beg_per = 1
        self.end_per = len(self.interval_dic)
        util.fill_year_widget(self.ui.frmYrComboBox, False,
                            self.available_years_list,
                            self.beg_per, self.end_per, mid_point)
        util.fill_year_widget(self.ui.toYrComboBox, False,
                            self.available_years_list,
                            self.beg_per, self.end_per, mid_point)
        self.year_period_ct = len(self.interval_dic)
        self.ui.frmYrComboBox.currentIndexChanged['QString'].connect(
            self.period_selection_changed)
        self.ui.toYrComboBox.currentIndexChanged['QString'].connect(
            self.period_selection_changed)
        self.ui.frmPerComboBox.currentIndexChanged['QString'].connect(
            self.period_selection_changed)
        self.ui.toPerComboBox.currentIndexChanged['QString'].connect(
            self.period_selection_changed)

        self.ui.saveButton.clicked.connect(self.save_param_file)
        self.ui.previousButton.clicked.connect(self.step2)
        self.ui.processButton.clicked.connect(self.start_process)
        self.ui.cancelButton.setEnabled(False)
        self.ui.intRangeLineEdit.setEnabled(False)

        if self.bat_dic['start_period'] != '':
            st_idx = self.ui.frmPerComboBox.findText(
                self.bat_dic['start_period'])
            if st_idx == -1:
                st_idx = 0
            self.ui.frmPerComboBox.setCurrentIndex(st_idx)
        if self.bat_dic['start_year'] != '':
            st_yr_idx = self.ui.frmYrComboBox.findText(
                self.bat_dic['start_year'])
            if st_yr_idx == -1:
                st_yr_idx = 0
            self.ui.frmYrComboBox.setCurrentIndex(st_yr_idx)

        if self.bat_dic['end_period'] != '':
            end_idx = self.ui.toPerComboBox.findText(
                self.bat_dic['end_period'])
            if end_idx == -1:
                end_idx = 0
            self.ui.toPerComboBox.setCurrentIndex(end_idx)
        if self.bat_dic['end_year'] != '':
            end_yr_idx = self.ui.toYrComboBox.findText(
                self.bat_dic['end_year'])
            if end_yr_idx == -1:
                end_yr_idx = 0
            self.ui.toYrComboBox.setCurrentIndex(end_yr_idx)

        if not os.path.exists(self.bat_dic['curr_output_path']):
            os.makedirs(self.bat_dic['curr_output_path'])

        self.color_dir = self.wrksp_setup.get_colors_path()
        self.available_outputs_list = []
        self.display_outputs_list = []
        self.os_outputs_list = []
        self.thread = None
        self.worker = None
        self.open_file_info = None
        self.is_processing = False
        self.closed = False  # A flag to indicate if close_event is triggered
        self.close_event = None

    def setup_output_list_check_for_open_outputs(self):
        '''
        Check Map layer panel for open outputs and remove them if they are
        loaded,  otherwise QGIS crashes.  In case user has loaded a previous
        output and is re-running.
        '''
        self.bat_dic['dst_filename_list'] = []
        layer_list = []
        for entry in self.bat_dic['dates_list']:
            data_name_base = self.bat_dic['output_prefix'] + entry
            anom_name_base = data_name_base + '_anom'
            shape_name_base = data_name_base + '_stn'
            layer_list.append(data_name_base)
            layer_list.append(anom_name_base)
            layer_list.append(shape_name_base)
            sub_list = []
            sub_list.append(os.path.join(self.bat_dic['curr_output_path'],
                            data_name_base + self.ds_dic["DATASUFFIX"]))
            sub_list.append(os.path.join(self.bat_dic['curr_output_path'],
                            shape_name_base + ".shp"))
            self.bat_dic['dst_filename_list'].append(sub_list)
        for entry in self.open_file_info:
            if entry[1] in layer_list:
                qgs_util.notify_loaded_file(self, entry[1], entry[0])

    def __disable_controls__(self):
        '''
        Disabled controls while processing
        '''
        self.ui.cancelButton.setEnabled(True)
        self.ui.previousButton.setEnabled(False)
        self.ui.processButton.setEnabled(False)
        self.ui.closeButton.setEnabled(False)
        self.ui.frmPerComboBox.setEnabled(False)
        self.ui.frmYrComboBox.setEnabled(False)
        self.ui.toPerComboBox.setEnabled(False)
        self.ui.toYrComboBox.setEnabled(False)
        self.ui.saveButton.setEnabled(False)

    def __process_error__(self, err, ex_str):
        '''
        Method to log any errors from the validateRFE object and thread.
        params(int) e  - Exception - not used
        params(string) - ex_str - Exception string.
        '''
        QgsMessageLog.logMessage(ex_str, level=Qgis.Critical)
        QMessageBox.critical(self, 'Error!', ex_str, QMessageBox.Ok)
        iface.mainWindow().statusBar().showMessage(ex_str)

        iface.messageBar().clearWidgets()
        self.__reset_controls__()

    def __reset_controls__(self):
        '''
        Reset disabled controls at end of process
        '''
        self.ui.cancelButton.setEnabled(False)
        self.ui.previousButton.setEnabled(True)
        self.ui.processButton.setEnabled(True)
        self.ui.closeButton.setEnabled(True)
        self.ui.frmPerComboBox.setEnabled(True)
        self.ui.frmYrComboBox.setEnabled(True)
        self.ui.toPerComboBox.setEnabled(True)
        self.ui.toYrComboBox.setEnabled(True)
        self.ui.saveButton.setEnabled(True)

    def blender_start(self):
        '''
        Method to get dates for the blending function for the rasters and
        stations.
        '''
        msg_text = u'BASIICS Blending running...'
        iface.mainWindow().statusBar().showMessage(msg_text)
        err = self.create_os_output_list()
        if err is True:
            return

        self.worker = BlenderWorker(self.bat_dic, self.wrksp_setup)
        self.ui.cancelButton.clicked.connect(self.worker.kill)
        self.__disable_controls__()

        time_str_l = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
        QgsMessageLog.logMessage(u'Start time: ' + time_str_l,
                                 level=Qgis.Info)

        # # ###self.worker.run()  # for debug
        # start the 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.blender_finished)
        self.worker.error.connect(self.__process_error__)
        self.worker.progress.connect(self.ui.progressBar.setValue)
        self.is_processing = True

    def blender_finished(self, return_value):
        '''
        Method to clean up the blender object and thread.
        params(list) -  ret - Returned code and string from blend object.
        '''
        self.is_processing = False
        self.thread.quit()
        self.thread.wait()
        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"Blending aborted by user")
            iface.messageBar().pushCritical("Blending", msg)
            QgsMessageLog.logMessage(msg, level=Qgis.Critical)
            iface.mainWindow().statusBar().showMessage(msg, 3000)
        elif return_value is not None:
            if return_value[0] == 0:
                iface.messageBar().clearWidgets()
                self.check_for_outputs()
                self.load_outputs_to_panel(config.RF_2500_RASTER_COLOR_FILE)
                self.create_jpgs_for_available_outputs(
                    config.RF_2500_RASTER_COLOR_FILE)
                if self.bat_dic['output_stats_flag']:
                    disp_file1 = os.path.join(self.bat_dic['curr_output_path'],
                                              'stats.crossval_graph.jpg')
                    disp_file2 = os.path.join(self.bat_dic['curr_output_path'],
                                              'stats.stngrid_graph.jpg')
                    self.open_jpg_csv([disp_file1, disp_file2])

            util.remove_temp_files(self.bat_dic['curr_output_path'])

            output_lists = []
            for out_list in self.display_outputs_list:
                output_lists += out_list
            log_util.add_log("BASIICS Blending",
                             log_util.log_string("Station",
                                                 [self.bat_dic['station_filename']]) +
                             log_util.log_string("Weight Power",
                                                 [self.bat_dic['wt_power']]) +
                             log_util.log_string("Min Stations",
                                                 [self.bat_dic['min_stations']]) +
                             log_util.log_string("Max Stations",
                                                 [self.bat_dic['max_stations']]) +
                             log_util.log_string("Search Radius",
                                                 [self.bat_dic['search_radius']]) +
                             log_util.log_string("Fuzz Factor (pixels)",
                                                 [self.bat_dic['fuzz_factor']]) +
                             log_util.log_string("Background Eq Dist (BED)",
                                                 [self.bat_dic['back_eq_distance']]) +
                             log_util.log_string("Long Range Value",
                                                 [self.bat_dic['lr_value']]) +
                             log_util.log_string("Max Ratio",
                                                 [self.bat_dic['max_ratio']]) +
                             log_util.log_string("Interpolation Style",
                                                 [self.bat_dic['interp_type']]) +
                             log_util.log_string("Start Period",
                                                 [self.bat_dic['start_period']]) +
                             log_util.log_string("Start Year",
                                                 [self.bat_dic['start_year']]) +
                             log_util.log_string("End Period",
                                                 [self.bat_dic['end_period']]) +
                             log_util.log_string("End Year",
                                                 [self.bat_dic['end_year']]),
                             output_lists)

            self.ui.progressBar.setValue(100)
            msg = 'BASIICS Blending Completed'
            QgsMessageLog.logMessage(msg, level=Qgis.Info)
            iface.mainWindow().statusBar().showMessage(msg, 3000)
        else:
            # notify of an error
            msg = (u'Error: Blending did not finish')
            iface.messageBar().pushCritical('Blending', msg)
            QgsMessageLog.logMessage(msg, level=Qgis.Critical)
            iface.mainWindow().statusBar().showMessage(msg, 3000)
        self.__reset_controls__()
        try:
            os.remove(self.bat_filename)
        except OSError:
            pass
        self.thread_worker_cleanup()
        all_done = util.notify_process_completed('BASIICS Blending')
        if all_done:
            self.ui.closeButton.click()

    def check_station_file_encoding(self):
        '''
        Reads the station file, must be utf-8 encoding
        Notify user if not.
        '''
        err = True
        try:
            # read in original station file, has all stations all sample dates
            with open(self.bat_dic['station_filename'],
                      'r', encoding='utf-8') as csv_input:
                csv.reader(csv_input, delimiter=self.bat_dic['delimiter'])
                err = False
        except BaseException:
            QMessageBox.critical(
                None, 'Error!',
                'Unable to read station file. Convert it to utf-8 format.',
                QMessageBox.Ok)
        return err

    def interpolator_start(self):
        '''
        Method to get info for interpolating stations.
        '''
        msg_text = u'Interpolate stations running...'
        iface.mainWindow().statusBar().showMessage(msg_text)
        err = self.create_os_output_list()
        if err is True:
            return

        self.worker = InterpolateStationsWorker(self.bat_dic, self.wrksp_setup)
        self.ui.cancelButton.clicked.connect(self.worker.kill)
        self.__disable_controls__()

        time_str_l = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
        QgsMessageLog.logMessage(u'Start time: ' + time_str_l,
                                 level=Qgis.Info)

        # # ###self.worker.run()  # for debug
        # start the 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.interpolator_finished)
        self.worker.error.connect(self.__process_error__)
        self.worker.progress.connect(self.ui.progressBar.setValue)
        self.is_processing = True

    def interpolator_finished(self, return_value):
        '''
        Method to clean up the interpolater object and thread.
        params(tuple) - ret_value - Returned code and string from blend object.
        '''
        self.is_processing = False
        self.thread.quit()
        self.thread.wait()
        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"Interpolating aborted by user")
            iface.messageBar().pushCritical("Interpolating", msg)
            QgsMessageLog.logMessage(msg, level=Qgis.Critical)
            iface.mainWindow().statusBar().showMessage(msg, 3000)
        elif return_value is not None:
            if return_value[0] == 0:
                iface.messageBar().clearWidgets()
                self.check_for_outputs()
                self.load_outputs_to_panel(config.RF_2500_RASTER_COLOR_FILE)
                self.create_jpgs_for_available_outputs(
                    config.RF_2500_RASTER_COLOR_FILE)
                if self.bat_dic['output_stats_flag']:
                    disp_file1 = os.path.join(self.bat_dic['curr_output_path'],
                                              'stats.crossval_graph.jpg')
                    self.open_jpg_csv([disp_file1])
            util.remove_temp_files(self.bat_dic['curr_output_path'])

            output_lists = []
            for out_list in self.display_outputs_list:
                output_lists += out_list
            log_util.add_log("BASIICS Interpolating",
                             log_util.log_string("Station",
                                                 [self.bat_dic['station_filename']]) +
                             log_util.log_string("Weight Power",
                                                 [self.bat_dic['wt_power']]) +
                             log_util.log_string("Min Stations",
                                                 [self.bat_dic['min_stations']]) +
                             log_util.log_string("Max Stations",
                                                 [self.bat_dic['max_stations']]) +
                             log_util.log_string("Search Radius",
                                                 [self.bat_dic['search_radius']]) +
                             log_util.log_string("Fuzz Factor (pixels)",
                                                 [self.bat_dic['fuzz_factor']]) +
                             log_util.log_string("Background Eq Dist (BED)",
                                                 [self.bat_dic['back_eq_distance']]) +
                             log_util.log_string("Long Range Value",
                                                 [self.bat_dic['lr_value']]) +
                             log_util.log_string("Max Ratio",
                                                 [self.bat_dic['max_ratio']]) +
                             log_util.log_string("Interpolation Style",
                                                 [self.bat_dic['interp_type']]) +
                             log_util.log_string("Start Period",
                                                 [self.bat_dic['start_period']]) +
                             log_util.log_string("Start Year",
                                                 [self.bat_dic['start_year']]) +
                             log_util.log_string("End Period",
                                                 [self.bat_dic['end_period']]) +
                             log_util.log_string("End Year",
                                                 [self.bat_dic['end_year']]),
                             output_lists)

            self.ui.progressBar.setValue(100)
            msg = 'Interpolating Completed'
            QgsMessageLog.logMessage(msg, level=Qgis.Info)
            iface.mainWindow().statusBar().showMessage(msg, 3000)
        else:
            # notify of an error
            msg = (u'Error: Interpolating did not finish')
            iface.messageBar().pushCritical('Interpolating', msg)
            QgsMessageLog.logMessage(msg, level=Qgis.Critical)
            iface.mainWindow().statusBar().showMessage(msg, 3000)
        self.__reset_controls__()
        try:
            os.remove(self.bat_filename)
        except OSError:
            pass
        self.thread_worker_cleanup()
        all_done = util.notify_process_completed('Interpolating')
        if all_done:
            self.ui.closeButton.click()

    def check_for_outputs(self):
        '''
        Sometimes there are no outputs because no data exists for dates, and
        we don't know that until the worker starts processing, so we just
        check to see which outputs exist.
        Only display up to the last three available outputs, per Diego.
        '''
        self.available_outputs_list = []
        for entry in self.bat_dic['dst_filename_list']:
            if (os.path.exists(entry[0]) and os.path.exists(entry[1])):
                self.available_outputs_list.append(entry)
        if len(self.available_outputs_list) > 3:
            self.display_outputs_list = sorted(
                self.available_outputs_list)[-3:]
        else:
            self.display_outputs_list = self.available_outputs_list

    def create_jpgs_for_available_outputs(self, color_file):
        '''
        Create jpg outputs for available outputs.
        '''
        pass
        # this is failing needs debug
        # for entry in self.available_outputs_list:
        #     input_file_list_l = [entry[0], entry[1], self.reg_shapefile]
        #     qgs_util.write_image(input_file_list_l, self.color_dir, color_file)

    def load_outputs_to_panel(self, color_file):
        '''
        Load specified outputs
        param(string) - color_file - Color filename
        '''
        color_path = os.path.join(self.color_dir, color_file)
        stn_color_file = config.RF_2500_PTS_COLOR_FILE
        open_file_names = [sublist[1] for sublist in self.open_file_info]
        # add new results to the map panel
        for entry in self.display_outputs_list:
            # list is raster, station shapefile
            qgs_util.display_raster_layer(entry[0], color_path)
            if entry[1]:  # the stn shapefile isn't always created
                qgs_util.display_vector_layer(
                    entry[1], self.color_dir, stn_color_file)
        if self.bat_dic['analysis_type'] != 2:
            qgs_util.display_map_layer(self.reg_dic, open_file_names)

    def open_jpg_csv(self, jpg_list):
        '''
        Opens the jpgs and csv using a batch file
        param(list) - jpg_list - List of jpgs to open
        '''
        with open(self.bat_filename, 'w') as batch_temp_file:
            for entry in jpg_list:
                batch_temp_file.write(
                    self.start_cmd + ' "" ' +
                    g_util.handle_subprocess_call_path(entry) + config.CR)
            batch_temp_file.write(
                self.start_cmd + ' "" ' +
                g_util.handle_subprocess_call_path(
                    self.bat_dic['stats_out_file']) + config.CR)
        subprocess.call(
            g_util.handle_subprocess_call_path(self.bat_filename),
            shell=True)

    def period_selection_changed(self):
        '''
        Method to calculate the period count to fill into widget on
        BASIICS -Step 3 form. Fills in batch dictionary with correct start and
        end periods and years.
        '''
        year_diff = (int(self.ui.toYrComboBox.currentText()) -
                     int(self.ui.frmYrComboBox.currentText()))
        period_diff = (self.ui.toPerComboBox.currentIndex() -
                       self.ui.frmPerComboBox.currentIndex())

        if year_diff == 0:
            self.ui.intRangeLineEdit.setText(str(period_diff))
        else:
            diff = (self.year_period_ct * year_diff) + period_diff
            self.ui.intRangeLineEdit.setText(str(diff))

    def save_param_file(self):
        '''
        Method to save the parameters out to csv file. Overwrites any file
        that has the same name.
        '''
        # must do this here because can't do it in period_selection_changed
        # has issues overwriting the batch dic when loading from a param file
        self.bat_dic['start_year'] = self.ui.frmYrComboBox.currentText()
        self.bat_dic['end_year'] = self.ui.toYrComboBox.currentText()
        self.bat_dic['start_period'] = self.ui.frmPerComboBox.currentText()
        self.bat_dic['end_period'] = self.ui.toPerComboBox.currentText()
        param_file_name = QFileDialog.getSaveFileName(
            None,
            u'Select Filename',
            self.bat_dic['curr_output_path'],
            '*.txt')
        if param_file_name[0]:
            with open(param_file_name[0], 'w') as dst_file:
                if self.bat_dic['analysis_type'] == 4:
                    temp_string = 'Under Catch Correct Stations'
                elif self.bat_dic['analysis_type'] == 3:
                    temp_string = 'Interpolate Stations'
                elif self.bat_dic['analysis_type'] == 2:
                    temp_string = 'Validate RF'
                else:
                    temp_string = 'Blend'
                dst_file.write(
                    u'Analysis type:  ' + temp_string + config.CR)
                dst_file.write('Station File:  ' +
                               self.bat_dic['station_filename'] + config.CR)
                dst_file.write('Delimiter:  ' +
                               self.bat_dic['delimiter'] + config.CR)
                dst_file.write(
                    'Station Missing Value:  ' +
                    str(self.bat_dic['stn_missing_val']) + config.CR)
                dst_file.write('Lat Column:  ' +
                               str(self.bat_dic['csv_lat_col']) + config.CR)
                dst_file.write('Long Column:  ' +
                               str(self.bat_dic['csv_long_col']) + config.CR)
                dst_file.write('Year Column:  ' +
                               str(self.bat_dic['csv_year_col']) + config.CR)
                dst_file.write('Header Rows:  ' +
                               str(self.bat_dic['csv_hdr_row']) + config.CR)
                dst_file.write(
                    'Station ID Column:  ' +
                    str(self.bat_dic['csv_stn_id_col']) + config.CR)
                dst_file.write(
                    'Beginning Period Column:  ' +
                    str(self.bat_dic['csv_beg_period_col']) + config.CR)
                dst_file.write(
                    'Ending Period Column:  ' +
                    str(self.bat_dic['csv_end_period_col']) + config.CR)
                dst_file.write('Start Period:  ' +
                               self.bat_dic['start_period'] + config.CR)
                dst_file.write('Start Year:  ' +
                               self.bat_dic['start_year'] + config.CR)
                dst_file.write('End Period:  ' +
                               self.bat_dic['end_period'] + config.CR)
                dst_file.write(
                    'End Year:  ' + self.bat_dic['end_year'] + config.CR)
                dst_file.write('Weight Power:  ' +
                               str(self.bat_dic['wt_power']) + config.CR)
                dst_file.write('Minimum Station Count:  ' +
                               str(self.bat_dic['min_stations']) + config.CR)
                dst_file.write('Maximum Station Count:  ' +
                               str(self.bat_dic['max_stations']) + config.CR)
                dst_file.write(
                    'Search Radius:  ' +
                    str(self.bat_dic['search_radius']) + config.CR)
                dst_file.write('Fuzz Factor:  ' +
                               str(self.bat_dic['fuzz_factor']) + config.CR)
                dst_file.write('Interpolation Type:  ' +
                               self.bat_dic['interp_type'] + config.CR)
                dst_file.write('Cointerpolation Type:  ' +
                               self.bat_dic['co_interp_type'] + config.CR)
                dst_file.write('Output Path:  ' +
                               self.bat_dic['curr_output_path'] + config.CR)
                dst_file.write('Output Prefix:  ' +
                               self.bat_dic['output_prefix'] + config.CR)
                dst_file.write('Statistics Output:  ' +
                               self.bat_dic['stats_out_file'] + config.CR)
                dst_file.write(
                    'Output Stats Flag:  ' +
                    str(self.bat_dic['output_stats_flag']) + config.CR)
                dst_file.write(
                    'Advanced Options Flag:  ' +
                    str(self.bat_dic['adv_options_flag']) + config.CR)
                dst_file.write('Selected Dataset:  ' +
                               str(self.bat_dic['selected_ds']) + config.CR)
                if self.bat_dic['analysis_type'] == 4:
                    self.bat_dic['input_prefix'] = 'legates_corrfactor_0.5Deg.90N90S.'
                dst_file.write('Input Prefix:  ' +
                               str(self.bat_dic['input_prefix']) + config.CR)
                dst_file.write('Dataset Average Flag:  ' +
                               str(self.bat_dic['ds_avg_flag']) + config.CR)
                dst_file.write('Selected Region:  ' +
                               str(self.bat_dic['selected_reg']) + config.CR)
                if self.bat_dic['analysis_type'] == 1:
                    dst_file.write(
                        'Maximum Ratio:  ' +
                        str(self.bat_dic['max_ratio']) + config.CR)
                if self.bat_dic['interp_type'] == 'Simple':
                    dst_file.write(
                        'Background Equivalent Distance:  ' +
                        str(self.bat_dic['back_eq_distance']) + config.CR)
                    dst_file.write('Long Range Value:  ' +
                                   str(self.bat_dic['lr_value']) + config.CR)
                if self.bat_dic['analysis_type'] == 4:
                    dst_file.write(
                        'Periodicity:  ' +
                        str(self.bat_dic['periodicity']) + config.CR)
                    dst_file.write(
                        'Corrected Station File Output:  ' +
                        str(self.bat_dic['corrected_station_filename']) + config.CR)
                    dst_file.write(
                        'Update Station File Output:  ' +
                        str(self.bat_dic['update_existing_corrected_file']) + config.CR)
                    if self.bat_dic['update_existing_corrected_file']:
                        dst_file.write('Lat Column2:  ' +
                                    str(self.bat_dic['csv_lat_col2']) + config.CR)
                        dst_file.write('Long Column2:  ' +
                                    str(self.bat_dic['csv_long_col2']) + config.CR)
                        dst_file.write('Year Column2:  ' +
                                    str(self.bat_dic['csv_year_col2']) + config.CR)
                        dst_file.write('Header Rows2:  ' +
                                    str(self.bat_dic['csv_hdr_row2']) + config.CR)
                        dst_file.write(
                            'Station ID Column2:  ' +
                            str(self.bat_dic['csv_stn_id_col2']) + config.CR)
                        dst_file.write(
                            'Beginning Period Column2:  ' +
                            str(self.bat_dic['csv_beg_period_col2']) + config.CR)
                        dst_file.write(
                            'Ending Period Column2:  ' +
                            str(self.bat_dic['csv_end_period_col2']) + config.CR)
                    dst_file.write('Delimiter2:  ' +
                               self.bat_dic['delimiter2'] + config.CR)
                    dst_file.write(
                            'Station Missing Value2:  ' +
                            str(self.bat_dic['stn_missing_val2']) + config.CR)
                    dst_file.write('Header Rows2:  ' +
                                    str(self.bat_dic['csv_hdr_row2']) + config.CR)

    def set_selected_years_list(self):
        '''
        Function to gather the years selected into a list
        '''
        temp_yr = int(self.bat_dic['start_year'])
        self.selected_years_list = [self.bat_dic['start_year']]
        while temp_yr != int(self.bat_dic['end_year']):
            temp_yr += 1
            self.selected_years_list.append(str(temp_yr))

    def start_process(self):
        '''
        Method to choose which analysis to start
        '''
        # Things for all analysis types
        if int(self.ui.intRangeLineEdit.text()) < 0:
                QMessageBox.information(self,
                                        u'Error!! Invalid parameters.',
                                        u'"From" must preceed "to" date.',
                                        QMessageBox.Ok)
                return

        if self.check_station_file_encoding():  # returns true if err
            return

        self.open_file_info = qgs_util.get_open_files_info()
        # have to set these here rather than in period_selection_changed
        # because if loading parameter files, it triggers an overwrite in the
        # batch dic in period_selection_changed
        self.bat_dic['start_year'] = self.ui.frmYrComboBox.currentText()
        self.bat_dic['end_year'] = self.ui.toYrComboBox.currentText()
        self.bat_dic['start_period'] = self.ui.frmPerComboBox.currentText()
        self.bat_dic['end_period'] = self.ui.toPerComboBox.currentText()
        self.set_selected_years_list()

        if self.bat_dic['analysis_type'] != 4:
            # check for open temp csv station file, do this before we get to the
            # worker so we can fix before it crashes
            region_stns_filename = os.path.join(
                os.path.split(self.bat_dic['station_filename'])[0],
                'regionStations.csv')
            try:
                if os.path.exists(region_stns_filename):
                    os.remove(region_stns_filename)
            except OSError:
                msg = ('File open in another process:  ' +
                    region_stns_filename)
                QgsMessageLog.logMessage(msg, level=Qgis.Critical)
                QMessageBox.critical(
                    None, 'Error', msg, QMessageBox.Ok)
                return

            region_data_filename =\
                self.bat_dic['station_filename'][:-4] + '_region.csv'
            try:
                if os.path.exists(region_data_filename):
                    os.remove(region_data_filename)
            except OSError:
                msg = ('File open in another process:  ' +
                    region_data_filename)
                QgsMessageLog.logMessage(msg, level=Qgis.Critical)
                QMessageBox.critical(
                    None, 'Error', msg, QMessageBox.Ok)
                return

            self.bat_dic['ds_dic'] = self.ds_dic
            self.bat_dic['reg_dic'] = self.reg_dic
            _, _, _, self.bat_dic['geotransform'], _ = g_util.get_geotiff_info(
                self.available_input_files[0])

            temp_start_period = util.get_period_string(
                self.ds_dic, self.bat_dic['start_period'])
            temp_end_period = util.get_period_string(
                self.ds_dic, self.bat_dic['end_period'])
            input_basenames, self.bat_dic['good_files_list'], missing_files =\
                util.check_consecutive_process_inputs(
                    self.ds_dic,
                    self.bat_dic['start_year'], self.bat_dic['end_year'],
                    temp_start_period, temp_end_period)
            if missing_files != []:
                QMessageBox.information(self,
                                        u'Missing data!!',
                                        u'Missing input files : ' +
                                        str(missing_files),
                                        QMessageBox.Ok)
                return
            # extract the date strings from the basenames, used for worker loop
            self.bat_dic['dates_list'] =\
                sorted([os.path.splitext(
                    item.split(self.bat_dic['input_prefix'])[1])[0]
                    for item in input_basenames])
            self.setup_output_list_check_for_open_outputs()

            # Remove temp files before running to make sure output area is clean
            util.remove_temp_files(self.bat_dic['curr_output_path'])
            _, self.bat_dic['good_files_list'] = g_util.resample_input_files(
                self.bat_dic['curr_output_path'], None, 
                self.bat_dic['good_files_list'], self.reg_dic
            )
            _, _, _, self.bat_dic['geotransform'], _ = g_util.get_geotiff_info(
                self.bat_dic['good_files_list'][0])

            if self.bat_dic['analysis_type'] == 1:
                self.blender_start()
            elif self.bat_dic['analysis_type'] == 2:
                self.validate_rfe_start()
            else:
                self.interpolator_start()
        else:  # We are running the under catch correct station file logic instead

            # Check that the needed undercatch_fields files all exist
            self.bat_dic['input_prefix'] = 'legates_corrfactor_0.5Deg.90N90S.'

            # Check if undercatch files are already open?
            if self.check_for_undercatch_fields_files():
                return  # Missing files so return until user gets those in correct area

            # Check corrected station output is not open already
            if os.path.exists(self.bat_dic['corrected_station_filename']):
                # Check if file is open already and prompt user to close it
                if self.check_corrected_output_file():
                    # If true, already have 
                    return
            else:
                # Check if file update is selected - Can't update if it doesn't exist
                if self.bat_dic['update_existing_corrected_file']:
                    QMessageBox.information(
                        self, u'File Does Not Exist',
                        'Cannot update existing corrected file: ' +
                        str(self.bat_dic['corrected_station_filename']) +
                        '\nA new file will be created instead. ',
                        QMessageBox.Ok)
                    self.bat_dic['update_existing_corrected_file'] = False
            self.under_catch_correct_start()

    def check_corrected_output_file(self):
        '''
        Method to check if the corrected station file output is already
        open in the OS and alert user to close it if it is.
        '''
        err = False
        try:
            with open(self.bat_dic['corrected_station_filename'], 'a') as f:
                # If we reach here, the file was opened successfully for appending.
                # Immediately close it and return False (not locked by someone else in a way that prevents appending).
                pass
            return False
        except OSError:
            msg = u'Error! Do you have the output csv file open in the OS??'
            iface.mainWindow().statusBar().showMessage(msg)
            iface.messageBar().pushMessage(msg, level=Qgis.Critical)
            err = True
        return err

    def check_for_undercatch_fields_files(self):
        '''
        Method to fill in the paths for the undercatch_fields files
        and check that each files exists, and if file is open then close it
        before we run
        '''
        undercatch_fields_file_list = []
        missing_files_list = []
        folder_path = os.path.join(self.wrksp_setup.get_climate_data_path(),
                                   config.UNDERCATCH_FIELDS)
        file_name = self.bat_dic['input_prefix'] + '<MM>' + config.TIF_SUFFIX

        for _, value in config.MONTH_2_NUM_DIC.items():
            file_path = os.path.join(folder_path, file_name).replace(
                '<MM>', value['PER_YEAR'])
            if os.path.exists(file_path):
                undercatch_fields_file_list.append(file_path)
            else:
                missing_files_list.append(file_path)

        # if any missing files, report that here and return and error
        if missing_files_list:
            QMessageBox.information(
                self, u'Missing files!!',
                'Missing undercatch fields files: ' +
                str(missing_files_list) +
                '\nThese files can be extracted from the DefaultWorkspaceTemplate.zip '
                + r'at DefaultWorkspaceTemplate\fews_tools_WS\ProgramSettings\Data\Climate\undercatch_fields',
                QMessageBox.Ok)
            QgsMessageLog.logMessage(
                u'Missing files!! \n Missing undercatch fields files: ' +
                str(missing_files_list) +
                f'\nThese files can be extracted from the DefaultWorkspaceTemplate.zip '
                + r'at DefaultWorkspaceTemplate\fews_tools_WS\ProgramSettings\Data\Climate\undercatch_fields',
                level=Qgis.Info)
            return True

        # Check for open files
        for entry in self.open_file_info:
            if any(entry[1] in undercatch for undercatch in undercatch_fields_file_list):
                qgs_util.notify_loaded_file(self, entry[1], entry[0])
        self.bat_dic['undercatch_fields_file_list'] = undercatch_fields_file_list
        return False

    def step2(self):
        '''
        Method to return to BASIICS -Step 2 form..
        '''
        if self.bat_dic['analysis_type'] != 4:
            dlg_2 = BasiicsController2(
                self.bat_dic, self.wrksp_setup, None, None)
            self.close()
            dlg_2.exec_()
        else:
            dlg_2 = BasiicsController4(
                self.bat_dic, self.wrksp_setup)
            self.close()
            dlg_2.exec_()

    def validate_rfe_start(self):
        '''
        Method to set up and start the ValidateRFE thread.
        '''
        msg_text = u'Validate RFE running...'
        iface.mainWindow().statusBar().showMessage(msg_text)
        # outputs_list = []
        # outputs_list.append(os.path.join(self.bat_dic['curr_output_path'],
        #                                  'stats.stngrid_graph.jpg'))
        # outputs_list.append(self.bat_dic['stats_out_file'])
        err = self.create_os_output_list()
        if err is True:
            return
        self.worker = ValidateRFEWorker(self.bat_dic, self.wrksp_setup)
        self.ui.cancelButton.clicked.connect(self.worker.kill)
        self.__disable_controls__()

        time_str_l = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
        QgsMessageLog.logMessage(u'Start time: ' + time_str_l,
                                 level=Qgis.Info)

        # # ###self.worker.run()  # for debug
        # start the 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.validate_rfe_finished)
        self.worker.error.connect(self.__process_error__)
        self.worker.progress.connect(self.ui.progressBar.setValue)
        self.is_processing = True

    def validate_rfe_finished(self, return_value):
        '''
        Method to clean up the validateRFE object and thread.
        params(list) - ret - Returned code and string from validateRFE object.
        '''
        self.is_processing = False
        self.thread.quit()
        self.thread.wait()
        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"Validate RFE aborted by user")
            iface.messageBar().pushCritical("Validate RFE", msg)
            QgsMessageLog.logMessage(msg, level=Qgis.Critical)
            iface.mainWindow().statusBar().showMessage(msg, 3000)
        elif return_value is not None:
            if return_value[0] == 0:
                iface.messageBar().clearWidgets()
                self.check_for_outputs()
                self.load_outputs_to_panel(config.RF_2500_RASTER_COLOR_FILE)
                self.create_jpgs_for_available_outputs(
                    config.RF_2500_RASTER_COLOR_FILE)
                if self.bat_dic['output_stats_flag']:
                    disp_file1 = os.path.join(self.bat_dic['curr_output_path'],
                                              'stats.stngrid_graph.jpg')
                    self.open_jpg_csv([disp_file1])
            util.remove_temp_files(self.bat_dic['curr_output_path'])

            output_lists = []
            for out_list in self.display_outputs_list:
                output_lists += out_list
            log_util.add_log("BASIICS Validate RFE Blending",
                             log_util.log_string("Dataset",
                                                 [self.ds_dic['DATASETNAME']]) +
                             log_util.log_string("Station",
                                                 [self.bat_dic['station_filename']]) +
                             log_util.log_string("Start Period",
                                                 [self.bat_dic['start_period']]) +
                             log_util.log_string("Start Year",
                                                 [self.bat_dic['start_year']]) +
                             log_util.log_string("End Period",
                                                 [self.bat_dic['end_period']]) +
                             log_util.log_string("End Year",
                                                 [self.bat_dic['end_year']]) +
                             log_util.log_string("Periods to Process",
                                                 self.selected_periods_list),
                             output_lists)

            self.ui.progressBar.setValue(100)
            msg = 'Validate RFE Completed'
            QgsMessageLog.logMessage(msg, level=Qgis.Info)
            iface.mainWindow().statusBar().showMessage(msg, 3000)
        else:
            # notify of an error
            msg = (u'Error: Validate RFE did not finish')
            iface.messageBar().pushCritical('Validate RFE', msg)
            QgsMessageLog.logMessage(msg, level=Qgis.Critical)
            iface.mainWindow().statusBar().showMessage(msg, 3000)
        self.__reset_controls__()
        try:
            os.remove(self.bat_filename)
        except OSError:
            pass
        self.thread_worker_cleanup()
        all_done = util.notify_process_completed('Validate RFE')
        if all_done:
            self.ui.closeButton.click()

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

    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 create_os_output_list(self):
        """
        Check for existing outputs, the remove will fail if they
        are open and then we can tell the user to close them before running
        the process, else QGIS will crash when it tries to write to the open
        files.
        Arguments:
        outputs_list : List of outputs.
        Returns:
        err(boolean) - True if files are open, else False.
        """
        err = False
        self.os_outputs_list = []
        self.os_outputs_list.append(self.bat_dic['stats_out_file'])
        if self.bat_dic['analysis_type'] in [1, 3]:  # blend or interpolate
            self.os_outputs_list.append(
                os.path.join(self.bat_dic['curr_output_path'],
                             'stats.stngrid_graph.jpg'))
        if self.bat_dic['analysis_type'] in [1, 2]:  # blend or validate
            self.os_outputs_list.append(
                os.path.join(self.bat_dic['curr_output_path'],
                             'stats.crossval_graph.jpg'))
        try:
            for entry in self.os_outputs_list:
                if os.path.exists(entry):
                    os.remove(entry)
        except OSError:
            msg = u'Error! Do you have an output file open in the OS??'
            iface.mainWindow().statusBar().showMessage(msg)
            iface.messageBar().pushMessage(msg, level=Qgis.Critical)
            err = True
        return err

    def under_catch_correct_start(self):
        '''
        Method to set up and start the UnderCatchCorrect thread.
        '''
        msg_text = u'Under Catch Correct Stations running...'
        iface.mainWindow().statusBar().showMessage(msg_text)

        self.worker = UnderCatchWorker(self.bat_dic, self.wrksp_setup)
        self.ui.cancelButton.clicked.connect(self.worker.kill)
        self.__disable_controls__()

        time_str_l = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
        QgsMessageLog.logMessage(u'Start time: ' + time_str_l,
                                 level=Qgis.Info)

        # # ###self.worker.run()  # for debug
        # start the 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.under_catch_correct_finished)
        self.worker.error.connect(self.__process_error__)
        self.worker.progress.connect(self.ui.progressBar.setValue)
        self.is_processing = True

    def under_catch_correct_finished(self, return_value):
        '''
        Method to clean up the validateRFE object and thread.
        params(list) - ret - Returned code and string from validateRFE object.
        '''
        self.is_processing = False
        self.thread.quit()
        self.thread.wait()
        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"Under Catch Correct Stations aborted by user")
            iface.messageBar().pushCritical(
                "Under Catch Correct Stations", msg)
            QgsMessageLog.logMessage(msg, level=Qgis.Critical)
            iface.mainWindow().statusBar().showMessage(msg, 3000)
        elif return_value is not None:
            if return_value[0] == 0:
                iface.messageBar().clearWidgets()
            util.remove_temp_files(self.bat_dic['curr_output_path'])
            if self.bat_dic['curr_output_path'].endswith(os.sep):
                outputs = [self.bat_dic['curr_output_path']]
            else:
                outputs = [self.bat_dic['curr_output_path'] + os.sep]
            log_util.add_log(
                "BASIICS Under Catch Correct Stations",
                log_util.log_string(
                    "Station", [self.bat_dic['station_filename']]) +
                log_util.log_string(
                    "Start Period", [self.bat_dic['start_period']]) +
                log_util.log_string(
                    "Start Year", [self.bat_dic['start_year']]) +
                log_util.log_string(
                    "End Period", [self.bat_dic['end_period']]) +
                log_util.log_string(
                    "End Year", [self.bat_dic['end_year']]) +
                log_util.log_string(
                    "Periods to Process", self.selected_periods_list) +
                log_util.log_string(
                    "Under Catch Corrected Station File",
                    [self.bat_dic['corrected_station_filename']]),
                outputs)

            self.ui.progressBar.setValue(100)
            msg = 'Under Catch Correct Stations Completed'
            QgsMessageLog.logMessage(msg, level=Qgis.Info)
            iface.mainWindow().statusBar().showMessage(msg, 3000)
        else:
            # notify of an error
            msg = (u'Error: Under Catch Correct Stations did not finish')
            iface.messageBar().pushCritical(
                'Under Catch Correct Stations', msg)
            QgsMessageLog.logMessage(msg, level=Qgis.Critical)
            iface.mainWindow().statusBar().showMessage(msg, 3000)
        self.__reset_controls__()
        try:
            os.remove(self.bat_filename)
        except OSError:
            pass
        self.thread_worker_cleanup()
        all_done = util.notify_process_completed('Under Catch Correct Stations')
        if all_done:
            self.ui.closeButton.click()

class BasiicsController4(QDialog):
    '''
    Class to for BASIICS parameter entry - Step 2.
    '''
    DELIMITER_DIC = {'Comma (,)': ",",
                    'Semi-colon (;)': ";",
                    'Period (.)': ".",
                    'Space ( )': " ",
                    'Tab': "\t"}

    def __init__(self, batch_dic, wrksp_setup):
        '''
        Initialize class with params
        Args:
        batch_dic(dic) - GUI parameters for BASIICS.
        wrksp_setup(dic) - Database parameters.
        ds_dic
        eg_dic
        '''
        QDialog.__init__(self)
        self.ui = Ui_BatchAssistant4()
        self.ui.setupUi(self)

        self.bat_dic = batch_dic
        self.wrksp_setup = wrksp_setup
        util.fill_misc_widget_no_sort(self.ui.periodicityComboBox,
                                      config.PERIODICITY_LIST[0:3])
        if self.bat_dic['periodicity']:
            sel_period_idx = self.ui.periodicityComboBox.findText(
                self.bat_dic['periodicity'])
            if sel_period_idx == -1:
                sel_period_idx = 0
            self.ui.periodicityComboBox.setCurrentIndex(sel_period_idx)
        # fill these widgets before connecting anything
        util.fill_misc_widget(self.ui.delimComboBox, self.DELIMITER_DIC.keys())
        # get the key that goes with the self.bat_dic['delimiter']
        key_l = [key for key, val in self.DELIMITER_DIC.items()
                 if val == self.bat_dic['delimiter']][0]
        self.ui.delimComboBox.setCurrentText(key_l)
        # fill these widgets before connecting anything
        util.fill_misc_widget(self.ui.delimComboBox_2, self.DELIMITER_DIC.keys())
        # get the key that goes with the self.bat_dic['delimiter2']
        key_l2 = [key for key, val in self.DELIMITER_DIC.items()
                 if val == self.bat_dic['delimiter2']][0]
        self.ui.delimComboBox_2.setCurrentText(key_l2)
        self.ui.stationBrowseButton.clicked.connect(self.browse_station_file)
        self.ui.previousButton.clicked.connect(self.step1)
        self.ui.nextButton.clicked.connect(self.step3)
        self.ui.stationFileLineEdit.textChanged.connect(
            self.update_station_file_path)
        self.ui.stationOutputLineEdit.textChanged.connect(
            self.update_existing_checkbox_logic)
        self.ui.delimComboBox.currentIndexChanged.connect(
            self.delimiter_selection_changed)
        self.ui.delimComboBox_2.currentIndexChanged.connect(
            self.under_catch_delimiter_selection_changed)
        self.ui.hdrRowSpinBox.valueChanged.connect(
            self.header_row_count_selection_changed)
        self.ui.hdrRowSpinBox_2.valueChanged.connect(
            self.under_catch_header_row_count_selection_changed)
        self.ui.stationBrowseButton_2.clicked.connect(
            self.browse_station_out_file)
        # check if station file info exists
        if self.bat_dic['station_filename'] != '':
            self.ui.stationFileLineEdit.setText(
                self.bat_dic['station_filename'])
            # make sure we have the correct selections
            self.ui.stnMissValSpinBox.setValue(
                self.bat_dic['stn_missing_val'])
            self.ui.stnIDColComboBox.setCurrentIndex(
                self.bat_dic['csv_stn_id_col'])
            self.ui.latColComboBox.setCurrentIndex(
                self.bat_dic['csv_lat_col'])
            self.ui.longColComboBox.setCurrentIndex(
                self.bat_dic['csv_long_col'])
            self.ui.yrColComboBox.setCurrentIndex(
                self.bat_dic['csv_year_col'])
            self.ui.fstIntvlColComboBox.setCurrentIndex(
                self.bat_dic['csv_beg_period_col'])
            self.ui.lastIntvlColComboBox.setCurrentIndex(
                self.bat_dic['csv_end_period_col'])
            self.ui.hdrRowSpinBox.setValue(
                self.bat_dic['csv_hdr_row'])

        if self.bat_dic['corrected_station_filename'] != '':
            if self.bat_dic['update_existing_corrected_file']:
                self.ui.updateExistingCheckBox.setChecked(True)
                self.setup_under_catch_station_info_boxes()
            self.ui.stationOutputLineEdit.setText(
                self.bat_dic['corrected_station_filename'])

        if self.bat_dic['update_existing_corrected_file']:
            # make sure we have the correct selections
            self.ui.stnMissValSpinBox_2.setValue(
                self.bat_dic['stn_missing_val2'])
            self.ui.stnIDColComboBox_2.setCurrentIndex(
                self.bat_dic['csv_stn_id_col2'])
            self.ui.latColComboBox_2.setCurrentIndex(
                self.bat_dic['csv_lat_col2'])
            self.ui.longColComboBox_2.setCurrentIndex(
                self.bat_dic['csv_long_col2'])
            self.ui.yrColComboBox_2.setCurrentIndex(
                self.bat_dic['csv_year_col2'])
            self.ui.fstIntvlColComboBox_2.setCurrentIndex(
                self.bat_dic['csv_beg_period_col2'])
            self.ui.lastIntvlColComboBox_2.setCurrentIndex(
                self.bat_dic['csv_end_period_col2'])
            self.ui.hdrRowSpinBox_2.setValue(
                self.bat_dic['csv_hdr_row2'])
        # Do after initial setup, so it doesn't mess up initial load params
        self.ui.updateExistingCheckBox.toggled.connect(
            self.update_existing_checkbox_logic)

    def update_existing_checkbox_logic(self):
        '''
        Method to change UI setup based on if checkbox is checked or not
        '''
        if self.ui.updateExistingCheckBox.isChecked():
            if self.ui.stationOutputLineEdit.text() != '':
                if not os.path.exists(self.ui.stationOutputLineEdit.text()):
                    QMessageBox.information(
                        self, u'File Does Not Exist',
                        'Cannot update existing corrected file: ' +
                        str(self.ui.stationOutputLineEdit.text()) +
                        '\nA new file will be created instead. ',
                        QMessageBox.Ok)
                    self.bat_dic['update_existing_corrected_file'] = False
                    self.ui.updateExistingCheckBox.setChecked(False)
                else:
                    # Read in the Station file
                    # Fill all the UI options
                    self.update_under_catch_station_file_path()
            else:
                QMessageBox.information(
                    self, u'File  Not Chosen',
                    'No Corrected Station Output Filename selected. ' +
                    '\nPlease Select a Corrected Station Output Filename. ',
                    QMessageBox.Ok)
                self.bat_dic['update_existing_corrected_file'] = False
                self.ui.updateExistingCheckBox.setChecked(False)
        else:
            self.ui.stnIDColComboBox_2.clear()
            self.ui.latColComboBox_2.clear()
            self.ui.longColComboBox_2.clear()
            self.ui.yrColComboBox_2.clear()
            self.ui.fstIntvlColComboBox_2.clear()
            self.ui.lastIntvlColComboBox_2.clear()
        # Always set this up
        self.setup_under_catch_station_info_boxes()

    def setup_under_catch_station_info_boxes(self):
        '''
        Logic to enable and disable options as needed
        '''
        if self.ui.updateExistingCheckBox.isChecked():
            self.ui.stnIDColComboBox_2.setEnabled(True)
            self.ui.latColComboBox_2.setEnabled(True)
            self.ui.longColComboBox_2.setEnabled(True)
            self.ui.yrColComboBox_2.setEnabled(True)
            self.ui.fstIntvlColComboBox_2.setEnabled(True)
            self.ui.lastIntvlColComboBox_2.setEnabled(True)
            self.ui.lblStnIDCol_2.setEnabled(True)
            self.ui.lblLatCol_2.setEnabled(True)
            self.ui.lblLongCol_2.setEnabled(True)
            self.ui.lblYrCol_2.setEnabled(True)
            self.ui.lblFrstIntvlCol_2.setEnabled(True)
            self.ui.lblLastIntvlCol_2.setEnabled(True)
        else:
            self.ui.stnIDColComboBox_2.setEnabled(False)
            self.ui.latColComboBox_2.setEnabled(False)
            self.ui.longColComboBox_2.setEnabled(False)
            self.ui.yrColComboBox_2.setEnabled(False)
            self.ui.fstIntvlColComboBox_2.setEnabled(False)
            self.ui.lastIntvlColComboBox_2.setEnabled(False)
            self.ui.lblStnIDCol_2.setEnabled(False)
            self.ui.lblLatCol_2.setEnabled(False)
            self.ui.lblLongCol_2.setEnabled(False)
            self.ui.lblYrCol_2.setEnabled(False)
            self.ui.lblFrstIntvlCol_2.setEnabled(False)
            self.ui.lblLastIntvlCol_2.setEnabled(False)

    def browse_station_out_file(self):
        '''
        Method for browsing to and select a statitical output file location.
        '''
        filename =\
            QFileDialog.getSaveFileName(self,
                                        u'Corrected station file output location',
                                        self.wrksp_setup.get_workspace(),
                                        '*.csv')
        if filename[0]:
            self.ui.stationOutputLineEdit.setText(
                self.wrksp_setup.fix_os_sep_in_path(filename[0]))

    def browse_station_file(self):
        '''
        Method to browse to and open a station file.
        '''
        file_name = QFileDialog.getOpenFileName(
            self,
            u'Select station  file',
            self.wrksp_setup.get_workspace(),
            '*.csv')
        if file_name[0]:
            self.ui.stationFileLineEdit.setText(
                self.wrksp_setup.fix_os_sep_in_path(file_name[0]))

    def update_station_file_path(self):
        '''
        Method to update the station file and controls.
        '''
        if self.ui.stationFileLineEdit.text() != '':
            self.extract_station_file_header_info(
                self.ui.stationFileLineEdit.text())
        else:
            self.ui.stnIDColComboBox.clear()
            self.ui.latColComboBox.clear()
            self.ui.longColComboBox.clear()
            self.ui.yrColComboBox.clear()
            self.ui.fstIntvlColComboBox.clear()
            self.ui.lastIntvlColComboBox.clear()
            self.ui.hdrRowSpinBox.setValue(1)
            self.ui.stnMissValSpinBox.setValue(
                self.bat_dic['stn_missing_val'])

    def extract_station_file_header_info(self, csv_filename):
        '''
        Method to extract column header information from a station file (.csv).
        params(string) - csv_filename - Station filename.
        '''
        try:
            self.bat_dic['stn_file_col_list'] = []
            self.bat_dic['stn_file_year_list'] = []
            self.ui.stnIDColComboBox.clear()
            self.ui.latColComboBox.clear()
            self.ui.longColComboBox.clear()
            self.ui.yrColComboBox.clear()
            self.ui.fstIntvlColComboBox.clear()
            self.ui.lastIntvlColComboBox.clear()

            # open the csv
            if not os.path.exists(csv_filename):
                QgsMessageLog.logMessage(
                    'Station file is missing:  ' + csv_filename,
                    level=Qgis.Critical)
                return

            with open(csv_filename) as csv_file:
                reader1 = csv.reader(csv_file,
                                     delimiter=self.bat_dic['delimiter'])
                i = 0
                # get the column headings(last row before data or the first row
                for rowa in reader1:
                    if i == self.bat_dic['csv_hdr_row'] - 1:
                        self.bat_dic['stn_file_col_list'] = rowa
                        break
                    i += 1
                self.ui.stnIDColComboBox.addItem(' ')
                self.ui.latColComboBox.addItem(' ')
                self.ui.longColComboBox.addItem(' ')
                self.ui.yrColComboBox.addItem(' ')
                self.ui.fstIntvlColComboBox.addItem(' ')
                self.ui.lastIntvlColComboBox.addItem(' ')

                # fill in the drop down lists for the station file
                i = 1
                for entry in self.bat_dic['stn_file_col_list']:
                    temp_string = 'Col ' + str(i) + ' - ' + entry
                    self.ui.stnIDColComboBox.addItem(temp_string)
                    self.ui.latColComboBox.addItem(temp_string)
                    self.ui.longColComboBox.addItem(temp_string)
                    self.ui.yrColComboBox.addItem(temp_string)
                    self.ui.fstIntvlColComboBox.addItem(temp_string)
                    self.ui.lastIntvlColComboBox.addItem(temp_string)
                    temp = entry
                    # test guesses
                    if 'id' in temp.lower():
                        self.ui.stnIDColComboBox.setCurrentIndex(i)
                    elif 'year' in temp.lower():
                        self.ui.yrColComboBox.setCurrentIndex(i)
                        yr_index = i - 1
                    elif 'lon' in temp.lower():
                        self.ui.longColComboBox.setCurrentIndex(i)
                    elif 'lat' in temp.lower():
                        self.ui.latColComboBox.setCurrentIndex(i)
                    i = i + 1
                # overwrite any that are specified by bat dic
                if self.bat_dic['csv_stn_id_col'] != 0:
                    self.ui.stnIDColComboBox.setCurrentIndex(
                        self.bat_dic['csv_stn_id_col'])
                if self.bat_dic['csv_year_col'] != 0:
                    self.ui.yrColComboBox.setCurrentIndex(
                        self.bat_dic['csv_year_col'])
                    yr_index = self.bat_dic['csv_year_col'] - 1
                if self.bat_dic['csv_long_col'] != 0:
                    self.ui.longColComboBox.setCurrentIndex(
                        self.bat_dic['csv_long_col'])
                if self.bat_dic['csv_lat_col'] != 0:
                    self.ui.latColComboBox.setCurrentIndex(
                        self.bat_dic['csv_lat_col'])
                if self.bat_dic['csv_beg_period_col'] != 0:
                    self.ui.fstIntvlColComboBox.setCurrentIndex(
                        self.bat_dic['csv_beg_period_col'])
                if self.bat_dic['csv_end_period_col'] != 0:
                    self.ui.lastIntvlColComboBox.setCurrentIndex(
                        self.bat_dic['csv_end_period_col'])

                # get the years
                for rowb in reader1:
                    self.bat_dic['stn_file_year_list'].append(rowb[yr_index])
            self.bat_dic['stn_file_year_list'] =\
                sorted(set(self.bat_dic['stn_file_year_list']),
                       key=None,
                       reverse=False)
            self.ui.stnMissValSpinBox.setValue(
                self.bat_dic['stn_missing_val'])

        except BaseException:
            QgsMessageLog.logMessage(
                'Check the columns and delimiter in the csv: ' + csv_filename,
                level=Qgis.Warning)

    def update_under_catch_station_file_path(self):
        '''
        Method to update the station file and controls.
        '''
        if self.ui.stationOutputLineEdit.text() != '' and\
                self.ui.updateExistingCheckBox.isChecked():
            self.extract_under_catch_station_file_header_info(
                self.ui.stationOutputLineEdit.text())
        else:
            self.ui.stnIDColComboBox_2.clear()
            self.ui.latColComboBox_2.clear()
            self.ui.longColComboBox_2.clear()
            self.ui.yrColComboBox_2.clear()
            self.ui.fstIntvlColComboBox_2.clear()
            self.ui.lastIntvlColComboBox_2.clear()
            self.ui.hdrRowSpinBox_2.setValue(1)
            self.ui.stnMissValSpinBox_2.setValue(
                self.bat_dic['stn_missing_val2'])

    def extract_under_catch_station_file_header_info(self, csv_filename):
        '''
        Method to extract column header information from a station file (.csv).
        params(string) - csv_filename - Station filename.
        '''
        try:
            self.bat_dic['stn_file_col_list2'] = []
            self.bat_dic['stn_file_year_list2'] = []
            self.ui.stnIDColComboBox_2.clear()
            self.ui.latColComboBox_2.clear()
            self.ui.longColComboBox_2.clear()
            self.ui.yrColComboBox_2.clear()
            self.ui.fstIntvlColComboBox_2.clear()
            self.ui.lastIntvlColComboBox_2.clear()

            # open the csv
            if not os.path.exists(csv_filename):
                QgsMessageLog.logMessage(
                    'Station file is missing:  ' + csv_filename,
                    level=Qgis.Critical)
                return

            with open(csv_filename) as csv_file:
                reader1 = csv.reader(csv_file,
                                     delimiter=self.bat_dic['delimiter2'])
                i = 0
                # get the column headings(last row before data or the first row
                for rowa in reader1:
                    if i == self.bat_dic['csv_hdr_row2'] - 1:
                        self.bat_dic['stn_file_col_list2'] = rowa
                        break
                    i += 1
                self.ui.stnIDColComboBox_2.addItem(' ')
                self.ui.latColComboBox_2.addItem(' ')
                self.ui.longColComboBox_2.addItem(' ')
                self.ui.yrColComboBox_2.addItem(' ')
                self.ui.fstIntvlColComboBox_2.addItem(' ')
                self.ui.lastIntvlColComboBox_2.addItem(' ')

                # fill in the drop down lists for the station file
                i = 1
                for entry in self.bat_dic['stn_file_col_list2']:
                    temp_string = 'Col ' + str(i) + ' - ' + entry
                    self.ui.stnIDColComboBox_2.addItem(temp_string)
                    self.ui.latColComboBox_2.addItem(temp_string)
                    self.ui.longColComboBox_2.addItem(temp_string)
                    self.ui.yrColComboBox_2.addItem(temp_string)
                    self.ui.fstIntvlColComboBox_2.addItem(temp_string)
                    self.ui.lastIntvlColComboBox_2.addItem(temp_string)
                    temp = entry
                    # test guesses
                    if 'id' in temp.lower():
                        self.ui.stnIDColComboBox_2.setCurrentIndex(i)
                    elif 'year' in temp.lower():
                        self.ui.yrColComboBox_2.setCurrentIndex(i)
                        yr_index = i - 1
                    elif 'lon' in temp.lower():
                        self.ui.longColComboBox_2.setCurrentIndex(i)
                    elif 'lat' in temp.lower():
                        self.ui.latColComboBox_2.setCurrentIndex(i)
                    i = i + 1
                # overwrite any that are specified by bat dic
                if self.bat_dic['csv_stn_id_col2'] != 0:
                    self.ui.stnIDColComboBox_2.setCurrentIndex(
                        self.bat_dic['csv_stn_id_col2'])
                if self.bat_dic['csv_year_col2'] != 0:
                    self.ui.yrColComboBox_2.setCurrentIndex(
                        self.bat_dic['csv_year_col2'])
                    yr_index = self.bat_dic['csv_year_col2'] - 1
                if self.bat_dic['csv_long_col2'] != 0:
                    self.ui.longColComboBox_2.setCurrentIndex(
                        self.bat_dic['csv_long_col2'])
                if self.bat_dic['csv_lat_col2'] != 0:
                    self.ui.latColComboBox_2.setCurrentIndex(
                        self.bat_dic['csv_lat_col2'])
                if self.bat_dic['csv_beg_period_col2'] != 0:
                    self.ui.fstIntvlColComboBox_2.setCurrentIndex(
                        self.bat_dic['csv_beg_period_col2'])
                if self.bat_dic['csv_end_period_col2'] != 0:
                    self.ui.lastIntvlColComboBox_2.setCurrentIndex(
                        self.bat_dic['csv_end_period_col2'])

                # get the years
                for rowb in reader1:
                    self.bat_dic['stn_file_year_list2'].append(rowb[yr_index])
            self.bat_dic['stn_file_year_list2'] =\
                sorted(set(self.bat_dic['stn_file_year_list2']),
                       key=None,
                       reverse=False)
            self.ui.stnMissValSpinBox_2.setValue(
                self.bat_dic['stn_missing_val2'])

        except BaseException:
            QgsMessageLog.logMessage(
                'Check the columns and delimiter in the csv: ' + csv_filename,
                level=Qgis.Warning)

    def delimiter_selection_changed(self):
        '''
        Method called when csv delimiter selection changes
        '''
        self.bat_dic["delimiter"] = self.DELIMITER_DIC[
            self.ui.delimComboBox.currentText()]
        if self.ui.stationFileLineEdit.text() != "":
            self.extract_station_file_header_info(
                self.ui.stationFileLineEdit.text())

    def header_row_count_selection_changed(self):
        '''
        Method called when the header row value changes
        '''
        self.bat_dic['csv_hdr_row'] = self.ui.hdrRowSpinBox.value()
        if self.ui.stationFileLineEdit.text() != "":
            self.extract_station_file_header_info(
                self.ui.stationFileLineEdit.text())
    
    def under_catch_delimiter_selection_changed(self):
        '''
        Method called when csv delimiter selection changes
        '''
        self.bat_dic["delimiter2"] = self.DELIMITER_DIC[
            self.ui.delimComboBox_2.currentText()]
        if self.ui.stationOutputLineEdit.text() != "":
            self.update_existing_checkbox_logic(
                self.ui.stationOutputLineEdit.text())

    def under_catch_header_row_count_selection_changed(self):
        '''
        Method called when the header row value changes
        '''
        self.bat_dic['csv_hdr_row2'] = self.ui.hdrRowSpinBox_2.value()
        if self.ui.stationOutputLineEdit.text() != "":
            self.update_existing_checkbox_logic(
                self.ui.stationOutputLineEdit.text())

    def step1(self):
        '''
        Method to return to form for BASIICS - Step 1 parameter entry.
        '''
        reply = QMessageBox.information(self,
                                        u'Confirm - start over?',
                                        u'Going back will undo entries '
                                        'made in this step.  Do you '
                                        'want to proceed?',
                                        QMessageBox.Cancel,
                                        QMessageBox.Ok)
        if reply == QMessageBox.Ok:
            dlg_1 = BasiicsController()
            self.close()
            dlg_1.exec_()

    def step3(self):
        '''
        Method to move on to the form for BASIICS Step 3 parameter entry.
        '''
        # save parameters into info structure, before calling the next screen
        if self.ui.stationFileLineEdit.text() == '':
            QMessageBox.information(self,
                                    u'Station File required to proceed!',
                                    u'Enter a station file to proceed!!',
                                    QMessageBox.Ok)
            return

        if self.ui.stnIDColComboBox.currentIndex() == 0 or \
                self.ui.latColComboBox.currentIndex() == 0 or \
                self.ui.longColComboBox.currentIndex() == 0 or \
                self.ui.yrColComboBox.currentIndex() == 0 or \
                self.ui.fstIntvlColComboBox.currentIndex() == 0 or \
                self.ui.lastIntvlColComboBox.currentIndex() == 0:
            QMessageBox.information(self,
                                    u'Missing Station File column!!',
                                    u'All column indexes must be '
                                    'identified to proceed!!',
                                    QMessageBox.Ok)
            return
        
        if self.ui.stationOutputLineEdit.text() == '':
            QMessageBox.information(
                self, u'Corrected Station File required to proceed!',
                u'Enter a corrected station file to proceed!!',
                QMessageBox.Ok)
            return
        
        if self.ui.updateExistingCheckBox.isChecked():
            if self.ui.stnIDColComboBox_2.currentIndex() == 0 or \
                    self.ui.latColComboBox_2.currentIndex() == 0 or \
                    self.ui.longColComboBox_2.currentIndex() == 0 or \
                    self.ui.yrColComboBox_2.currentIndex() == 0 or \
                    self.ui.fstIntvlColComboBox_2.currentIndex() == 0 or \
                    self.ui.lastIntvlColComboBox_2.currentIndex() == 0:
                QMessageBox.information(
                    self, u'Missing Corrected Station File column!!',
                    u'All column indexes must be '
                    'identified to proceed!!',
                    QMessageBox.Ok)
                return

        self.bat_dic['station_filename'] = self.ui.stationFileLineEdit.text()
        self.bat_dic['corrected_station_filename'] =\
            self.ui.stationOutputLineEdit.text()
        self.bat_dic['stn_missing_val'] = self.ui.stnMissValSpinBox.value()
        self.bat_dic['csv_hdr_row'] = self.ui.hdrRowSpinBox.value()
        # these column indexes are ones based...
        self.bat_dic['csv_stn_id_col'] =\
            self.ui.stnIDColComboBox.currentIndex()
        self.bat_dic['csv_lat_col'] = self.ui.latColComboBox.currentIndex()
        self.bat_dic['csv_long_col'] = self.ui.longColComboBox.currentIndex()
        self.bat_dic['csv_year_col'] = self.ui.yrColComboBox.currentIndex()
        self.bat_dic['csv_beg_period_col'] =\
            self.ui.fstIntvlColComboBox.currentIndex()
        self.bat_dic['csv_end_period_col'] =\
            self.ui.lastIntvlColComboBox.currentIndex()

        self.bat_dic['wt_power'] = "None"
        self.bat_dic['min_stations'] = "None"
        self.bat_dic['max_stations'] = "None"
        self.bat_dic['search_radius'] = "None"
        self.bat_dic['fuzz_factor'] = "None"
        self.bat_dic['back_eq_distance'] = "None"
        self.bat_dic['lr_value'] = "None"
        self.bat_dic['max_ratio'] = "None"
        self.bat_dic['interp_type'] = "None"
        self.bat_dic['output_stats_flag'] = "None"
        self.bat_dic['stats_out_file'] = "None"
        self.bat_dic['selected_ds'] = "None"
        self.bat_dic['selected_reg'] = "None"
        self.bat_dic['periodicity'] = self.ui.periodicityComboBox.currentText()
        self.bat_dic['update_existing_corrected_file'] =\
            self.ui.updateExistingCheckBox.isChecked()
        self.bat_dic['stn_missing_val2'] = self.ui.stnMissValSpinBox_2.value()
        self.bat_dic['csv_hdr_row2'] = self.ui.hdrRowSpinBox_2.value()
        # these column indexes are ones based...
        self.bat_dic['csv_stn_id_col2'] =\
            self.ui.stnIDColComboBox_2.currentIndex()
        self.bat_dic['csv_lat_col2'] = self.ui.latColComboBox_2.currentIndex()
        self.bat_dic['csv_long_col2'] = self.ui.longColComboBox_2.currentIndex()
        self.bat_dic['csv_year_col2'] = self.ui.yrColComboBox_2.currentIndex()
        self.bat_dic['csv_beg_period_col2'] =\
            self.ui.fstIntvlColComboBox_2.currentIndex()
        self.bat_dic['csv_end_period_col2'] =\
            self.ui.lastIntvlColComboBox_2.currentIndex()

        dlg_3 = BasiicsController3(
            self.bat_dic, self.wrksp_setup, None, None)
        self.close()
        dlg_3.exec_()
