"""
/***************************************************************************
 KFRMDialog
                                 A QGIS plugin

 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                             -------------------
        begin                : 2019-10-14
        git sha              : $Format:%H$
        copyright            : (C) 2019 by KICT, Hermesys
        email                : shpark@hermesys.co.kr
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

import os
import time

from qgis.core import QgsProject, QgsVectorLayer
from qgis.PyQt.QtCore import Qt, pyqtSignal
from qgis.PyQt.QtWidgets import (
    QAbstractItemView,
    QAbstractSpinBox,
    QApplication,
    QComboBox,
    QFileDialog,
    QHeaderView,
    QMainWindow,
    QTableWidget,
    QTabWidget,
)

from kfrm_tool.include.canvas.canvas_tools import CanvasTools
from kfrm_tool.include.canvas.layer_legend_tool import LayerLegendTool
from kfrm_tool.include.canvas.layer_tools import LayerTools
from kfrm_tool.include.db.dao.geoserver_contents import ReqGeoserver
from kfrm_tool.include.db.dao.pg_query_dao import PgQuery
from kfrm_tool.include.db.dao.sqlite_query_dao import SqliteQuery
from kfrm_tool.include.db.db_connection import PostgreSQLConnection, SQLiteConnection
from kfrm_tool.include.File_Class import (
    ChFile_Exists,
    GetCSVContent_List,
    GetFile_Name,
    GetShpColumnData,
    GetShpColumnList,
    GetSubFolder_List,
)
from kfrm_tool.include.file_export import FileExport
from kfrm_tool.include.file_import import FileImport
from kfrm_tool.include.levelpool.level_pool import MakeFloodMap
from kfrm_tool.include.loading_dialog import LoadingDialog
from kfrm_tool.include.ras_to_vec import RasterToVector
from kfrm_tool.include.setting.css import WidgetCss
from kfrm_tool.include.setting.error_list import ErrorConfirm
from kfrm_tool.include.setting.logger import KFRMLogger
from kfrm_tool.include.setting.name_list import *
from kfrm_tool.include.setting.path_list import DefaultPath
from kfrm_tool.include.setting.qlabel_mouse_event import QLabelMouseEvent
from kfrm_tool.include.setting.setting_widget import SettingWidget
from kfrm_tool.include.Util import MsError, MsInfo, MsQuestion, MsTitle, makeTempFile
from kfrm_tool.kfrm_tool_dialog_base import Ui_MainWindow as KFRMDialogBase


class KFRMDialog(QMainWindow, KFRMDialogBase):
    changedVesion = pyqtSignal()

    def __init__(self, loginType, parent=None):
        """Constructor."""
        super(KFRMDialog, self).__init__(parent)
        self.setupUi(self)
        self.setWindowTitle("K-FRM")
        MsTitle("K-FRM Tool", self)

        self.loginType = loginType
        self.project = QgsProject.instance()
        self.txt_lossPath.setText(os.path.join(os.path.expanduser("~"), "Downloads"))

        self.layerLegend = LayerLegendTool(self.tabWidget)
        self.setWidget = SettingWidget(self.trw_menuList, self.loginType)
        if loginType:
            self.pgDao = PgQuery()
        self.sqlConn = SQLiteConnection()
        self.sqliteDao = SqliteQuery(self.sqlConn)
        self.layerTool = LayerTools(self.sqliteDao)
        self.fileImport = FileImport()
        self.fileExport = FileExport()
        self.pathList = DefaultPath()
        self.css = WidgetCss()
        self.error = ErrorConfirm()
        self.canTool = CanvasTools(self.wig_mapCanvas, self.layerLegend)
        self.rtv = RasterToVector()
        self.loading = LoadingDialog("K-FRM Tool")
        self.levelPool = MakeFloodMap()
        self.reqGeo = ReqGeoserver()

        self.widgetSettings()
        self.connecting()
        self.labelMove = QLabelMouseEvent(
            self.lbl_sizeController, self.layerLegend, self.wig_mapCanvas
        )

        self.unionList = {}
        self.pasteFlag = False
        self.btnModifyFlag = True
        self.reclassSaveFlag = False
        self.invenModifyFlag = [False] * 7
        self.tabIndex = [[0, 0] for _ in range(6)]
        self.cmbIndex = [[0, 0] for _ in range(14)]
        self.treeIndex = [0, 0]
        self.resultName = "_result"

    def __del__(self):
        self.project.layerRemoved.disconnect(self.mainCanvasRemoveLayer)

    def closeEvent(self, e):
        try:
            self.sqlConn.closeDB()
            PostgreSQLConnection().closeDB()
        except AttributeError:
            pass

    # ---1. 기본 세팅
    # widget에 대한 connect 이벤트
    def connecting(self):
        self.project.layerRemoved.connect(self.mainCanvasRemoveLayer)

        self.trw_menuList.currentItemChanged.connect(self.treeClickedAction)

        self.tab_buildingValue.currentChanged.connect(
            lambda: self.paramTabChange(
                self.tab_buildingValue, self.btn_buildParamModify, 0
            )
        )
        self.tab_buildVulnerability.currentChanged.connect(
            lambda: self.paramTabChange(
                self.tab_buildVulnerability, self.btn_buildVulModify, 1
            )
        )
        self.tab_vehiValue.currentChanged.connect(
            lambda: self.paramTabChange(self.tab_vehiValue, self.btn_vehiParamModify, 2)
        )
        self.tab_popuVulnerability.currentChanged.connect(
            lambda: self.paramTabChange(
                self.tab_popuVulnerability, self.btn_popuVulModify, 3
            )
        )
        self.tab_agriValuation.currentChanged.connect(self.agriTabChange)
        self.tab_agriVulnerability.currentChanged.connect(
            lambda: self.paramTabChange(
                self.tab_agriVulnerability, self.btn_agriVulModify, 5
            )
        )

        #         self.cmb_reclass.currentIndexChanged.connect(self.reclassFieldChangeEvent)
        self.cmb_costs.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(B_PARAM)
        )
        self.cmb_csvr.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(B_PARAM)
        )
        self.cmb_depreciation.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(B_PARAM)
        )
        self.cmb_entrance.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(B_PARAM)
        )
        self.cmb_buildEvalArea.currentIndexChanged.connect(
            lambda: self.evaluationComboChange("building")
        )
        self.cmb_buildSumType.currentIndexChanged.connect(
            self.buildSummarizeComboChange
        )
        self.cmb_structure.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(B_VUL)
        )
        self.cmb_innerContents.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(B_VUL)
        )
        self.cmb_vehiCosts.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(V_PARAM)
        )
        self.cmb_salvage.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(V_PARAM)
        )
        self.cmb_vehiEvalArea.currentIndexChanged.connect(
            lambda: self.evaluationComboChange("vehicle")
        )
        self.cmb_vehiSumType.currentIndexChanged.connect(self.vehiSummarize)
        self.cmb_vehiVul.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(V_VUL)
        )
        self.cmb_popuLifeVul.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(P_VUL)
        )
        self.cmb_popuVictimVul.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(P_VUL)
        )
        self.cmb_popuParamSgg.currentIndexChanged.connect(
            lambda: self.settingEmdComboBox(0)
        )
        self.cmb_popuParamEmd.currentIndexChanged.connect(
            lambda: self.invenParamViewInfo(0)
        )
        self.cmb_popuSum.currentIndexChanged.connect(self.popuSummarize)
        self.cmb_popuDmgUnitYear.currentIndexChanged.connect(
            lambda: self.invenParamComboChangeSpin(True)
        )
        self.cmb_stanPriceFlow.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(A_PARAM)
        )
        self.cmb_crops.currentIndexChanged.connect(self.agriComboChange)
        self.cmb_cropsFloodPeriod.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(A_VUL)
        )
        self.cmb_farmland.currentIndexChanged.connect(
            lambda: self.invenParamComboChange(A_VUL)
        )
        self.cmb_agriEvalArea.currentIndexChanged.connect(
            lambda: self.evaluationComboChange("agriculture")
        )
        self.cmb_agriSumType.currentIndexChanged.connect(self.agriSummarizeComboChange)
        self.cmb_agriDmgUnitYear.currentIndexChanged.connect(
            lambda: self.invenParamComboChangeSpin(False)
        )
        self.cmb_lossLayer.activated.connect(lambda: self.lossComboChangeEvent(1))
        self.cmb_lossInven.activated.connect(lambda: self.lossComboChangeEvent(2))
        self.cmb_lossType.activated.connect(lambda: self.lossComboChangeEvent(3))
        self.cmb_agriType.activated.connect(lambda: self.lossComboChangeEvent(4))
        self.cmb_LossInfraFlood.currentIndexChanged.connect(
            self.publicSectorComboChange
        )

        self.btn_rasUserData.clicked.connect(
            lambda: self.fileDialog(2, "Open Flood area folder", self.txt_rasUserData)
        )
        self.btn_cancelRaster.clicked.connect(lambda: self.clearHazardContent("raster"))
        self.btn_loadRasUserData.clicked.connect(
            lambda: self.insertUserDataInSQLite("raster")
        )
        self.btn_vecUserData.clicked.connect(
            lambda: self.fileDialog(2, "Open Flood area folder", self.txt_vecUserData)
        )
        self.btn_cancelVector.clicked.connect(lambda: self.clearHazardContent("vector"))
        self.btn_loadVecUserData.clicked.connect(
            lambda: self.insertUserDataInSQLite("vector")
        )
        self.btn_inputBuilding.clicked.connect(
            lambda: self.fileDialog(
                1,
                "Open Building inventory shape file",
                self.lbl_inputBuilding,
                "Shape (*.shp)",
            )
        )
        self.btn_inputPopu.clicked.connect(
            lambda: self.fileDialog(
                1,
                "Open Population inventory shape file",
                self.lbl_inputPopu,
                "Shape (*.shp)",
            )
        )
        self.btn_inputCar.clicked.connect(
            lambda: self.fileDialog(
                1,
                "Open Vehicle inventory shape file",
                self.lbl_inputCar,
                "Shape (*.shp)",
            )
        )
        self.btn_inputFarm.clicked.connect(
            lambda: self.fileDialog(
                1,
                "Open Agriculture inventory shape file",
                self.lbl_inputFarm,
                "Shape (*.shp)",
            )
        )
        self.btn_reclassLoad.clicked.connect(self.distictLoadFieldData)
        self.btn_reclassSave.clicked.connect(self.saveReclassificationData)
        self.btn_proLoadMap.clicked.connect(self.insertAddMapInSQLite)
        self.btn_genLoadMap.clicked.connect(self.insertAddMapInSQLite)
        self.btn_buildParamSave.clicked.connect(
            lambda: self.valueSaveAction(B_PARAM, self.tbl_buildParamCfm, 2)
        )
        self.btn_buildParamExport.clicked.connect(
            lambda: self.valueCsvExportAction(B_PARAM)
        )
        self.btn_buildParamImport.clicked.connect(
            lambda: self.valueCsvImportAction(B_PARAM)
        )
        self.btn_buildParamModify.toggled.connect(
            lambda: self.valueModifyParameters(B_PARAM)
        )
        self.btn_buildEvalRun.clicked.connect(
            lambda: self.evaluationRunModel(
                self.tbl_buildParamCfm,
                self.cmb_buildEvalArea,
                self.cmb_buildSumType,
                "Building",
            )
        )
        self.btn_buildSumExports.clicked.connect(
            lambda: self.valueCsvExportAction(B_SUMRIZ)
        )
        self.btn_buildVulExport.clicked.connect(
            lambda: self.valueCsvExportAction(B_VUL)
        )
        self.btn_buildVulImport.clicked.connect(
            lambda: self.valueCsvImportAction(B_VUL)
        )
        self.btn_buildVulModify.toggled.connect(
            lambda: self.valueModifyParameters(B_VUL)
        )
        self.btn_buildVulSave.clicked.connect(
            lambda: self.valueSaveAction(B_VUL, self.tbl_lossView)
        )
        self.btn_vehiParamExport.clicked.connect(
            lambda: self.valueCsvExportAction(V_PARAM)
        )
        self.btn_vehiParamImport.clicked.connect(
            lambda: self.valueCsvImportAction(V_PARAM)
        )
        self.btn_vehiParamModify.toggled.connect(
            lambda: self.valueModifyParameters(V_PARAM)
        )
        self.btn_vehiParamSave.clicked.connect(
            lambda: self.valueSaveAction(V_PARAM, self.tbl_vehiParamCfm, 2)
        )
        self.btn_vehiSumExport.clicked.connect(
            lambda: self.valueCsvExportAction(V_SUMRIZ)
        )
        self.btn_vehiVulExport.clicked.connect(lambda: self.valueCsvExportAction(V_VUL))
        self.btn_vehiVulImport.clicked.connect(lambda: self.valueCsvImportAction(V_VUL))
        self.btn_vehiVulModify.toggled.connect(
            lambda: self.valueModifyParameters(V_VUL)
        )
        self.btn_vehiVulSave.clicked.connect(
            lambda: self.valueSaveAction(V_VUL, self.tbl_lossView, 2)
        )
        self.btn_vehiEvalRun.clicked.connect(
            lambda: self.evaluationRunModel(
                self.tbl_vehiParamCfm,
                self.cmb_vehiEvalArea,
                self.cmb_vehiSumType,
                "Vehicle",
            )
        )
        self.btn_popuDamageUnit.clicked.connect(self.popuDamageUnitSave)
        self.btn_popuVulExport.clicked.connect(lambda: self.valueCsvExportAction(P_VUL))
        self.btn_popuVulImport.clicked.connect(lambda: self.valueCsvImportAction(P_VUL))
        self.btn_popuVulModify.toggled.connect(
            lambda: self.valueModifyParameters(P_VUL)
        )
        self.btn_popuVulSave.clicked.connect(
            lambda: self.valueSaveAction(P_VUL, self.tbl_lossView, 3)
        )
        self.btn_agriParamExport.clicked.connect(
            lambda: self.valueCsvExportAction(A_PARAM)
        )
        self.btn_agriParamImport.clicked.connect(
            lambda: self.valueCsvImportAction(A_PARAM)
        )
        self.btn_agriParamModify.toggled.connect(
            lambda: self.valueModifyParameters(A_PARAM)
        )
        self.btn_agriParamSave.clicked.connect(
            lambda: self.valueSaveAction(A_PARAM, self.tbl_agriParamCfm, 2)
        )
        self.btn_agriVulExport.clicked.connect(lambda: self.valueCsvExportAction(A_VUL))
        self.btn_agriVulImport.clicked.connect(lambda: self.valueCsvImportAction(A_VUL))
        self.btn_agriVulModify.toggled.connect(
            lambda: self.valueModifyParameters(A_VUL)
        )
        self.btn_agriVulSave.clicked.connect(
            lambda: self.valueSaveAction(A_VUL, self.tbl_lossView, 5)
        )
        self.btn_agriEvalRun.clicked.connect(
            lambda: self.evaluationRunModel(
                self.tbl_agriParamCfm,
                self.cmb_agriEvalArea,
                self.cmb_agriSumType,
                "Agriculture",
            )
        )
        self.btn_agriSumExports.clicked.connect(
            lambda: self.valueCsvExportAction(A_SUMRIZ)
        )
        self.btn_agriDamageUnit.clicked.connect(self.agriDamageUnitSave)
        self.btn_lossRun.clicked.connect(self.lossRunControl)
        self.btn_lossPath.clicked.connect(
            lambda: self.fileDialog(2, "Save result folder", self.txt_lossPath)
        )
        self.btn_lossDownload.clicked.connect(self.lossResultDownload)
        self.btn_lossinfraApply.clicked.connect(self.lossPublicApply)
        self.btn_lossinfraCalc.clicked.connect(self.lossPublicCalc)
        self.btn_annuApply.clicked.connect(self.lossAnnualizedApply)
        self.btn_annuCalc.clicked.connect(self.lossAnnualizedCalc)

        self.btn_pan.clicked.connect(self.canTool.canvasPan)
        self.btn_extent.clicked.connect(self.canTool.zoomToExtent)
        self.btn_identify.clicked.connect(self.canTool.identify)

        self.btn_version.clicked.connect(self.changeVersion)

        self.rdo_ascFile.clicked.connect(self.radioDisableWidgets)
        self.rdo_vecFile.clicked.connect(self.radioDisableWidgets)
        self.rdo_depth.toggled.connect(self.reclassFieldChangeEvent)
        #         self.rdo_code.clicked.connect(self.reclassFieldChangeEvent)

        self.txt_rasUserData.textChanged.connect(
            lambda: self.hazardTableColumnSetting("raster")
        )
        self.txt_vecUserData.textChanged.connect(
            lambda: self.hazardTableColumnSetting("vector")
        )

        # 여기는 임시로 추가한 기능들. (KICT에 기능 추가 말씀은 드렸지만 반응 X. 추후 삭제 or 변경 가능성 있음.)
        self.btn_gridRun.clicked.connect(lambda: self.lossRunControl(True))
        self.btn_gridRun.setDisabled(True)

        self.btn_lineShp.clicked.connect(
            lambda: self.fileDialog(
                1, "Open water level shp file", self.txt_lineShp, "Shape (*.shp)"
            )
        )
        self.btn_lineCsv.clicked.connect(
            lambda: self.fileDialog(
                1, "Open water level csv file", self.txt_lineCsv, "CSV (*.csv)"
            )
        )
        self.btn_dem.clicked.connect(
            lambda: self.fileDialog(
                1,
                "Open dem file",
                self.txt_dem,
                "GeoTiff (*.tif *.tiff);;ASCII (*.asc)",
            )
        )
        self.btn_lpEpWater.clicked.connect(
            lambda: self.fileDialog(
                3,
                "Save water level tif file",
                self.txt_lpEpWater,
                "GeoTiff (*.tif *.tiff)",
            )
        )
        self.btn_lpEpDepth.clicked.connect(
            lambda: self.fileDialog(
                3, "Save depth tif file", self.txt_lpEpDepth, "GeoTiff (*.tif *.tiff)"
            )
        )
        self.btn_lpRun.clicked.connect(self.levelPoolRun)
        self.btn_lpCancel.clicked.connect(lambda: self.clearHazardContent("levelPool"))

    # widget에 대한 기본 세팅 설정 관련
    def widgetSettings(self):
        try:
            # css 조정
            self.setStyleSheet(self.css.setMainCss())
            self.lbl_title.setStyleSheet(self.css.getBoldCss())
            self.grb_vector.setStyleSheet(self.css.setGroupBoxTopMargin())
            self.grb_raster.setStyleSheet(self.css.setGroupBoxTopMargin())
            self.grb_levelPoolExtent.setStyleSheet(self.css.setGroupBoxTopMargin())
            self.grb_reclass.setStyleSheet(self.css.setGroupBoxTopMargin(margin=0))
            self.tabWidget.setStyleSheet(self.css.setTableConerBorder())

            # treeWidget 세팅
            self.trw_menuList.setHeaderHidden(True)
            self.setWidget.treeWidgetSetting()
            self.layerLegend.setHeaderHidden(True)
            self.layerLegend.setContextMenuPolicy(Qt.CustomContextMenu)
            self.layerLegend.setEditTriggers(QAbstractItemView.NoEditTriggers)

            # combo 세팅
            mapYears = self.reqGeo.getContentsToList(DATA_YEARS, "years")
            self.cmb_mapYear.addItems(mapYears)

            self.cmb_agriType.setVisible(False)
            #             self.cmb_reclass.addItems(["Depth (m)", "Code"])
            self.setWidget.comboBoxInit(self.cmb_agriType, ["농작물", "농경지"])
            self.comboList = [
                self.cmb_buildEvalArea,
                self.cmb_buildSumType,
                self.cmb_agriEvalArea,
                self.cmb_agriSumType,
                self.cmb_lossInven,
                self.cmb_lossLayer,
                self.cmb_lossType,
                self.cmb_popuParamSgg,
                self.cmb_popuParamEmd,
                self.cmb_popuSum,
                self.cmb_vehiEvalArea,
                self.cmb_vehiSumType,
                self.cmb_LossInfraFlood,
            ]
            for combo in self.comboList:
                self.setWidget.comboBoxInit(combo)

            # table 세팅
            header = [
                "Flood",
                "Area",
                "Building structure",
                "Building contents",
                "Depreciation parameter",
                "First floor elevations",
            ]
            self.setWidget.tableWidgetInit(
                self.tbl_buildParamCfm, header, verticalFixed=False
            )
            self.tbl_buildParamCfm.setEditTriggers(QAbstractItemView.NoEditTriggers)
            self.tbl_buildParamCfm.setColumnWidth(2, 110)
            self.tbl_buildParamCfm.setColumnWidth(3, 110)
            self.tbl_buildParamCfm.setColumnWidth(4, 150)
            self.tbl_buildParamCfm.setColumnWidth(5, 130)

            header = ["Flood", "Area", "Replacement costs", "Salvage rate"]
            self.setWidget.tableWidgetInit(
                self.tbl_vehiParamCfm, header, verticalFixed=False
            )
            self.tbl_vehiParamCfm.setEditTriggers(QAbstractItemView.NoEditTriggers)
            self.tbl_vehiParamCfm.setColumnWidth(2, 150)
            self.tbl_vehiParamCfm.setColumnWidth(3, 110)

            header = ["Flood", "Area", "Month price flow", "Damage unit"]
            self.setWidget.tableWidgetInit(
                self.tbl_agriParamCfm, header, verticalFixed=False
            )
            self.tbl_agriParamCfm.setEditTriggers(QAbstractItemView.NoEditTriggers)
            self.tbl_agriParamCfm.setColumnWidth(2, 110)
            self.tbl_agriParamCfm.setColumnWidth(3, 110)

            header = ["File path", "Frequency (yr)", "Map name"]
            self.setWidget.tableWidgetInit(
                self.tbl_rasFloodList, header, verticalFixed=False
            )
            self.tbl_rasFloodList.setColumnWidth(0, 500)
            self.tbl_rasFloodList.setColumnWidth(1, 132)
            self.tbl_rasFloodList.setColumnWidth(2, 132)

            header = ["File path", "Frequency (yr)", "Map name"]
            self.setWidget.tableWidgetInit(
                self.tbl_vecFloodList, header, verticalFixed=False
            )
            self.tbl_vecFloodList.setColumnWidth(0, 500)
            self.tbl_vecFloodList.setColumnWidth(1, 132)
            self.tbl_vecFloodList.setColumnWidth(2, 132)

            header = ["Old values", "Depth values"]
            self.setWidget.tableWidgetInit(self.tbl_reclass, header)
            self.tbl_reclass.verticalHeader().hide()
            self.tbl_reclass.setColumnWidth(0, 305)
            self.tbl_reclass.setColumnWidth(0, 305)
            self.tbl_reclass.setColumnWidth(1, 440)

            header = [
                "Building structure",
                "Building content",
                "Vehicle",
                "Population life loss",
                "Population victim loss",
                "Agriculture crops",
                "Farmland",
            ]
            self.setWidget.tableWidgetInit(
                self.tbl_lossView, header, [""], verticalFixed=False
            )
            self.tbl_lossView.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)
            self.tbl_lossView.setEditTriggers(QAbstractItemView.NoEditTriggers)

            header = [
                "Building(Res.)",
                "Building(Non-Res.)",
                "Vehicle",
                "Life",
                "Victim",
                "Crops",
                "Farmland",
                "Sum",
            ]
            self.setWidget.tableWidgetInit(self.tbl_totalResult, header)
            self.tbl_lossView.verticalHeader().setSectionResizeMode(QHeaderView.Stretch)
            self.tbl_lossView.setEditTriggers(QAbstractItemView.NoEditTriggers)

            header = [
                "Flood Scenario",
                "Frequency(yr)",
                "Private Sector\nLoss (1,000KRW)",
                "Public Sector\nLoss (1,000KRW)",
                "Total Flood\nLoss (1,000KRW)",
            ]
            self.setWidget.tableWidgetInit(self.tbl_annualized, header)
            self.tbl_annualized.setEditTriggers(QAbstractItemView.NoEditTriggers)

            header = [
                "Flood Scenario",
                "Frequency(yr)",
                "Total Flood\nLoss(1,000KRW)",
                "△ Probability",
                "Average Loss\n(Interval)",
                "Annual Loss\n(Interval)",
                "Cumulative Loss",
            ]
            self.setWidget.tableWidgetInit(self.tbl_annuResult, header)
            self.tbl_annuResult.setEditTriggers(QAbstractItemView.NoEditTriggers)

            # button 세팅
            btnList = [
                self.btn_buildParamModify,
                self.btn_buildVulModify,
                self.btn_vehiParamModify,
                self.btn_vehiVulModify,
                self.btn_popuVulModify,
                self.btn_agriParamModify,
                self.btn_agriVulModify,
            ]
            for btn in btnList:
                btn.setCheckable(True)

            self.rdo_ascFile.setChecked(True)
            self.rdo_extVec.setChecked(True)
            self.rdo_depth.setChecked(True)
            self.radioDisableWidgets()

            # checkbox 세팅
            self.chk_lossTypeXls.setChecked(True)

            # spinbox 세팅
            spbList = [
                self.spb_mapYear,
                self.spb_popuLifeLoss,
                self.spb_popuVictimLoss,
                self.spb_agriDamageUnit,
                self.spb_LossInfraDmg,
                self.spb_LossInfra,
            ]
            for spb in spbList:
                spb.setButtonSymbols(QAbstractSpinBox.NoButtons)
                spb.setMaximum(9999999999)
                spb.setMinimum(-9999999999)
            self.spb_mapYear.setMinimum(0)

            # groupbox 세팅
            self.grb_reclass.setDisabled(True)

            # lbl 세팅
            self.lbl_mapYear.setText("Map Year : ")
            self.lbl_title.setText("")

        except Warning:
            pass

    # user data를 실행하면 이전에 작업했던 내용을 초기화 함.
    def widgetClear(self, table):
        rowCnt = table.rowCount()
        tblList = [
            self.tbl_buildParamCfm,
            self.tbl_vehiParamCfm,
            self.tbl_agriParamCfm,
            self.tbl_totalResult,
            self.tbl_lossResultView,
            self.tbl_annualized,
            self.tbl_annuResult,
            self.tbl_lossView,
        ]
        rowCntList = ([rowCnt] * 3) + ([0] * 4) + [1]

        for i, tbl in enumerate(tblList):
            self.setWidget.clearNSetTable(tbl, rowCntList[i])

        for combo in self.comboList:
            self.setWidget.comboBoxInit(combo)

        SQLiteConnection().clearDB()
        SQLiteConnection().baseCreateTable()

    # 좌측 하단에 있는 version 변경 버튼. 버전을 변경하면 이전에 작업했던 내용은 모두 초기화
    def changeVersion(self):
        rst = MsQuestion("All previous work will be lost. Do you want to continue?")
        if rst:
            lyrs = self.wig_mapCanvas.layers()  # 레이어가 비활성화 상태일 때 지워지지 않는 현상 있음. 레이어 리스트를 레이어 패널에서 받아야 함.
            for l in lyrs:
                self.project.removeMapLayer(l.id())

            self.close()
            self.changedVesion.emit()

    # 작업했던 레이어를 삭제. 삭제 안하면 qgis background에 레이어가 남아있음.
    def mainCanvasRemoveLayer(self, layerId):
        group = self.layerLegend.model().rootGroup()
        targetLyr = group.findLayer(layerId)
        if targetLyr:
            group.removeLayer(targetLyr.layer())

    # ---2. 실행
    # --- 2-1. 통합(기타 항목)
    # 사용자가 입력 or 선택했던 map year 값을 가져옴. 해당 라벨은 map tab 우상단에 있음.
    def getYear(self):
        mapYear = self.lbl_mapYear.text()[11:]
        return mapYear

    # 프로그램 작업시 사용자에게 띄우는 창. 밑에 QApplication~ 이건 작업 중 ui refresh하는 것이라고 보면 됨.
    def loadingSetText(self, label1="", label2=""):
        if label1:
            self.loading.setLabel(label1, True)
        if label2:
            self.loading.setLabel(label2, False)
        QApplication.processEvents()

    # TreeWidget 목록 선택 시 Stacked 위젯 패널 변경
    def treeClickedAction(self, item):
        if item:
            index = int(item.whatsThis(0))
            title = item.text(0)
            #             if index>0:
            self.stw_changePanel.setCurrentIndex(index)
            self.lbl_title.setText(title)

    # 캔버스에 레이어 띄우기
    def viewCanvasLayer(self, tableName, flag=True, index=1):
        layer = self.layerTool.convertTableToLayer("spatialite", tableName)
        self.canTool.existingRemoveLayer(tableName)
        if flag:
            self.canTool.addLayer(
                layer, index
            )  # 인구, 차량, 농업, 건물 인벤토리 레이어 추가
            self.layerTool.setSingleSymbolRule(layer)
        else:
            self.canTool.setLayer(layer)  # Base map과 침수구역도 SHP 세팅

        return layer

    def fileDialog(self, option, title, txtbox=None, fileType=""):
        if option == 1:  # Open File
            fpath = QFileDialog.getOpenFileName(None, title, "", fileType)[0]
        elif option == 2:  # Open/Save folder
            fpath = QFileDialog.getExistingDirectory(None, title, "")
        elif option == 3:  # save file
            fpath = QFileDialog.getSaveFileName(None, title, "", fileType)[0]

        if txtbox and fpath:
            txtbox.setText("")
            txtbox.setText(str(fpath))
        return fpath

    # --- 2-2. Hazard
    def radioDisableWidgets(self):
        rasFlag = self.rdo_ascFile.isChecked()
        self.grb_raster.setDisabled(not rasFlag)
        self.grb_vector.setDisabled(rasFlag)

    def reclassFieldChangeEvent(self, flag):
        self.grb_reclass.setDisabled(flag)

    # 사용자가 침수구역도 폴더를 선택하면 table에 내용 삽입
    def hazardTableColumnSetting(self, types):
        if types == "raster":
            txt = self.txt_rasUserData.text()
            exten = "asc, tif, txt, out"  # out은 level pool 이야기가 나올때 추가해 달라는 이야기가 있었음.
            table = self.tbl_rasFloodList

        elif types == "vector":
            txt = self.txt_vecUserData.text()
            exten = "shp"
            table = self.tbl_vecFloodList

        if txt and ChFile_Exists(txt):
            fileList = GetSubFolder_List(txt, exten)
            self.setWidget.tableWidgetAddWidgetSetting(table, fileList, vertical=False)

            if self.grb_vector.isEnabled():
                self.cmb_floodField.clear()
                if not table.item(0, 0):
                    return
                filePath = table.item(0, 0).text()
                column = GetShpColumnList(filePath)
                self.cmb_floodField.addItems(column)

    # user data에서 cancel 버튼을 클릭하면 입력되었던 내용 모두 삭제
    def clearHazardContent(self, types):
        if types == "raster":  # raster
            self.txt_rasUserData.setText("")
            self.tbl_rasFloodList.setRowCount(0)
        elif types == "vector":  # vector
            self.txt_vecUserData.setText("")
            self.tbl_vecFloodList.setRowCount(0)
            self.tbl_reclass.setRowCount(0)
            self.cmb_floodField.clear()
            self.rdo_depth.setChecked(True)
            #             self.cmb_reclass.setCurrentIndex(0)
            self.reclassSaveFlag = False
        elif types == "levelPool":
            self.txt_lineShp.setText("")
            self.txt_lineCsv.setText("")
            self.txt_dem.setText("")
            self.txt_lpEpWater.setText("")
            self.txt_lpEpDepth.setText("")
            self.rdo_extVec.setChecked(True)

    # 침수구역도 SHP 타입에서 사용자가 선택한 code 값의 내용을 distinct 함.
    def distictLoadFieldData(self):
        try:
            self.error.confirmComboCount(self.cmb_floodField)
            colName = self.cmb_floodField.currentText()
            rowCnt = self.tbl_vecFloodList.rowCount()
            fields = []
            for i in range(rowCnt):
                filePath = self.tbl_vecFloodList.item(i, 0).text()
                fields += GetShpColumnData(filePath, colName, False)

            fields = sorted(list(set(fields)))
            self.tbl_reclass.setRowCount(len(fields))
            self.setWidget.tableWidgetOneColumnUpdate(self.tbl_reclass, fields, 0)

        except KeyError:
            MsError("Please enter a SHP file with uniform fields.")
        except Exception as e:
            KFRMLogger.exception(e)
            # err_msg = traceback.format_exc()
            MsError(e)

    # code 부분에서 사용자가 입력한 내용 sqlite에 저장
    def saveReclassificationData(self):
        try:
            data = []
            self.error.tableEmptyConfirm(
                self.tbl_reclass, "Please load the code data first."
            )
            row = self.tbl_reclass.rowCount()
            for r in range(row):
                code = self.tbl_reclass.item(r, 0).text()
                value = self.tbl_reclass.item(r, 1)
                self.error.confirmValueData(value)
                min, max = value.text().replace(" ", "").split("-")
                min = min if min else -9999
                max = max if max else -9999
                data.append([r + 1, code, min, max])

            self.sqliteDao.insertTableData(RECLASS_TABLE, data)
            self.reclassSaveFlag = True
            MsInfo("Data saved successfully.")

        except Exception as e:
            KFRMLogger.exception(e)
            # err_msg = traceback.format_exc()
            MsError(e)

    # 침수 구역도 Sqlite에 저장하고 캔버스에 출력하기
    def insertUserDataInSQLite(self, types):
        try:
            KFRMLogger.info(f"insertUserDataInSQLite({types})")
            reclassFlag = self.grb_reclass.isEnabled()
            if types == "raster":
                tbl = self.tbl_rasFloodList
            else:
                tbl = self.tbl_vecFloodList
                if reclassFlag and not self.reclassSaveFlag:
                    raise Warning("No reclassification data was saved.")

            KFRMLogger.debug(f"tbl : {tbl}")
            self.error.tableEmptyConfirm(tbl)
            self.error.notTableOneWidgetConfirm(tbl, 2)
            widgetItem = self.trw_menuList.currentItem()
            KFRMLogger.debug(f"widgetItem : {widgetItem}")
            self.widgetClear(tbl)

            # 테이블에 있는 파일 목록 읽어서 sqlite db에 저장
            row = tbl.rowCount()
            insertList, overlapNames = [], []
            for i in range(row):
                path = tbl.item(i, 0).text()
                if types == "raster":
                    depth = "depth"
                else:
                    depth = self.cmb_floodField.currentText()
                    self.error.columnInList(path, depth)
                spin = tbl.cellWidget(i, 1).value()  # flood frequency
                txt = tbl.cellWidget(i, 2).text().replace(" ", "_").lower()  # comment

                tableName = "(floodmap)" + (f"{spin}yr_" if spin else "") + txt
                overlapNames.append(tableName)
                insertList.append([path, spin, txt, tableName, depth, "", "", ""])
            KFRMLogger.debug(f"overlapNames : {overlapNames}")
            KFRMLogger.debug(f"insertList : {insertList}")
            self.error.listOverlapConfirm(overlapNames)
            rowCnt = tbl.rowCount()

            s = time.time()  # 시간 확인
            maxCount, count = len(insertList), 1
            self.loading.show()
            for path, _, _, tableName, floodColumn, _, _, _ in insertList:
                c = 0 if self.loginType else 1

                if types == "raster":
                    self.loadingSetText(
                        f"raster to vector ({count}/{maxCount})",
                        f"계산에 필요한 파일 생성 중... (1/{6-c})",
                    )
                    rePath = self.rtv.standardization(path, 3, "proc_nodata")
                    tempPath = self.rtv.rasToVec(rePath)
                    self.loadingSetText(label2=f"파일 DB에 저장하는 중... (2/{6-c})")
                    self.fileImport.sqliteImport(tempPath, tableName + "_c")

                    if self.loginType:
                        rename = "convert_zero"
                        self.loadingSetText(label2="Union 작업 중... (3/6)")
                        tempPath = self.rtv.standardization(path, 2, rename)
                        rePath = self.rtv.rasToVec(tempPath, 2, rename, "-8")
                        self.fileImport.sqliteImport(rePath, "(floodmap)" + rename)
                        union, srid, auth, proj, sr = self.sqliteDao.getUnionGeometry(
                            "(floodmap)" + rename
                        )
                        self.unionList[tableName] = union[0][0], [srid, auth, proj, sr]

                    self.loadingSetText(label2=f"침수 재분류 중... ({4-c}/{6-c})")
                    rePath = self.rtv.standardization(path)
                    self.loadingSetText(label2=f"벡터 파일로 변환  중... ({5-c}/{6-c})")
                    path = self.rtv.rasToVec(rePath, 2)

                    self.loadingSetText(
                        label2=f"변환 파일 DB에 저장하는 중... ({6-c}/{6-c})"
                    )
                    value, error = self.fileImport.sqliteImport(path, tableName)

                    if not error:
                        flag = False if count == 1 else True
                        layer = self.viewCanvasLayer(tableName, flag)
                        self.layerTool.setSymbolRule(
                            layer, floodColumn
                        )  # 레이어 색 변경
                    else:
                        raise Warning("Failed to get flood file.")
                # ----------------------------------------- 아래는 vector -----------------------------------------------------
                else:
                    self.loadingSetText(
                        f"insert vector ({count}/{maxCount})",
                        f"파일 DB에 저장하는 중... (1/{3-c})",
                    )
                    value, error = self.fileImport.sqliteImport(path, tableName + "_c")

                    if not error:
                        if self.loginType:
                            self.loadingSetText(label2="Simplify 작업 중... (2/3)")
                            geomInfo = self.sqliteDao.getGeometryInfo(tableName + "_c")
                            geom = self.sqliteDao.getIntersectGeometry(
                                tableName + "_c", self.rtv.getFileLayerSimplify(path)
                            )[0][0]
                            self.unionList[tableName] = (
                                geom,
                                [
                                    geomInfo[4],
                                    geomInfo[6],
                                    geomInfo[8],
                                    geomInfo[7],
                                ],
                            )

                        self.loadingSetText(label2=f"침수 재분류 중... ({3-c}/{3-c})")
                        self.sqliteDao.tableStandardization(
                            tableName + "_c", tableName, floodColumn, reclassFlag
                        )

                        flag = False if count == 1 else True
                        layer = self.viewCanvasLayer(tableName, flag)
                        self.layerTool.setSymbolRule(
                            layer, floodColumn
                        )  # 레이어 색 변경
                    else:
                        raise Warning("Failed to get flood file.")

                count += 1

            print(" : ", time.time() - s)
            self.loading.close()
            self.setWidget.setTreeIconReset()
            self.setWidget.setTreeItemSetIcon(widgetItem)
            self.sqliteDao.insertTableData(LAYER_FLOOD, insertList)

            flag = (
                Qt.ItemIsEnabled | Qt.ItemIsSelectable
                if len(insertList) > 1
                else Qt.NoItemFlags
            )
            self.trw_menuList.topLevelItem(3).child(2).setFlags(flag)

            MsInfo("Hazard import success.")
        except Exception as e:
            self.loading.close()
            KFRMLogger.exception(f"insertUserDataInSQLite : {e}")
            # err_msg = traceback.format_exc()
            MsError(e)

    # quick flood mapping
    def levelPoolRun(self):
        try:
            lineLayer = self.txt_lineShp.text()
            demLayer = self.txt_dem.text()
            csv = self.txt_lineCsv.text()
            waterTif = self.txt_lpEpWater.text()
            depthTif = self.txt_lpEpDepth.text()
            exType = "origin"
            if self.rdo_extVecBuf.isChecked():
                exType = "buffer"
            elif self.rdo_extDem.isChecked():
                exType = "dem"

            pathList = [
                lineLayer,
                demLayer,
                csv,
                os.path.dirname(waterTif),
                os.path.dirname(depthTif),
            ]
            for p in pathList:
                self.error.filePathErrorConfirm(p)
            vecLine = QgsVectorLayer(lineLayer, "Line layer", "ogr")
            self.error.notInSHPField(vecLine, "No")

            self.loading.show()
            self.loadingSetText("수위 격자 파일을 생성하는 중...")
            waterLayer = self.levelPool.runTin(
                lineLayer, demLayer, csv, exType, waterTif
            )

            self.loadingSetText("수심 격자 파일을 생성하는 중...")
            self.levelPool.runCalcDepth(waterLayer, demLayer, depthTif)
            self.loading.close()

            MsInfo("Complete")

        except Exception as e:
            self.loading.close()
            KFRMLogger.exception(f"levelPoolRun : {e}")
            # err_msg = traceback.format_exc()
            MsError(e)

    # --- 2-3. Inventory
    # ---    ┖≫Add Map
    # 침수 구역도와 겹치는 네개의 인벤토리 내용을 sqlite에 저장하고 캔버스에 출력하기
    def insertAddMapInSQLite(self):
        try:
            KFRMLogger.info("Run insertAddMapInSQLite()")
            self.error.canvasEmptyConfirm(self.wig_mapCanvas)
            tableNames = [INV_POPULATION, INV_VEHICLE, INV_FARMLAND, INV_BUILDING]
            floodTables = self.sqliteDao.getTableContents(LAYER_FLOOD, "table_name")
            widgetItem = self.trw_menuList.currentItem()
            start = time.time()

            if self.loginType:
                selectYear = self.cmb_mapYear.currentText()
                self.error.notSelectComboIndex(self.cmb_mapYear, "map year", -1)
                KFRMLogger.debug(f"selectYear : {selectYear}")

                areaList, printList = [], []
                self.loading.show()
                self.loadingSetText(
                    "Flood 지역 매칭",
                    "Flood 지역 위치를 찾는 중 입니다. 잠시만 기다려 주세요...",
                )
                for i, flood in enumerate(floodTables):
                    oneGeom, ref = self.unionList[flood[0]]
                    self.error.noneResultListData(
                        oneGeom,
                        "Geometry values are not available. Please check the file.",
                    )
                    code, area = self.pgDao.getIntersectAreaName(
                        selectYear, oneGeom, ref
                    )
                    areaList.append([flood[0], code, area])
                    printList.append(f"{i}. {flood[0]} : {area}\n")
                self.loading.close()
                KFRMLogger.debug(f"areaList : {areaList}")
                KFRMLogger.debug(f"printList : {printList}")

                # 선택된 지역 이름 확인
                msg = "Selected region List\n" + "".join(printList)
                ret = MsQuestion(msg)
                if not ret:
                    return

                start = time.time()
                confirmArea = []
                layerIndex = len(areaList)
                count = 0
                self.loading.show()
                for flood, code, area in areaList:
                    self.loadingSetText(
                        f"인벤토리와 침수구역도 매칭 중... ({count+1}/{layerIndex})"
                    )
                    for tbl in [
                        self.tbl_buildParamCfm,
                        self.tbl_vehiParamCfm,
                        self.tbl_agriParamCfm,
                    ]:
                        self.setWidget.tableWidgetOneLineUpdate(
                            tbl, count, [flood, area]
                        )

                    count += 1
                    mergeCount = len(confirmArea)
                    if area in confirmArea:
                        merge = self.sqliteDao.getTableContents(
                            LAYER_FLOOD, "area_count", f'WHERE area="{area}"'
                        )
                        self.sqliteDao.updateTableMultiColumns(
                            LAYER_FLOOD,
                            ["area", "sig_cd", "area_count"],
                            [area, code, merge[0][0]],
                            f'table_name="{flood}"',
                        )
                        continue
                    else:
                        self.sqliteDao.updateTableMultiColumns(
                            LAYER_FLOOD,
                            ["area", "sig_cd", "area_count"],
                            [area, code, f"_merge{mergeCount}"],
                            f'table_name="{flood}"',
                        )

                    for name in tableNames:
                        invTypes = name[name.find("inv_") + 4 :]
                        self.loadingSetText(
                            label2=f"{invTypes} 인벤토리 불러오는 중..."
                        )
                        # 인벤토리의 겹치는 구역 정보를 sqlite에 저장하기
                        pgTableName = f"{name}_{selectYear}"
                        saveTableName = f"({invTypes}){selectYear}_merge{mergeCount}"
                        sql = self.pgDao.getAreaSql(pgTableName, code)
                        value, error = self.fileImport.convertPostgisToSqlite(
                            sql, saveTableName
                        )

                        if not error:
                            # geometry 값을 복원(일반테이블 -> 공간테이블)하고 캔버스에 출력하기
                            geomInfo = self.pgDao.getGeometryInfo(pgTableName)
                            geom, srid, type, coord = (
                                geomInfo[3],
                                geomInfo[5],
                                geomInfo[6],
                                geomInfo[4],
                            )
                            self.sqliteDao.recoverGeometry(
                                saveTableName, geom, srid, type, coord
                            )

                            self.viewCanvasLayer(saveTableName, index=layerIndex)

                    # 선택 칼럼을 보여주는 테이블에 지역 이름 세팅 & 매칭 테이블 updqte
                    confirmArea.append(area)

            else:
                selectYear = str(self.spb_mapYear.value())
                fileList = [
                    self.lbl_inputPopu.text(),
                    self.lbl_inputCar.text(),
                    self.lbl_inputFarm.text(),
                    self.lbl_inputBuilding.text(),
                ]

                for path in fileList:
                    self.error.filePathErrorConfirm(path)

                start = time.time()
                self.loading.show()

                areaStr = []
                for i, invPath in enumerate(fileList):
                    self.loadingSetText(f"인벤토리 DB에 넣는중... ({i+1}/4)")
                    invName = f"({tableNames[i].lower()[4:]}){selectYear}"
                    self.fileImport.sqliteImport(invPath, invName)
                    self.viewCanvasLayer(invName, index=len(floodTables))

                areaList = self.sqliteDao.getAreaList(
                    False, f"({tableNames[0].lower()[4:]}){selectYear}"
                )
                for area in areaList:
                    spCnt = 2 if area[0][-1] == "0" else 3
                    areaStr.append(" ".join(area[1].split(" ")[0:spCnt]))
                areaStr = ", ".join(areaStr)

                for i, flood in enumerate(floodTables):
                    self.sqliteDao.updateTableColumns(
                        LAYER_FLOOD, "area", areaStr, f'table_name="{flood[0]}"'
                    )
                    self.setWidget.tableWidgetOneLineUpdate(
                        self.tbl_buildParamCfm, i, [flood[0], areaStr]
                    )
                    self.setWidget.tableWidgetOneLineUpdate(
                        self.tbl_agriParamCfm, i, [flood[0], areaStr]
                    )
                    self.setWidget.tableWidgetOneLineUpdate(
                        self.tbl_vehiParamCfm, i, [flood[0], areaStr]
                    )

            print("Area mapping : ", time.time() - start)

            self.lbl_mapYear.setText("Map year : " + selectYear)
            if self.cmb_costs.count() == 0:
                self.invenParameterSetting()
            self.settingSggComboBox(self.cmb_popuParamSgg, self.cmb_popuSum)
            self.setDefaultAllComboBox()

            self.loading.close()
            self.setWidget.setTreeItemSetIcon(widgetItem)
            MsInfo("Inventory import success.")
        except Exception as e:
            self.loading.close()
            KFRMLogger.exception(f"insertAddMapInSQLite : {e}")
            # err_msg = traceback.format_exc()
            MsError(e)

    # ---    ┖≫공통 항목
    # 현재 사용자가 선택한 tab에 맞춰 값 가져오기 (tab 객체, db table 이름, 버튼 객체 등)
    def valueParamWidget(self, types, userIndex=None):
        parameters = {
            B_PARAM: [
                B_COSTS,
                B_CSVR,
                B_DEPRECIATION,
                B_ENTRACE,
                self.tab_buildingValue,
                self.btn_buildParamModify,
            ],
            B_SUMRIZ: ["evaluation", "summarize", self.tab_buildingEvalSum, None],
            B_VUL: [
                B_SDAMAGE,
                B_IDAMAGE,
                self.tab_buildVulnerability,
                self.btn_buildVulModify,
            ],
            V_PARAM: [V_COSTS, V_SALVAGE, self.tab_vehiValue, self.btn_vehiParamModify],
            V_SUMRIZ: ["evaluation", "summarize", self.tab_vehiViewEvalSum, None],
            V_VUL: [V_VULFUNC, self.tab_vehiVulnerability, self.btn_vehiVulModify],
            P_VUL: [
                P_LIFE,
                P_VICTIM,
                self.tab_popuVulnerability,
                self.btn_popuVulModify,
            ],
            A_PARAM: [A_PRIFLOW, self.tab_agriValuation, self.btn_agriParamModify],
            A_VUL: [
                A_CDAMAGE,
                A_FDAMAGE,
                self.tab_agriVulnerability,
                self.btn_agriVulModify,
            ],
            A_SUMRIZ: ["evaluation", "summarize", self.tab_agricultureEvalSum, None],
        }
        tab = parameters[types][-2]
        tabPanel = tab.currentWidget()
        tabIndex = tab.currentIndex()
        cmb = tabPanel.findChild(QComboBox)
        tbl = tabPanel.findChild(QTableWidget)
        return tabPanel, cmb, tbl, tabIndex, parameters[types]

    # modify 버튼이 눌린 상태에서 tab을 변경할 경우 기존에 수정하던 값을 저장하는지 물어보기 위함
    def paramTabChange(self, tab, btn, index):
        self.tabIndex[index][0] = tab.currentIndex()
        if btn.isChecked() and self.tabIndex[index][1] != self.tabIndex[index][0]:
            self.tabIndex[index][0] = self.tabIndex[index][1]
            tab.setCurrentIndex(self.tabIndex[index][1])
            btn.setChecked(False)

        self.tabIndex[index][1] = self.tabIndex[index][0]

    # 원단위 combobox change event
    def invenParamComboChange(self, types, userIndex=-1):
        _, cmb, tbl, tabIndex, param = self.valueParamWidget(types, userIndex)
        if userIndex >= 0:
            tabIndex = userIndex

        tableName = param[tabIndex]
        btnModify = param[-1]

        index, f, decimalPoint = PARAM_INDEX[types][tableName]
        typeIndex = PARAM_INDEX[types]["INDEX"]

        self.cmbIndex[index][1] = cmb.currentIndex()
        if btnModify.isChecked():
            if self.cmbIndex[index][1] != self.cmbIndex[index][0]:
                cmb.setCurrentIndex(self.cmbIndex[index][0])
                btnModify.setChecked(False)
            if btnModify.text() != "Save":
                self.cmbIndex[index][1] = self.cmbIndex[index][0]
            return

        self.cmbIndex[index][0] = self.cmbIndex[index][1]
        if tableName == A_CDAMAGE and self.cmb_cropsFloodPeriod.count() > 0:
            # 농작물 손상함수(crop) 부분에서 DB에서 값을 읽어와 기간에 대한 comboBox 값이 있을 경우
            cmb = self.cmb_cropsFloodPeriod

        _, filePath = cmb.currentData().split(";")
        if not filePath:
            return
        headerList = TABLE_HEADER_DIC[tableName]
        columns = '"' + '", "'.join(headerList) + '"'

        l = len(headerList)
        fileContent = GetCSVContent_List(filePath)
        self.setWidget.tableWidgetContentsSetting(
            tbl, fileContent[1:], [f, l], headerList, decimal=decimalPoint
        )

        self.setWidget.tableHeaderStretch(tbl)

    # 인구, 농업의 경우 인구 원단위 같은 spin에 표시되는 값 change event
    def invenParamComboChangeSpin(self, typeFlag):
        if typeFlag:
            data = self.cmb_popuDmgUnitYear.currentData()
            if data:
                _, filePath = data.split(";")
                content = GetCSVContent_List(filePath)
                self.spb_popuLifeLoss.setValue(float(content[1][0]))
                self.spb_popuVictimLoss.setValue(float(content[1][1]))
        else:
            data = self.cmb_agriDmgUnitYear.currentData()
            if data:
                _, filePath = data.split(";")
                content = GetCSVContent_List(filePath)
                self.spb_agriDamageUnit.setValue(float(content[1][0]))

    # 원단위 combobox에서 item, filepath 세팅
    def cmbSetData(self, cmb, items, tblName, viewParam):
        for i in items:
            tmpPath = makeTempFile(self.pathList.getTempPath(), ".csv")
            param = f"{viewParam}:{i}" if viewParam else ""
            self.reqGeo.getContentsToCSV(tblName, tmpPath, param)
            cmb.addItem(i, "server;" + tmpPath)

    # geoserver에서 가져온 값 테이블에 세팅
    def invenParameterSetting(self):
        try:
            selectYear = self.getYear()
            allYears = self.reqGeo.getContentsToList(
                DATA_YEARS, "years", "column:params"
            )

            for i, t in enumerate(PARAM_INDEX):
                parameters = self.valueParamWidget(t)[4]
                tab = parameters[-2]

                for j, p in enumerate(parameters[:-2]):
                    tab.setCurrentIndex(j)
                    tabPanel = tab.widget(j)
                    items = ["Default"]
                    viewParam = ""

                    if p in [B_COSTS, V_COSTS, A_PRIFLOW]:
                        items = allYears
                        viewParam = "year"

                    cmb = tabPanel.findChild(QComboBox)
                    self.cmbSetData(cmb, items, p, viewParam)
                tab.setCurrentIndex(0)

            cmbDict = {
                self.cmb_popuDmgUnitYear: P_COSTS,
                self.cmb_agriDmgUnitYear: A_DUNIT,
            }
            for k, v in cmbDict.items():
                self.cmbSetData(k, allYears, v, "year")

        except Exception as e:
            KFRMLogger.exception(f"invenParameterSetting : {e}")
            # err_msg = traceback.format_exc()
            MsError(e)

    # 원단위 combobox 값 기본 세팅
    def setDefaultAllComboBox(self):
        selectYear = self.getYear()

        for i in range(5, 15):
            wig = self.stw_changePanel.widget(i)
            tab = wig.findChild(QTabWidget)
            tabCnt = tab.count()

            for j in range(tabCnt):
                tab.setCurrentIndex(j)
                cmb = tab.widget(j).findChild(QComboBox)
                txt = cmb.itemText(0)
                index = 0

                if txt.isdigit():
                    index = cmb.findText(selectYear)
                    index = index if index >= 0 else 0

                cmb.setCurrentIndex(index)
            tab.setCurrentIndex(0)

    def settingSggComboBox(self, cmbSgg, cmbSum):
        sggList = []
        areaList = self.sqliteDao.getAreaList()
        for area in areaList:
            sp = area[0].split(", ")
            for sgg in sp:
                if sgg not in sggList:
                    sggList.append(sgg)

        self.setWidget.comboBoxInit(cmbSgg, ["전체"] + sorted(sggList))
        self.setWidget.comboBoxInit(cmbSum, ["시군구", "읍면동"])

    def settingEmdComboBox(self, index):
        if index == 0:
            invenType = "population"
            cmbSgg, cmbEmd = self.cmb_popuParamSgg, self.cmb_popuParamEmd

        if cmbSgg.currentIndex() > 0:
            emdList = []
            sgg = cmbSgg.currentText()
            mapYear = self.getYear()

            condition = f"WHERE area LIKE '%{sgg}%' LIMIT 1"
            result = self.sqliteDao.getTableContents(
                LAYER_FLOOD, "area_count", condition
            )

            if result:
                tableName = f"({invenType}){mapYear}{result[0][0]}"
                condition = f"WHERE l_admin LIKE '%{sgg}%' GROUP BY emd_cd"
                areaList = self.sqliteDao.getTableContents(
                    tableName, "l_admin", condition
                )
                emdList = [area[0].split(" ")[2] for area in areaList]
                emdList = list(set(emdList))

            self.setWidget.comboBoxInit(cmbEmd, ["전체"] + emdList)

    # 가치 계산 후 summary 부분에 표시되는 테이블 세팅
    def invenParamViewInfo(self, index):
        result = []
        mapYear = self.getYear()

        if index == 0:
            tbl, comma = self.tbl_popuParam, [3, 6]
            sgg, emd = (
                self.cmb_popuParamSgg.currentText(),
                self.cmb_popuParamEmd.currentText(),
            )
            emdIndex = self.cmb_popuParamEmd.currentIndex()
            invenKind, columns = (
                "population",
                "l_admin, tot_reg_cd, tot_pop_n, tot_vul_n, tot_gen_n",
            )

        if emd == "전체":
            areaList = self.sqliteDao.getAreaList()
            tableName = f"({invenKind}){mapYear}{areaList[0][1]}"
            condition = "" if sgg == "전체" else f"AND l_admin LIKE '{sgg}%'"
            condition = f"WHERE l_admin<>'' {condition}"
            sqls = condition

            for i in range(1, len(areaList)):
                name = f"({invenKind}){mapYear}{areaList[i][1]}"
                sqls += f" UNION SELECT {columns} FROM '{name}' {condition}"
            result = self.sqliteDao.getTableContents(tableName, columns, sqls)

        elif emdIndex > 1:
            condition = f"WHERE area LIKE '%{sgg}%' LIMIT 1"
            areaList = self.sqliteDao.getTableContents(
                LAYER_FLOOD, "area_count", condition
            )
            tableName = f"({invenKind}){mapYear}{areaList[0][0]}"
            sqls = f"WHERE l_admin LIKE '%{sgg} {emd}%'"

            result = self.sqliteDao.getTableContents(tableName, columns, sqls)

        for i in range(len(result)):
            result[i] = list(result[i])  # default = tuple
            admin = result[i][0]
            result[i][0] = " ".join(admin.split(" ")[:2])
            result[i].insert(1, " ".join(admin.split(" ")[2:]))

        columns = ["sig_nm", "emd_nm"] + columns.split(", ")[1:] if result else []
        self.setWidget.tableWidgetContentsSetting(tbl, result, comma, columns)

    # 원단위 테이블에서 수정 버튼 눌렀을 때 event
    def valueModifyParameters(self, types):
        try:
            _, cmb, tbl, tabIndex, param = self.valueParamWidget(types)
            name = param[tabIndex]
            btn = param[-1]
            toggled = btn.isChecked()

            if not self.btnModifyFlag:
                self.btnModifyFlag = True
                return
            if name == A_CDAMAGE and cmb.currentIndex() == 0:
                oriContents = self.readContents(
                    self.cmb_cropsFloodPeriod, tbl, param[tabIndex]
                )
            else:
                oriContents = self.readContents(cmb, tbl, param[tabIndex])

            last = tbl.columnCount()
            index = PARAM_INDEX[types]["INDEX"]
            cmbIndex, first, decimalPoint = PARAM_INDEX[types][name]
            self.error.tableEmptyConfirm(tbl, "There is no data selected.", toggled)

            if toggled:
                self.setWidget.tableWidgetColumnEditable(tbl, first, last, oriContents)
                btn.setText("Save")
                self.invenModifyFlag[index] = True

            elif (not toggled) and (self.invenModifyFlag[index]):
                ret = MsQuestion("Do you want to save your changes?")
                if ret:  # 변경 내용 저장? -- yes
                    gets, savePath = cmb.currentData().split(";")
                    maxCnt = cmb.count()
                    dataColumnindex = cmb.currentIndex()
                    if (
                        gets == "server"
                    ):  # 서버에서 가져온 데이터일때(새로운 임시 파일 생성)
                        savePath = makeTempFile(self.pathList.getTempPath(), ".csv")

                        cnt = 1
                        for i in range(cmb.count()):
                            if "User Defined" in cmb.itemText(i):
                                cnt += 1

                        cmb.addItem(f"User Defined {cnt}", "user;" + savePath)
                        dataColumnindex = maxCnt

                    self.setWidget.tableWidgetChangeNoneToZero(tbl, first, last)
                    self.fileExport.tableExportToCsv(tbl, savePath, first, last)
                    cmb.setCurrentIndex(dataColumnindex)
                    self.setWidget.tableWidgetColumnEditable(
                        tbl, first, last, oriContents, False, decimalPoint
                    )
                    MsInfo("The modified data has been saved.")
                else:
                    self.invenParamComboChange(types)

                self.invenModifyFlag[index] = False
                btn.setText("Modify")

        except Exception as e:
            self.btnModifyFlag = False
            btn.setChecked(False)
            KFRMLogger.exception(f"valueModifyParameters : {e}")
            # err_msg = traceback.format_exc()
            MsError(e)

    # 입력을 완료하면 SQLite DB에 내용 저장
    def readContents(self, cmb, table, tableName):
        contents = None
        data = cmb.currentData()
        if data and data.split(";")[1]:
            contents = GetCSVContent_List(data.split(";")[1])[1:]
        self.error.confirmValueData(
            contents, False, "The selected value could not be found."
        )

        for i, c in enumerate(contents):
            c = list(map(str, list(c)))
            contents[i] = list(map(str.strip, c))

        return contents

    def valueSaveAction(self, types, viewTable, viewCount=0):
        try:
            _, cmb, table, tabIndex, param = self.valueParamWidget(types)
            btnModify = param[-1]
            btnModify.setChecked(False)
            tableName = param[tabIndex]
            widgetItem = self.trw_menuList.currentItem()
            self.error.tableEmptyConfirm(
                table, f"{tableName.replace('_', ' ')} is not selected."
            )

            count = tabIndex + viewCount
            # 테이블에 있는 내용 sqlite에 삽입
            contents = self.readContents(cmb, table, tableName)
            self.error.columnMatchError(
                self.sqliteDao.getTableHeader(tableName), contents[0]
            )
            self.sqliteDao.insertTableData(tableName, contents)

            # evaluation확인 tbl에 cmb 선택 값 넣기
            for i in range(viewTable.rowCount()):
                self.setWidget.tableWidgetOneRowUpdate(
                    viewTable, i, cmb.currentText(), count, count + 1
                )

            if self.sqliteDao.isParamSetting(types):
                self.setWidget.setTreeItemSetIcon(widgetItem)

            MsInfo("Finish setting.")

        except Exception as e:
            KFRMLogger.exception(f"valueSaveAction : {e}")
            if "values were supplied" in str(e):
                for i in range(viewTable.rowCount()):  # 이미 저장되어 있는 값 삭제
                    self.setWidget.tableWidgetOneRowUpdate(
                        viewTable, i, "", count, count + 1
                    )
                self.setWidget.setTreeItemSetIcon("")
                e = "Wrong type of data."
            # err_msg = traceback.format_exc()
            MsError(e)

    def valueCsvExportAction(self, type):
        try:
            cmb, tbl = self.valueParamWidget(type)[1:3]
            self.error.notSelectComboIndex(cmb, f"{type}", -1)
            self.error.tableEmptyConfirm(tbl, f"{type} is not selected.")
            savePath = self.fileDialog(3, f"Export {type}", fileType="CSV (*.csv)")
            if savePath:
                data = cmb.currentData()
                if data and ChFile_Exists(data.split(";")[1]):
                    self.fileExport.copyCsv(data.split(";")[1], savePath)
                else:
                    self.fileExport.tableExportToCsv(tbl, savePath)
                MsInfo("Successful file export.")
        except Exception as e:
            KFRMLogger.exception(f"valueCsvExportAction : {e}")
            # err_msg = traceback.format_exc()
            MsError(e)

    def valueCsvImportAction(self, type):
        try:
            cmb, tbl = self.valueParamWidget(type)[1:3]
            self.error.tableEmptyConfirm(tbl, "Please run 'Add Map' first.")
            filePath = self.fileDialog(1, f"Import {type}", fileType="CSV (*.csv)")
            if filePath:
                cmb.addItem(GetFile_Name(filePath)[:-4], "user;" + filePath)
                cmbIndex = cmb.count() - 1
                cmb.setCurrentIndex(cmbIndex)
        except Exception as e:
            KFRMLogger.exception(f"valueCsvImportAction : {e}")
            # err_msg = traceback.format_exc()
            MsError(e)

    # 인벤토리 가치 계산
    def evaluationRunModel(self, tbl, cmbEval, cmbSum, invKind):
        try:
            start = time.time()
            self.error.notTableSelectetConfirm(tbl)
            mapYear = self.getYear()
            widgetItem = self.trw_menuList.currentItem()

            areaList = self.sqliteDao.getAreaList()
            self.setWidget.comboBoxInit(cmbEval)

            maxCount, count = len(areaList), 1
            self.loading.show()
            tableList = []
            for area, area_count in areaList:
                self.loadingSetText(
                    f"{invKind} 가치 계산하는 중... ({count}/{maxCount})"
                )
                tableName = f"({invKind.lower()}){mapYear}{area_count}"
                valueTableName = tableName + "_eval"
                tableList.append(valueTableName)

                # 자산 가치 계산
                if invKind == "Building":
                    self.sqliteDao.createBuildingValueTable(tableName, mapYear)
                elif invKind == "Vehicle":
                    self.sqliteDao.createVehicleValueTable(tableName, mapYear)
                elif invKind == "Agriculture":
                    self.sqliteDao.createAgricultureValueTable(tableName, mapYear)
                geomInfo = self.sqliteDao.getGeometryInfo(tableName)
                geom, srid, type, coord = (
                    geomInfo[1],
                    geomInfo[4],
                    geomInfo[2],
                    geomInfo[3],
                )
                self.sqliteDao.recoverGeometry(
                    valueTableName, geom, srid, type, coord
                )  # geometry 복구

                # 캔버스에 다시 레이어 뿌리기
                layer = self.layerTool.convertTableToLayer(
                    "spatialite", valueTableName, "", valueTableName
                )
                self.canTool.changeLayer(layer, tableName, valueTableName)
                self.layerTool.setSingleSymbolRule(layer)
                cmbEval.addItem(area)
                count += 1

            self.setWidget.comboBoxInit(cmbSum, ["시군구 별", "읍면동 별"])
            self.loadingSetText(f"{invKind} 가치 집계하는 중...")
            if invKind == "Building":
                self.sqliteDao.createBuildingSumValue(tableList)
            elif invKind == "Vehicle":
                self.sqliteDao.createVehicleSumValue(tableList)
            elif invKind == "Agriculture":
                self.sqliteDao.createAgricultureSumValue(tableList)
            self.loading.close()
            self.setWidget.setTreeItemSetIcon(widgetItem)
            KFRMLogger.info(f"{invKind} evaluation : {time.time() - start}")
            MsInfo("End of evaluation.")
        except Exception as e:
            self.loading.close()
            KFRMLogger.exception(f"evaluationRunModel : {e}")
            # err_msg = traceback.format_exc()
            MsError(e)

    # 가치 계산하는 곳 밑에 있는 테이블의 combobox change event
    def evaluationComboChange(self, invKind):
        varDic = {
            "building": [self.cmb_buildEvalArea, self.tbl_buildEvalResult],
            "vehicle": [self.cmb_vehiEvalArea, self.tbl_vehiEvalResult],
            "agriculture": [self.cmb_agriEvalArea, self.tbl_agriEvalResult],
        }
        cmb, tbl = varDic[invKind]

        contents, headerList = [], []
        if cmb.currentIndex() > 0:
            area = cmb.currentText()
            mapYear = self.getYear()
            mergeCount = self.sqliteDao.getTableContents(
                LAYER_FLOOD, "area_count", f"where area='{area}'"
            )
            tableName = f"({invKind}){mapYear}{mergeCount[0][0]}_eval"
            if self.sqliteDao.isTabled(tableName):
                headerList = self.sqliteDao.getTableHeader(tableName)[1:-1]
                if invKind == "vehicle":
                    headerList = headerList[2:]
                contents = self.sqliteDao.getTableContents(
                    tableName, ",".join(headerList)
                )

        self.setWidget.tableViewContentsSetting(tbl, contents, headerList)

    # ---    ┖≫Buildings
    # Buildings - Evaluation 가치를 계산하여 테이블에 정보 뿌리기
    def buildSummarizeComboChange(self, index):
        if index > 0:
            value = self.sqliteDao.getBuildingSumValue("", index + 1, True)
            contents = value.fetchall()
            headerList = [h[0] for h in value.description]

            self.setWidget.tableWidgetContentsSetting(
                self.tbl_buildSumResult, contents, [2, 6], headerList
            )
        else:
            self.setWidget.tableWidgetInit(self.tbl_buildSumResult)

    # ---    ┖≫Vehicle
    def vehiSummarize(self, index):
        if index > 0:
            result = self.sqliteDao.getVehicleSumValue("", index + 1, True)
            columns = [h[0] for h in result.description]
            result = result.fetchall()

            self.setWidget.tableWidgetContentsSetting(
                self.tbl_vehiSumResult, result, [1, 7], columns
            )
        else:
            self.setWidget.tableWidgetInit(self.tbl_vehiSumResult)

    # ---    ┖≫Population
    def popuViewInfo(self, index):
        sgg = self.cmb_popuParamSgg.currentText()
        emd = self.cmb_popuParamEmd.currentText()
        mapYear = self.getYear()

        columns = "l_admin, tot_reg_cd, tot_pop_n, tot_vul_n, tot_gen_n"
        result = []

        if emd == "전체":
            areaList = self.sqliteDao.getAreaList()
            tableName = f"(population){mapYear}{areaList[0][1]}"
            condition = "" if sgg == "전체" else f"AND l_admin LIKE '{sgg}%'"
            condition = f"WHERE l_admin<>'' {condition}"
            sqls = condition

            for i in range(1, len(areaList)):
                name = f"(population){mapYear}{areaList[i][1]}"
                sqls += f" UNION SELECT {columns} FROM '{name}' {condition}"
            result = self.sqliteDao.getTableContents(tableName, columns, sqls)

        elif index > 1:
            condition = f"WHERE area LIKE '%{sgg}%' LIMIT 1"
            areaList = self.sqliteDao.getTableContents(
                LAYER_FLOOD, "area_count", condition
            )
            tableName = f"(population){mapYear}{areaList[0][0]}"
            sqls = f"WHERE l_admin LIKE '%{sgg} {emd}%'"

            result = self.sqliteDao.getTableContents(tableName, columns, sqls)

        for i in range(len(result)):
            result[i] = list(result[i])  # default = tuple
            admin = result[i][0]
            result[i][0] = " ".join(admin.split(" ")[:2])
            result[i].insert(1, " ".join(admin.split(" ")[2:]))

        columns = ["sig_nm", "emd_nm"] + columns.split(", ")[1:] if result else []
        self.setWidget.tableWidgetContentsSetting(
            self.tbl_popuParam, result, [3, 6], columns
        )

    def popuSummarize(self, index):
        if index > 0:
            mapYear = self.getYear()
            areaList = self.sqliteDao.getAreaList()
            tableNames = ["(population)" + mapYear + area[1] for area in areaList]

            result = self.sqliteDao.getPopulationSumValue(tableNames, index + 1, True)
            columns = [h[0] for h in result.description]
            result = result.fetchall()
            self.setWidget.tableWidgetContentsSetting(
                self.tbl_popuSum, result, [1, 4], columns
            )
        else:
            self.setWidget.tableWidgetInit(self.tbl_popuSum)

    def popuDamageUnitSave(self):
        widgetItem = self.trw_menuList.currentItem()
        lifeLoss = self.spb_popuLifeLoss.value()
        victimLoss = self.spb_popuVictimLoss.value()
        self.sqliteDao.insertTableData(
            P_COSTS, [[self.getYear(), lifeLoss, victimLoss]]
        )

        if self.sqliteDao.isParamSetting(P_PARAM):
            self.setWidget.setTreeItemSetIcon(widgetItem)
        MsInfo("Finish setting.")

    # ---    ┖≫Agriculture
    def agriComboChange(self, index):
        self.cmb_cropsFloodPeriod.clear()
        gets, _ = self.cmb_crops.currentData().split(";")

        if gets == "server":
            allPeriod = ["1~2 days", "3~4 days", "5~6 days", "7~ days"]
            self.cmbSetData(self.cmb_cropsFloodPeriod, allPeriod, A_CDAMAGE, "period")

        elif gets:
            self.invenParamComboChange(A_VUL, 0)

    def agriSummarizeComboChange(self, index):
        if index > 0:
            value = self.sqliteDao.getAgricultureSumValue("", index + 1, True)
            contents = value.fetchall()
            headerList = [h[0] for h in value.description]

            self.setWidget.tableWidgetContentsSetting(
                self.tbl_agriSumResult, contents, [1, len(contents[0])], headerList
            )
        else:
            self.setWidget.tableWidgetInit(self.tbl_agriSumResult)

    def agriDamageUnitSave(self):
        try:
            widgetItem = self.trw_menuList.currentItem()
            saveValue = self.spb_agriDamageUnit.value()
            self.sqliteDao.insertTableData(A_DUNIT, [[saveValue]])
            for i in range(self.tbl_agriParamCfm.rowCount()):
                self.setWidget.tableWidgetOneRowUpdate(
                    self.tbl_agriParamCfm, i, saveValue, 3, 4
                )

            if self.sqliteDao.isParamSetting(A_PARAM):
                self.setWidget.setTreeItemSetIcon(widgetItem)
            MsInfo("Finish setting.")

        except Exception as e:
            KFRMLogger.exception(f"agriDamageUnitSave : {e}")
            for i in range(
                self.tbl_agriParamCfm.rowCount()
            ):  # 이미 저장되어 있는 값 삭제
                self.setWidget.tableWidgetOneRowUpdate(
                    self.tbl_agriParamCfm, i, "", 3, 4
                )
            self.setWidget.setTreeItemSetIcon(widgetItem, "")
            # err_msg = traceback.format_exc()
            MsError(e)

    def agriTabChange(self, index):
        btnList = [
            self.btn_agriParamExport,
            self.btn_agriParamImport,
            self.btn_agriParamModify,
            self.btn_agriParamSave,
        ]
        for btn in btnList:
            btn.setVisible(not index)

        self.paramTabChange(self.tab_agriValuation, btnList[2], 4)

    # --- 2.4 Vulnerability

    # --- 2.5 Loss
    # ---    ┖≫Private Sector Loss
    # 인벤토리 피해액 계산
    def lossRunControl(self, flag=False):
        try:
            self.resultName = "_result"

            # vulnerability 정보 확인
            self.error.notTableSelectetConfirm(self.tbl_lossView)
            self.setWidget.tableWidgetContentsSetting(
                self.tbl_lossResultView, [], headerFlag=True
            )  # 기존 테이블 내용 비우기
            self.setWidget.tableWidgetContentsSetting(self.tbl_totalResult, [])
            floodTableList = self.sqliteDao.getTableContents(
                LAYER_FLOOD, "table_name, area, area_count"
            )
            widgetItem = self.trw_menuList.currentItem()
            self.error.lossNoneDataTables(floodTableList)
            mapYear = self.getYear()

            self.loading.show()
            invenList = ["Building", "Vehicle", "Population", "Agriculture"]

            if (not self.sqliteDao.isParamSetting(P_PARAM)) or (
                not self.sqliteDao.isParamSetting(A_PARAM)
            ):
                raise Warning("All raw data were not selected.")

            for i, inven in enumerate(invenList):
                s = time.time()
                maxCount, count = len(floodTableList), 1

                for flood, area, mergeCount in floodTableList:
                    self.loadingSetText(
                        f"{inven} - {flood} 피해액 계산하는 중... ({count}/{maxCount})"
                    )

                    floodTable = flood if i > 0 else flood + "_c"
                    invenTable = f"({inven.lower()}){mapYear}{mergeCount}" + (
                        "" if i == 2 else "_eval"
                    )

                    if (i != 1) and (not self.sqliteDao.isTabled(invenTable)):
                        raise Warning(
                            f"The {inven.lower()} evaluation process was not performed."
                        )

                    global rstName
                    exec(
                        "global rstName; rstName = self.sqliteDao."
                        + inven.lower()
                        + "LossResult(floodTable, invenTable)"
                    )

                    if flag:
                        wkt = self.sqliteDao.getTableContents(
                            flood, "astext(extent(geometry))"
                        )[0][0]
                        features = self.reqGeo.getContentsToList(
                            BASE_GRID, "", "wkt:" + wkt.replace(",", "\\,")
                        )
                        content = self.reqGeo.dataProcessing(features)
                        rstName = self.sqliteDao.createExtentGrid(
                            content, inven, rstName
                        )
                        self.resultName = "_result_grid"

                    self.viewCanvasLayer(rstName, index=0)
                    count += 1
                    del rstName

                    print(f"loss {inven} : ", time.time() - s)

            self.loadingSetText("결과 집계하는 중...")
            self.sqliteDao.insertInvenInfraSumResult()
            print("sum result : ", time.time() - s)

            tableNames = [names[0] for names in floodTableList]
            self.setWidget.comboBoxInit(self.cmb_lossLayer, tableNames)
            self.setWidget.comboBoxInit(self.cmb_lossInven)
            self.setWidget.comboBoxInit(self.cmb_lossType)

            self.loading.close()
            self.setWidget.setTreeItemSetIcon(widgetItem)

            MsInfo("Finished.")

            # Loss 계산이 완료된 후 자동으로 다운로드 버튼 활성화
            self.btn_lossDownload.setEnabled(True)
        except Exception as e:
            self.loading.close()
            KFRMLogger.exception(f"lossRunControl : {e}")
            # err_msg = traceback.format_exc()
            MsError(e)

    # 인벤토리 피해액 계산 부분 밑 combobox change event
    def lossComboChangeEvent(self, cmbType):
        invenIndex = self.cmb_lossInven.currentIndex()
        invenKind = self.cmb_lossInven.currentText()
        layerIndex = self.cmb_lossLayer.currentIndex()
        typeIndex = self.cmb_lossType.currentIndex()

        global headerList, contents, comma  # exec에서 변수 값을 변경하면 로컬변수로 인식해서 기존의 변수에 값을 덮어씌우는게 아니라 새 변수 값을 할당함.
        contents, headerList = [[]] * 2
        comma = [-1, -1]

        if cmbType == 1:
            invenList = (
                []
                if layerIndex < 1
                else ["Building", "Vehicle", "Population", "Agriculture"]
            )
            self.setWidget.comboBoxInit(self.cmb_lossInven, invenList)
            self.setWidget.comboBoxInit(self.cmb_lossType)
            self.cmb_agriType.setCurrentIndex(0)
            self.cmb_agriType.setVisible(False)

        if cmbType == 2:
            types = []
            self.cmb_agriType.setVisible(False)
            if invenIndex == 1:
                types = ["개별", "시군구", "읍면동"]
            elif invenIndex >= 2:
                types = ["시군구", "읍면동"]

            self.setWidget.comboBoxInit(self.cmb_lossType, types)

        if (
            ((cmbType == 3 and invenIndex != 4) or (cmbType == 4))
            and layerIndex != 0
            and invenIndex > 0
        ):
            tableName = (
                f"({invenKind.lower()})"
                + self.cmb_lossLayer.currentText()[10:]
                + self.resultName
            )
            exec(
                "global headerList, contents, comma; headerList, contents, comma = self.loss"
                + invenKind
                + "Combo(typeIndex, tableName)"
            )
        elif cmbType == 3 and invenIndex == 4:
            self.cmb_agriType.setCurrentIndex(0)
            self.cmb_agriType.setVisible(bool(typeIndex > 0))

        self.setWidget.tableWidgetContentsSetting(
            self.tbl_lossResultView, contents, comma, headerList, True
        )
        self.lossInfraComboChange(layerIndex)
        del headerList, contents, comma

    def lossBuildingCombo(self, typeIndex, tableName):
        headerList, contents, comma = [[]] * 3
        if typeIndex == 1:
            st = 4 if len(self.resultName) > 7 else 3
            headerList = self.sqliteDao.getTableHeader(tableName)[st:-1]
            contents = self.sqliteDao.getTableContents(tableName, ", ".join(headerList))
            comma = [2, len(headerList)]
        elif typeIndex > 1:
            value = self.sqliteDao.getBuildingSumValue(tableName, typeIndex, False)
            headerList, contents, comma = self.lossComboCommon(value, typeIndex)

        return headerList, contents, comma

    def lossPopulationCombo(self, typeIndex, tableName):
        headerList, contents, comma = [[]] * 3
        if typeIndex > 0:
            value = self.sqliteDao.getPopulationSumValue(
                tableName, typeIndex + 1, False
            )
            headerList, contents, comma = self.lossComboCommon(value, typeIndex + 1)

        return headerList, contents, comma

    def lossVehicleCombo(self, typeIndex, tableName):
        headerList, contents, comma = [[]] * 3
        if typeIndex > 0:
            value = self.sqliteDao.getVehicleSumValue(tableName, typeIndex + 1, False)
            headerList, contents, comma = self.lossComboCommon(value, typeIndex + 1)

        return headerList, contents, comma

    def lossAgricultureCombo(self, typeIndex, tableName, agriIndex=0):
        headerList, contents, comma = [[]] * 3
        agriIndex = self.cmb_agriType.currentIndex() if agriIndex < 1 else agriIndex

        if typeIndex > 0 and agriIndex > 0:
            value = self.sqliteDao.getAgricultureSumValue(
                tableName, typeIndex, False, agriIndex
            )
            headerList, contents, comma = self.lossComboCommon(value, typeIndex + 1)

        return headerList, contents, comma

    def lossComboCommon(self, value, typeIndex):
        headerList = [h[0] for h in value.description]
        contents = value.fetchall()
        conCount = len(contents)
        comma = [1, len(headerList)]

        for i in range(conCount):
            contents[i] = list(contents[i])  # default = tuple
            area = contents[i][0].split(" ")
            # 시군구 중에서 수원시 ~구, 수원시 ~구 이런식으로 구분되는 경우가 있음. 이런경우 수원시로 통일
            tyIn = typeIndex + 1 if area[2][-1:] == "구" else typeIndex
            contents[i][0] = " ".join(area[:tyIn])

        return headerList, contents, comma

    def lossResultDownload(self):
        try:
            isShp = self.chk_lossTypeShp.isChecked()
            isXls = self.chk_lossTypeXls.isChecked()
            isSummary = self.chk_lossTypeSummary.isChecked()
            path = self.txt_lossPath.text()

            self.error.filePathErrorConfirm(path)
            self.error.noneSelectedType([isShp, isXls, isSummary])

            self.loading.show()
            if isShp or isXls:
                invenList = ["Building", "Vehicle", "Population", "Agriculture"]
                for i, inven in enumerate(invenList):
                    self.loadingSetText(f"{inven} 내보내는 중... ({i+1}/4)")
                    tableList = self.sqliteDao.getReaultTableList(
                        inven, self.resultName.replace("_result", "")
                    )
                    self.error.noneResultListData(tableList)

                    maxCount, count = len(tableList), 1
                    for table in tableList:
                        if isShp:
                            headers = self.sqliteDao.getTableHeader(table)
                            index = (
                                2 if i == 0 else 11 if i == 1 else 5 if i == 2 else 3
                            )
                            if len(self.resultName) > 7:
                                index += 1

                            self.loadingSetText(
                                label2=f"{table}결과  SHP로 내보내는 중... ({count}/{maxCount})"
                            )
                            savePath = path + f"/{table}.shp"
                            self.fileExport.dataExporToShape(
                                savePath,
                                table,
                                ", ".join(headers[index:]),
                                encoding="CP949",
                            )

                        if isXls:
                            self.loadingSetText(
                                label2=f"{table}결과  XLS로 내보내는 중... ({count}/{maxCount})"
                            )
                            savePath = path + f"/{table}.xls"
                            global shtName, contents  # global 선언 안하면 exec 안에서 로컬 변수로 인식해서 값 전달이 안됨.
                            exec(
                                "global shtName, contents; shtName, contents = self."
                                + inven.lower()
                                + "XlsContents(table)"
                            )
                            self.fileExport.dataExportToExcel(
                                savePath, shtName, contents
                            )
                            del shtName, contents

                        count += 1

            if isSummary:
                self.loadingSetText("요약 결과 집계 내보내는 중...")
                savePath = path + "/SummaryResults.xls"

                # 기능개선 #1: 모든 시나리오의 Summary 결과를 한 번에 다운로드
                # 모든 시나리오(침수구역도) 목록 가져오기
                floodTables = self.sqliteDao.getTableContents(LAYER_FLOOD, "table_name")
                self.error.noneResultListData(floodTables, "No flood tables found.")

                all_summary_contents = []
                sheet_names = []

                # 각 시나리오별로 Summary 데이터 가져오기
                for i, flood_table in enumerate(floodTables):
                    flood_name = flood_table[0]
                    sheet_names.append(flood_name)

                    self.loadingSetText(
                        f"시나리오 {flood_name} 요약 결과 처리 중... ({i+1}/{len(floodTables)})"
                    )

                    # 해당 시나리오의 Summary 데이터 가져오기
                    columns = self.sqliteDao.getTableHeader(F_RESULT)[2:]
                    contents = self.sqliteDao.getTableContents(
                        F_RESULT, ",".join(columns), f'WHERE table_name="{flood_name}"'
                    )

                    # 엑셀 형식으로 변환
                    summary_content = [["Category"] + columns]
                    row_headers = ["Damage amount", "Loss (1,000KRW)"]

                    for i, row in enumerate(contents):
                        if i < len(row_headers):
                            summary_content.append([row_headers[i]] + list(row))

                    all_summary_contents.append(summary_content)

                # 모든 시나리오의 Summary를 하나의 엑셀 파일로 내보내기
                self.fileExport.dataExportToExcel(
                    savePath, sheet_names, all_summary_contents
                )

            self.loading.close()
            MsInfo("Export successfully.")
        except Exception as e:
            self.loading.close()
            KFRMLogger.exception(f"lossResultDownload : {e}")
            # err_msg = traceback.format_exc()
            MsError(e)

    def buildingXlsContents(self, table):
        shtNames = ["전체 결과", "시군구 집계", "읍면동 집계"]
        contents = []
        for i in range(1, 4):
            value = self.lossBuildingCombo(i, table)
            # 0번째 = header (header 값은 1차원 배열이라서 2차원 배열로 변경)
            contents.append([value[0]] + value[1])

        return shtNames, contents

    def populationXlsContents(self, table):
        shtNames = ["시군구 집계", "읍면동 집계"]
        contents = []
        for i in range(1, 3):
            value = self.lossPopulationCombo(i, table)
            contents.append([value[0]] + value[1])

        return shtNames, contents

    def vehicleXlsContents(self, table):
        shtNames = ["시군구 집계", "읍면동 집계"]
        contents = []
        for i in range(1, 3):
            value = self.lossVehicleCombo(i, table)
            contents.append([value[0]] + value[1])

        return shtNames, contents

    def agricultureXlsContents(self, table):
        shtNames = [
            "시군구 집계(농작물)",
            "시군구 집계(농경지)",
            "읍면동 집계(농작물)",
            "읍면동 집계(농경지)",
        ]
        contents = []
        for i in range(1, 3):  # 시군구, 읍면동
            for j in range(1, 3):  # 농경지, 농작물
                value = self.lossAgricultureCombo(i, table, j)
                contents.append([value[0]] + value[1])

        return shtNames, contents

    def lossInfraComboChange(self, index):  # self.tbl_lossInfra
        contents, count = [], 0
        if index > 0:
            #             item = self.cmb_LossInfraFlood.currentText()
            item = self.cmb_lossLayer.currentText()
            columns = self.sqliteDao.getTableHeader(F_RESULT)[2:]
            contents = self.sqliteDao.getTableContents(
                F_RESULT, ",".join(columns), f'WHERE table_name="{item}"'
            )
            #             contents = self.sqliteDao.invenInfraSumResult(item)
            count = len(columns)
        self.setWidget.tableWidgetContentsSetting(
            self.tbl_totalResult, contents, [0, count], decimal=4
        )
        self.tbl_totalResult.setVerticalHeaderLabels(
            ["Damage amount", "Loss (1,000KRW)"]
        )

    # ---    ┖≫Public Sector Loss
    def lossPublicApply(self):
        coe = self.spb_LossInfraDmg.value()
        self.sqliteDao.insertTableData(PUB_LOSS, [[coe]])
        MsInfo("Application completed.")

    def lossPublicCalc(self):
        try:
            tbCoe = self.spb_LossInfraDmg.value()
            dbCoe = self.sqliteDao.getTableContents(PUB_LOSS)
            results = self.sqliteDao.getTableContents(F_RESULT)
            self.error.noneResultListData(
                dbCoe, "The value damage coefficient has not been applied."
            )
            self.error.noneResultListData(
                results, "Please run private sector loss first."
            )
            self.error.listComparison([["", tbCoe]], [("",) + dbCoe[0]])

            widgetItem = self.trw_menuList.currentItem()

            self.sqliteDao.calcPublicLoss()
            annu = self.settingsAnnualizedTable()

            tableNames = [c[0] for c in annu]
            self.setWidget.comboBoxInit(self.cmb_LossInfraFlood, tableNames)
            self.setWidget.setTreeItemSetIcon(widgetItem)

            MsInfo("Finished.")

        except Exception as e:
            KFRMLogger.exception(f"lossPublicCalc : {e}")
            # err_msg = traceback.format_exc()
            MsError(e)

    def publicSectorComboChange(self, index):
        self.spb_LossInfra.setValue(0)
        if index > 0:
            flood = self.cmb_LossInfraFlood.currentText()
            val = self.sqliteDao.getTableContents(
                ANNU_LOSS, "public_sector", f"WHERE flood='{flood}'"
            )[0][0]
            self.spb_LossInfra.setValue(val)

    # ---    ┖≫Annualized Loss
    def settingsAnnualizedTable(self, setTblFlag=True):
        columns = "flood, frequency, private_sector, public_sector, total"
        annu = self.sqliteDao.getTableContents(ANNU_LOSS, columns, "ORDER BY frequency")

        if setTblFlag:
            last = len(annu[0])
            self.setWidget.tableWidgetContentsSetting(
                self.tbl_annualized, annu, [1, last], decimal=0
            )
            self.setWidget.tableChageWidgetItemToSpin(self.tbl_annualized, 1)
            self.setWidget.tableHeaderStretch(self.tbl_annualized)

        return annu

    def getAnnualizedFrequency(self):
        row = self.tbl_annualized.rowCount()
        col = self.tbl_annualized.columnCount()
        values = []

        for r in range(row):
            flood = self.tbl_annualized.item(r, 0).text()
            frequency = self.tbl_annualized.cellWidget(r, 1).value()
            values.append([flood, frequency])

        return values

    def confirmAnnualized(self):
        dbAnnu = self.settingsAnnualizedTable(False)
        self.error.noneResultListData(dbAnnu, "Calculate the public sector first.")
        values = self.getAnnualizedFrequency()

        return [dbAnnu, values]

    def lossAnnualizedApply(self):
        try:
            values = self.confirmAnnualized()[1]

            self.sqliteDao.calcAnuualizedApply(values)
            self.settingsAnnualizedTable()

            MsInfo("Application completed.")

        except Exception as e:
            KFRMLogger.exception(f"lossAnnualizedApply : {e}")
            # err_msg = traceback.format_exc()
            MsError(e)

    def lossAnnualizedCalc(self):
        try:
            dbAnnu, tblAnnu = self.confirmAnnualized()
            self.error.listComparison(dbAnnu, tblAnnu)
            widgetItem = self.trw_menuList.currentItem()

            #             self.loading.show()
            #             self.loadingSetText(f"Infrastructure loss 계산중...")

            self.sqliteDao.calcAnnualizedCalc()

            columns = (
                "flood, frequency, total, probability, average, annual, cumulative"
            )
            annu = self.sqliteDao.getTableContents(
                ANNU_LOSS, columns, "ORDER BY frequency"
            )
            last = len(annu[0])
            self.setWidget.tableWidgetContentsSetting(
                self.tbl_annuResult, annu, [1, last], decimal=4
            )

            self.setWidget.setTreeItemSetIcon(widgetItem)
            #             self.loading.close()

            MsInfo("Finished.")

        except Exception as e:
            #             self.loading.close()
            KFRMLogger.exception(f"lossAnnualizedCalc : {e}")
            # err_msg = traceback.format_exc()
            MsError(e)
