
import os


from qgis.PyQt import uic
from qgis.PyQt.QtWidgets import QMessageBox
from qgis.gui import QgsFileWidget
from qgis.core import QgsVectorLayer
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QDialog, QTableWidgetItem


import common
from urbanq.logging.logging_config import logger
from urbanq.function.qss import qLabel_style
from urbanq.function.security.status import frost_echo


from urbanq.custom.FileInfo import FileInfo, FilePreview


from urbanq.function.widgetutils import (
    show_progress,
    update_progress,
    is_qtable_has_data,
    clear_widget_signals,
    action_widget_signals,
    update_checkbox_signals,
    set_combobox_fill_list_signals,
    set_qlabel_default_value_signals,
    set_combobox_checked_list_signals,
    set_combobox_default_value_signals,
    toggle_ensure_single_checkbox_signals,
    set_combobox_fill_list_set_value_signals,
    update_combobox_with_item_and_block_signals,
)


from urbanq.function.file import (
    extract_most_common_file_type,
    load_json_or_geojson_preview,
    load_layer_or_shp_preview,
    load_txt_or_csv_preview_with_single_encoding,
    load_txt_or_csv_preview_with_multiple_encodings,
)



FORM_CLASS, _ = uic.loadUiType(os.path.join(
    os.path.dirname(__file__), 'fileRread_dockwidget_base.ui'))


class fileRreadDockWidget(QDialog, FORM_CLASS):  
    def __init__(self, parent=None, option=None):
        
        super(fileRreadDockWidget, self).__init__(parent)  
        
        
        
        
        
        self.setupUi(self)

        
        
        
        
        apply_basic_qss = option["apply_basic_qss"]
        show_uid_in_file = option["show_uid_in_file"]
        show_tuid_in_file = option["show_tuid_in_file"]
        show_field_in_file = option["show_field_in_file"]

        
        disable_file_type_layer = option["disable_file_type_layer"]
        disable_file_type_shp = option["disable_file_type_shp"]
        disable_file_type_json = option["disable_file_type_json"]
        disable_file_type_txtcsv = option["disable_file_type_txtcsv"]
        disable_file_type_fold = option["disable_file_type_fold"]

        
        self.file_manager = None

        
        show_progress(self.progressBar, False)

        
        self.shpFileWidget.setFilter("Shapefiles (*.shp)")
        self.jsonFileWidget.setFilter("JSON Files (*.json *.geojson)")
        self.txtcsvFileWidget.setFilter("Text and CSV Files (*.txt *.csv)")
        self.foldFileWidget.setFilter("All Files (*)")
        self.foldFileWidget.setStorageMode(QgsFileWidget.StorageMode.GetDirectory)

        
        for combobox in [
            self.layerIncoding, self.shpIncoding, self.jsonIncoding, self.txtcsvIncoding, self.foldIncoding
        ]:
            set_combobox_fill_list_set_value_signals(combobox, common.korea_encodings)

        
        self.pages_mapping_connect([
            (self.file_type_layer, 0),
            (self.file_type_shp, 1),
            (self.file_type_json, 2),
            (self.file_type_txtcsv, 3),
            (self.file_type_fold, 4),
        ], self.stackedWidget)

        
        self.field_groupbox_connect({
            self.layerfield_all: self.layerfieldGroup,
            self.shpfield_all: self.shpfieldGroup,
            self.jsonfield_all: self.jsonfieldGroup,
            self.txtcsvfield_all: self.txtcsvfieldGroup,
            self.foldfield_all: self.foldfieldGroup,
        })

        
        self.connect_toggle_pairs([
            (self.layerfield_all, self.layerfield_some),
            (self.shpfield_all, self.shpfield_some),
            (self.jsonfield_all, self.jsonfield_some),
            (self.txtcsvfield_all, self.txtcsvfield_some),
            (self.txtcsvHeadCheckBoxY, self.txtcsvHeadCheckBoxN),
            (self.foldfield_all, self.foldfield_some),
            (self.foldHeadCheckBoxY, self.foldHeadCheckBoxN)
        ])

        
        self.apply_qss_to_labels(apply_basic_qss, [
                self.qlabel_4071, self.qlabel_8387, self.qlabel_2788, self.qlabel_1240, self.qlabel_2435,
                self.qlabel_6046, self.qlabel_5249, self.qlabel_8896, self.qlabel_3854, self.qlabel_3176,
                self.qlabel_2146, self.qlabel_6003, self.qlabel_5607, self.qlabel_2568, self.qlabel_3212,
                self.qlabel_4354, self.qlabel_4566, self.qlabel_8913, self.qlabel_2076, self.qlabel_6174,
                self.qlabel_6405, self.qlabel_6742, self.qlabel_1241, self.qlabel_5250, self.qlabel_3177,
                self.qlabel_3213, self.qlabel_6406
            ])

        
        for widget in [
            self.layerfield_all, self.layerfield_some,
            self.shpfield_all, self.shpfield_some,
            self.jsonfield_all, self.jsonfield_some,
            self.txtcsvfield_all, self.txtcsvfield_some,
            self.foldfield_all, self.foldfield_some,

            self.txtcsvHeadCheckBoxY, self.txtcsvHeadCheckBoxN,
            self.foldHeadCheckBoxY, self.foldHeadCheckBoxN,

            self.file_type_json, self.file_type_layer, self.file_type_shp, self.file_type_txtcsv, self.file_type_fold,

            self.qlabel_4842, self.qlabel_5694, self.qlabel_3572, self.qlabel_7313, self.qlabel_4880, self.qlabel_3161,
            self.qlabel_6849, self.qlabel_9881, self.qlabel_5536, self.qlabel_4916, self.qlabel_5824, self.qlabel_9165,
            self.qlabel_5835, self.qlabel_6587, self.qlabel_4710, self.qlabel_2547, self.qlabel_1074, self.qlabel_4673,
            self.qlabel_2396, self.qlabel_2813, self.qlabel_2718, self.qlabel_6133, self.qlabel_2397, self.qlabel_2814,
            self.qlabel_2719, self.qlabel_6134, self.qlabel_4674
        ]:
            widget.setProperty("class", "mediumText")

        for widget in [
            self.qlabel_1073, self.qlabel_4071, self.qlabel_5043, self.qlabel_8387, self.qlabel_2701, self.qlabel_2788,
            self.qlabel_2197, self.qlabel_6046, self.qlabel_4402, self.qlabel_3854, self.qlabel_1488, self.qlabel_6003,
            self.qlabel_3419, self.qlabel_5607, self.qlabel_8761, self.qlabel_2568, self.qlabel_6911, self.qlabel_4566,
            self.qlabel_3542, self.qlabel_8913, self.qlabel_6482, self.qlabel_2076, self.qlabel_7783, self.qlabel_6174,
            self.qlabel_7015, self.qlabel_6742, self.qlabel_9464, self.qlabel_6405, self.qlabel_7892, self.qlabel_2435,
            self.qlabel_2974, self.qlabel_8896, self.qlabel_5909, self.qlabel_2146, self.qlabel_8520, self.qlabel_1240,
            self.qlabel_4755, self.qlabel_5249, self.qlabel_9511, self.qlabel_3176, self.qlabel_7466, self.qlabel_4354,
            self.qlabel_6056, self.qlabel_3212, self.qlabel_1241, self.qlabel_8521, self.qlabel_5250, self.qlabel_4756,
            self.qlabel_3177, self.qlabel_9512, self.qlabel_3213, self.qlabel_6057, self.qlabel_6406, self.qlabel_9465,
        ]:
            widget.setProperty("class", "boldText")

        self.step_group = [
            [self.qlabel_4071, self.qlabel_8387, self.qlabel_2788, self.qlabel_1240, self.qlabel_1241,
             self.qlabel_2435],
            [self.qlabel_4071, self.qlabel_8387, self.qlabel_6046, self.qlabel_5249, self.qlabel_5250,
             self.qlabel_8896],
            [self.qlabel_4071, self.qlabel_8387, self.qlabel_3854, self.qlabel_3176, self.qlabel_3177,
             self.qlabel_2146],
            [self.qlabel_4071, self.qlabel_8387, self.qlabel_6003, self.qlabel_2568, self.qlabel_5607,
             self.qlabel_3212, self.qlabel_3213, self.qlabel_4354],
            [self.qlabel_4071, self.qlabel_8387, self.qlabel_4566, self.qlabel_8913, self.qlabel_2076,
             self.qlabel_6174, self.qlabel_6405, self.qlabel_6406, self.qlabel_6742],
        ]

        
        self.stackedWidget.currentChanged.connect(lambda: self.update_step_labels(self.step_group))

        
        self.label_groups = {
            "FILE_UID": [
                [self.qlabel_8520, self.qlabel_2396, self.qlabel_2398],
                [self.qlabel_4755, self.qlabel_2813, self.qlabel_2815],
                [self.qlabel_9511, self.qlabel_2718, self.qlabel_2720],
                [self.qlabel_6056, self.qlabel_6133, self.qlabel_6135],
                [self.qlabel_9464, self.qlabel_4673, self.qlabel_4675]
            ],
            "FILE_TUID": [
                [self.qlabel_8521, self.qlabel_2397, self.qlabel_2399],
                [self.qlabel_4756, self.qlabel_2814, self.qlabel_2816],
                [self.qlabel_9512, self.qlabel_2719, self.qlabel_2721],
                [self.qlabel_6057, self.qlabel_6134, self.qlabel_6136],
                [self.qlabel_9465, self.qlabel_4674, self.qlabel_4676]
            ],
            "FILE_FIELD": [
                [self.qlabel_7892, self.qlabel_5835, self.qlabel_5836],
                [self.qlabel_2974, self.qlabel_6587, self.qlabel_6588],
                [self.qlabel_5909, self.qlabel_4710, self.qlabel_4711],
                [self.qlabel_7466, self.qlabel_2547, self.qlabel_2548],
                [self.qlabel_7015, self.qlabel_1074, self.qlabel_1075]
            ],
        }
        self.update_label_texts(option, self.label_groups)

        self.input_mapping_layer = {
            "file_type": self.file_type_layer,
            "layer_widget": self.layerComboBox,
            "file_widget": None,
            "indcoding_widget": self.layerIncoding,
            "table_widget": self.layerTable,
            "field_check": [self.layerfield_all, self.layerfield_some],
            "field_widget": self.layerfieldComboBox,
            "uid_widget": self.layerUIDComboBox,
            "tuid_widget": self.layerTUIDComboBox,
            "delimiter_widget": None,
            "header_check": None,
            "path_widget": None,
            "page_widget": None,
            "uid_group": self.layerUIDWidget,
            "tuid_group": self.layerTUIDWidget,
            "field_group": self.layerfieldWidget
        }
        self.input_mapping_shp = {
            "file_type": self.file_type_shp,
            "layer_widget": None,
            "file_widget": self.shpFileWidget,
            "indcoding_widget": self.shpIncoding,
            "table_widget": self.shpTable,
            "field_check": [self.shpfield_all, self.shpfield_some],
            "field_widget": self.shpfieldComboBox,
            "uid_widget": self.shpUIDComboBox,
            "tuid_widget": self.shpTUIDComboBox,
            "delimiter_widget": None,
            "header_check": None,
            "path_widget": None,
            "page_widget": None,
            "uid_group": self.shpUIDWidget,
            "tuid_group": self.shpTUIDWidget,
            "field_group": self.shpfieldWidget
        }
        self.input_mapping_json = {
            "file_type": self.file_type_json,
            "layer_widget": None,
            "file_widget": self.jsonFileWidget,
            "indcoding_widget": self.jsonIncoding,
            "table_widget": self.jsonTable,
            "field_check": [self.jsonfield_all, self.jsonfield_some],
            "field_widget": self.jsonfieldComboBox,
            "uid_widget": self.jsonUIDComboBox,
            "tuid_widget": self.jsonTUIDComboBox,
            "delimiter_widget": None,
            "header_check": None,
            "path_widget": None,
            "page_widget": None,
            "uid_group": self.jsonUIDWidget,
            "tuid_group": self.jsonTUIDWidget,
            "field_group": self.jsonfieldWidget
        }
        self.input_mapping_txtcsv = {
            "file_type": self.file_type_txtcsv,
            "layer_widget": None,
            "file_widget": self.txtcsvFileWidget,
            "indcoding_widget": self.txtcsvIncoding,
            "table_widget": self.txtcsvTable,
            "field_check": [self.txtcsvfield_all, self.txtcsvfield_some],
            "field_widget": self.txtcsvfieldComboBox,
            "uid_widget": self.txtcsvUIDComboBox,
            "tuid_widget": self.txtcsvTUIDComboBox,
            "delimiter_widget": self.txtcsvSplit,
            "header_check": [self.txtcsvHeadCheckBoxY, self.txtcsvHeadCheckBoxN],
            "path_widget": None,
            "page_widget": None,
            "uid_group": self.txtcsvUIDWidget,
            "tuid_group": self.txtcsvTUIDWidget,
            "field_group": self.txtcsvfieldWidget
        }
        self.input_mapping_fold = {
            "file_type": self.file_type_fold,
            "layer_widget": None,
            "file_widget": self.foldFileWidget,
            "indcoding_widget": self.foldIncoding,
            "table_widget": self.foldTable,
            "field_check": [self.foldfield_all, self.foldfield_some],
            "field_widget": self.foldfieldComboBox,
            "uid_widget": self.foldUIDComboBox,
            "tuid_widget": self.foldTUIDComboBox,
            "delimiter_widget": self.foldSplit,
            "header_check": [self.foldHeadCheckBoxY, self.foldHeadCheckBoxN],
            "path_widget": self.foldFilename,
            "page_widget": self.foldNowTableNum,
            "uid_group": self.foldUIDWidget,
            "tuid_group": self.foldTUIDWidget,
            "field_group": self.foldfieldWidget
        }

        
        for widget in [
            self.input_mapping_layer, self.input_mapping_shp, self.input_mapping_json,
            self.input_mapping_txtcsv, self.input_mapping_fold
        ]:
            widget["uid_group"].setVisible(show_uid_in_file)
            widget["tuid_group"].setVisible(show_tuid_in_file)
            widget["field_group"].setVisible(show_field_in_file)

        
        self.file_type_layer.setEnabled(disable_file_type_layer)
        self.file_type_shp.setEnabled(disable_file_type_shp)
        self.file_type_json.setEnabled(disable_file_type_json)
        self.file_type_txtcsv.setEnabled(disable_file_type_txtcsv)
        self.file_type_fold.setEnabled(disable_file_type_fold)

        
        for idx, btn, flag in [
            (1, self.file_type_shp, disable_file_type_shp),
            (0, self.file_type_layer, disable_file_type_layer),
            (2, self.file_type_json, disable_file_type_json),
            (3, self.file_type_txtcsv, disable_file_type_txtcsv),
            (4, self.file_type_fold, disable_file_type_fold),
        ]:
            
            if flag:
                btn.setChecked(True)

                
                
                
                self.stackedWidget.setCurrentIndex(idx)
                break

        
        self.layerfieldComboBox.checkedItemsChanged.connect(
            lambda: self.update_field_selection_order(self.input_mapping_layer))
        self.shpfieldComboBox.checkedItemsChanged.connect(
            lambda: self.update_field_selection_order(self.input_mapping_shp))
        self.jsonfieldComboBox.checkedItemsChanged.connect(
            lambda: self.update_field_selection_order(self.input_mapping_json))
        self.txtcsvfieldComboBox.checkedItemsChanged.connect(
            lambda: self.update_field_selection_order(self.input_mapping_txtcsv))
        self.foldfieldComboBox.checkedItemsChanged.connect(
            lambda: self.update_field_selection_order(self.input_mapping_fold))

        
        if self.file_type_layer.isChecked():
            QTimer.singleShot(0, lambda: self.file_selection(self.input_mapping_layer))

        
        self.file_type_layer.clicked.connect(
            lambda: self.file_selection(self.input_mapping_layer, is_field_update=True))
        self.file_type_shp.clicked.connect(
            lambda: self.file_selection(self.input_mapping_shp, is_field_update=True))
        self.file_type_json.clicked.connect(
            lambda: self.file_selection(self.input_mapping_json, is_field_update=True))
        self.file_type_txtcsv.clicked.connect(
            lambda: self.file_selection(self.input_mapping_txtcsv, is_field_update=True))
        self.file_type_fold.clicked.connect(
            lambda: self.file_selection(self.input_mapping_fold, is_field_update=True))

        
        self.layerComboBox.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_layer)
            if self.stackedWidget.currentIndex() == 0 else None)  
        self.layerIncoding.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_layer, is_data_update=True))
        self.layerUIDComboBox.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_layer, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_layer["table_widget"]) else None)
        self.layerTUIDComboBox.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_layer, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_layer["table_widget"]) else None)
        self.layerfieldComboBox.checkedItemsChanged.connect(
            lambda: self.file_selection(self.input_mapping_layer, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_layer["table_widget"]) else None)
        self.layerfield_all.toggled.connect(
            lambda: self.file_selection(self.input_mapping_layer, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_layer["table_widget"]) else None)

        
        self.shpFileWidget.fileChanged.connect(
            lambda: self.file_selection(self.input_mapping_shp))
        self.shpIncoding.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_shp, is_data_update=True))
        self.shpUIDComboBox.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_shp, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_shp["table_widget"]) else None)
        self.shpTUIDComboBox.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_shp, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_shp["table_widget"]) else None)
        self.shpfieldComboBox.checkedItemsChanged.connect(
            lambda: self.file_selection(self.input_mapping_shp, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_shp["table_widget"]) else None)
        self.shpfield_all.toggled.connect(
            lambda: self.file_selection(self.input_mapping_shp, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_shp["table_widget"]) else None)

        
        self.jsonFileWidget.fileChanged.connect(
            lambda: self.file_selection(self.input_mapping_json))
        self.jsonIncoding.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_json, is_data_update=True))
        self.jsonUIDComboBox.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_json, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_json["table_widget"]) else None)
        self.jsonTUIDComboBox.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_json, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_json["table_widget"]) else None)
        self.jsonfieldComboBox.checkedItemsChanged.connect(
            lambda: self.file_selection(self.input_mapping_json, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_json["table_widget"]) else None)
        self.jsonfield_all.toggled.connect(
            lambda: self.file_selection(self.input_mapping_json, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_json["table_widget"]) else None)

        
        self.txtcsvFileWidget.fileChanged.connect(
            lambda: self.file_selection(self.input_mapping_txtcsv))
        self.txtcsvIncoding.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_txtcsv, is_data_update=True))
        self.txtcsvHeadCheckBoxY.toggled.connect(
            lambda: self.file_selection(self.input_mapping_txtcsv, is_data_update=True)
            if self.if_table_not_empty(self.input_mapping_txtcsv["table_widget"]) else None)
        self.txtcsvSplit.editingFinished.connect(
            lambda: self.file_selection(self.input_mapping_txtcsv, is_data_update=True)
            if self.if_table_not_empty(self.input_mapping_txtcsv["table_widget"]) else None)
        self.txtcsvUIDComboBox.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_txtcsv, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_txtcsv["table_widget"]) else None)
        self.txtcsvTUIDComboBox.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_txtcsv, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_txtcsv["table_widget"]) else None)
        self.txtcsvfieldComboBox.checkedItemsChanged.connect(
            lambda: self.file_selection(self.input_mapping_txtcsv, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_txtcsv["table_widget"]) else None)
        self.txtcsvfield_all.toggled.connect(
            lambda: self.file_selection(self.input_mapping_txtcsv, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_txtcsv["table_widget"]) else None)

        
        self.foldFileWidget.fileChanged.connect(
            lambda: self.file_selection(self.input_mapping_fold))
        self.foldIncoding.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_fold, is_data_update=True))
        self.foldHeadCheckBoxY.toggled.connect(
            lambda: self.file_selection(self.input_mapping_fold, is_data_update=True)
            if self.if_table_not_empty(self.input_mapping_fold["table_widget"]) else None)
        self.foldSplit.editingFinished.connect(
            lambda: self.file_selection(self.input_mapping_fold, is_data_update=True)
            if self.if_table_not_empty(self.input_mapping_fold["table_widget"]) else None)
        self.foldUIDComboBox.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_fold, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_fold["table_widget"]) else None)
        self.foldTUIDComboBox.currentIndexChanged.connect(
            lambda: self.file_selection(self.input_mapping_fold, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_fold["table_widget"]) else None)
        self.foldfieldComboBox.checkedItemsChanged.connect(
            lambda: self.file_selection(self.input_mapping_fold, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_fold["table_widget"]) else None)
        self.foldfield_all.toggled.connect(
            lambda: self.file_selection(self.input_mapping_fold, is_data_update=True, is_field_update=True)
            if self.if_table_not_empty(self.input_mapping_fold["table_widget"]) else None)

        self.foldPreviousTablePushButton.clicked.connect(
            lambda: self.table_change(-1, self.file_manager, self.input_mapping_fold)
            if self.if_table_not_empty(self.input_mapping_fold["table_widget"]) else None)
        self.foldNextTablePushButton.clicked.connect(
            lambda: self.table_change(1, self.file_manager, self.input_mapping_fold)
            if self.if_table_not_empty(self.input_mapping_fold["table_widget"]) else None)

    @staticmethod
    def connect_toggle_pairs(pairs):
        
        for checkbox1, checkbox2 in pairs:
            toggle = lambda checked, cb1, cb2: toggle_ensure_single_checkbox_signals(cb1, cb2)
            checkbox1.toggled.connect(lambda checked, cb1=checkbox1, cb2=checkbox2: toggle(checked, cb1, cb2))
            checkbox2.toggled.connect(lambda checked, cb1=checkbox2, cb2=checkbox1: toggle(checked, cb1, cb2))

    @staticmethod
    def field_groupbox_connect(field_mapping):
        
        toggle_visibility = lambda gb, rb: gb.hide() if rb.isChecked() else gb.show()

        for groupbox in field_mapping.values():
            groupbox.hide()

        for radiobutton, groupbox in field_mapping.items():
            radiobutton.toggled.connect(lambda _, rb=radiobutton, gb=groupbox: toggle_visibility(gb, rb))

    @staticmethod
    def pages_mapping_connect(pages_mapping, stacked_widget):
        
        switch_page = lambda stacked, is_checked, page_index: stacked.setCurrentIndex(page_index) if is_checked else None

        for button, target_page in pages_mapping:
            button.toggled.connect(
                lambda is_checked, page_index=target_page: switch_page(stacked_widget, is_checked, page_index))

    @staticmethod
    def apply_qss_to_labels(apply_basic_qss, qlabel_list):
        
        if apply_basic_qss:
            return

        for qlabel in qlabel_list:
            qlabel.setStyleSheet(qLabel_style)

    @staticmethod
    def update_label_texts(option, label_groups):
        
        def is_nested_list(obj):
            return isinstance(obj, list) and any(isinstance(i, list) for i in obj)

        for key, labels_list in label_groups.items():
            texts = option.get(key, [])  
            if not texts:
                continue  

            if is_nested_list(labels_list):
                for i, labels in enumerate(labels_list):
                    for j, label in enumerate(labels):
                        if j < len(texts) and texts[j] and texts[j].strip():
                            label.setText(texts[j])
            else:
                for i, label in enumerate(labels_list):
                    if i < len(texts) and texts[i] and texts[i].strip():
                        label.setText(texts[i])

    def get_qgs_layer_widget(self):
        
        return self.layerComboBox

    def update_step_labels(self, step_group):
        
        try:
            for group in step_group:
                step = 1
                for widget in group:
                    if widget.isVisibleTo(self):  
                        widget.setText(f" STEP {step} ")
                        step += 1

        except Exception as e:
            logger.error("에러 발생: %s", e, exc_info=True)

    def if_table_not_empty(self, table_widget):
        
        try:
            is_empty = is_qtable_has_data(table_widget)
            if not is_empty:
                QMessageBox.information(self, "파일 오류", "파일을 올바르게 불러오지 못했습니다. 파일을 확인한 후 다시 시도해 주세요.", QMessageBox.Ok)

            return is_empty

        except Exception as e:
            logger.error("에러 발생: %s", e, exc_info=True)
            return False

    def update_field_selection_order(self, input_mapping):
        
        
        field_widget = input_mapping['field_widget']

        
        field_selection = field_widget.checkedItems() if field_widget else []

        
        field_preview_index = self.file_manager.file_preview_index

        
        field_tracker = self.file_manager.file_preview[field_preview_index].field_tracker

        
        for item in field_selection:
            if item not in field_tracker:
                field_tracker.append(item)

        
        field_tracker[:] = [item for item in field_tracker if item in field_selection]

        
        field_widget.setEditText(", ".join(field_tracker))
        self.file_manager.file_preview[field_preview_index].field_tracker = field_tracker

    def table_display(self, table, header, rows):
        
        try:
            table.setColumnCount(len(header))
            table.setRowCount(len(rows))
            table.setHorizontalHeaderLabels(header)

            for row_idx, row in enumerate(rows):
                for col_idx, value in enumerate(row):
                    table.setItem(row_idx, col_idx, QTableWidgetItem(str(value)))

            return True

        except Exception as e:
            logger.error("에러 발생: %s", e, exc_info=True)
            return False

    def table_clear(self, table, path_widget=None, page_widget=None):
        
        try:
            if table:
                clear_widget_signals(table, True)
                table.setRowCount(5)
                table.setColumnCount(5)

            if page_widget:
                page_widget.setText("0/0")

            if path_widget:
                path_widget.setText("선택된 파일이 없습니다.")

        except Exception as e:
            logger.error("에러 발생: %s", e, exc_info=True)

    def table_change(self, direction, file_manager, input_mapping):
        
        try:
            
            indcoding_widget = input_mapping["indcoding_widget"]
            table_widget = input_mapping["table_widget"]
            field_check = input_mapping["field_check"]
            field_widget = input_mapping["field_widget"]
            uid_widget = input_mapping["uid_widget"]
            tuid_widget = input_mapping["tuid_widget"]
            delimiter_widget = input_mapping["delimiter_widget"]
            header_check = input_mapping["header_check"]
            path_widget = input_mapping["path_widget"]
            page_widget = input_mapping["page_widget"]

            
            new_index = file_manager.file_preview_index + direction
            if not (0 <= new_index < len(file_manager.file_preview)):
                return  

            file_manager.file_preview_index = new_index
            file_preview = file_manager.file_preview[new_index]

            
            if not self.table_display(table_widget, file_preview.get_header(), file_preview.get_preview_rows()):
                QMessageBox.information(self, "파일 오류", "유효한 데이터를 추출할 수 없습니다.", QMessageBox.Ok)
                return

            
            if page_widget:
                page_widget.setText(f"{file_manager.file_preview_index + 1}/{len(self.file_manager.file_preview)}")

            
            if path_widget:
                path_widget.setText(file_preview.get_file_name())

            
            if indcoding_widget:
                set_combobox_default_value_signals(indcoding_widget, file_preview.get_file_encoding(), True)

            
            if header_check:
                has_header = file_preview.get_header_check()
                update_checkbox_signals(header_check[0], has_header, True)
                update_checkbox_signals(header_check[1], not has_header, True)

            
            if delimiter_widget:
                set_qlabel_default_value_signals(delimiter_widget, file_preview.get_file_delimiter(), True)

            
            if field_check:
                update_checkbox_signals(field_check[0], not file_preview.get_field_check(), True)
                update_checkbox_signals(field_check[1], file_preview.get_field_check(), True)

            
            if field_widget:
                if file_preview.get_field_check():
                    field_widget.parent().parent().show()
                else:
                    field_widget.parent().parent().hide()

            
            set_combobox_fill_list_signals(field_widget, file_preview.get_header(), True)
            if field_widget:
                set_combobox_checked_list_signals(field_widget, file_preview.get_selection_field(), True)

            
            set_combobox_fill_list_signals(uid_widget, file_preview.get_header(), True)
            if uid_widget:
                set_combobox_default_value_signals(uid_widget, file_preview.get_file_uid(), True)

            
            set_combobox_fill_list_signals(tuid_widget, file_preview.get_header(), True)
            if tuid_widget:
                set_combobox_default_value_signals(tuid_widget, file_preview.get_file_tuid(), True)

        except Exception as e:
            logger.error("에러 발생: %s", e, exc_info=True)

    def file_selection(self, input_mapping, is_data_update=False, is_field_update=False):
        

        
        
        

        
        layer_widget = input_mapping["layer_widget"]
        file_widget = input_mapping["file_widget"]
        indcoding_widget = input_mapping["indcoding_widget"]
        table_widget = input_mapping["table_widget"]
        field_check = input_mapping["field_check"]
        field_widget = input_mapping["field_widget"]
        uid_widget = input_mapping["uid_widget"]
        tuid_widget = input_mapping["tuid_widget"]
        delimiter_widget = input_mapping["delimiter_widget"]
        header_check = input_mapping["header_check"]
        path_widget = input_mapping["path_widget"]
        page_widget = input_mapping["page_widget"]

        try:
            
            show_progress(self.progressBar)

            
            layer = layer_widget.currentLayer() if layer_widget else None
            file_path = file_widget.filePath() if file_widget else None
            file_encoding = indcoding_widget.currentText() if indcoding_widget else None
            file_delimiter = delimiter_widget.text() if delimiter_widget else None
            file_header_check = True if header_check and header_check[0].isChecked() else False
            file_field_check = True if field_check and field_check[1].isChecked() else False
            file_field = None  
            file_uid = uid_widget.currentText() if uid_widget else None
            file_tuid = tuid_widget.currentText() if tuid_widget else None
            
            update_progress(self.progressBar, 35)  

            
            if (not file_path and (not layer or not layer.isValid())) or frost_echo(file_path):

                
                self.file_manager = FileInfo()

                
                self.table_clear(table_widget, path_widget, page_widget)

                
                if indcoding_widget:
                    action_widget_signals(indcoding_widget, lambda w: w.setCurrentIndex(0))

                if header_check:
                    action_widget_signals(header_check[0], lambda w: w.setChecked(True))

                if delimiter_widget:
                    action_widget_signals(delimiter_widget, lambda w: w.setText(","))
                    delimiter_widget.parent().parent().parent().show()

                if uid_widget:
                    action_widget_signals(uid_widget, lambda w: w.clear())

                if tuid_widget:
                    action_widget_signals(tuid_widget, lambda w: w.clear())

                if field_check:
                    action_widget_signals(field_check[0], lambda w: w.setChecked(True))
                    action_widget_signals(field_check[1], lambda w: w.setChecked(False))

                if field_widget:
                    action_widget_signals(field_widget, lambda w: w.clear())
                    field_widget.parent().parent().hide()
                return

            
            if header_check and not file_delimiter:
                delimiter_widget.setText(",")
                file_delimiter = ','

            header, rows = [], []

            
            if not is_data_update:

                
                self.file_manager = FileInfo()

                
                if layer and layer.isValid():
                    result_files = [layer]
                    self.file_manager.file_record.set_record('layer', layer, file_name=layer.name())

                
                elif os.path.isdir(file_path):
                    result_files = extract_most_common_file_type(file_path)
                    self.file_manager.file_record.set_record('folder', file_path)

                
                else:
                    result_files = [file_path]
                    self.file_manager.file_record.set_record(file_path.split('.')[-1].lower(), file_path)

                
                file_type = 'layer' if layer and layer.isValid() else result_files[0].split('.')[-1].lower()

                
                for index, fp in enumerate(result_files):

                    
                    if file_type == 'layer':
                        file_header_check = True
                        header, rows, file_encoding = load_layer_or_shp_preview(layer=fp)
                        fp = fp.source()if os.path.splitext(fp.source())[1].lower() == ".shp" else None

                    elif file_type == 'shp':
                        file_header_check = True
                        header, rows, file_encoding = load_layer_or_shp_preview(file_path=fp)

                    elif file_type == 'json' or file_type == 'geojson':
                        file_header_check = True
                        header, rows, file_encoding, json_or_geojson_type = load_json_or_geojson_preview(fp)

                        
                        
                        
                        if file_type == 'json' and json_or_geojson_type == 'geojson':

                            
                            self.file_manager.file_record.set_record('geojson', file_path)

                    elif file_type == 'csv':
                        file_delimiter = ','  
                        delimiter_widget.parent().parent().parent().hide()  
                        header, rows, file_encoding = load_txt_or_csv_preview_with_multiple_encodings(fp, encoding_list=common.korea_encodings, row_count=5, delimiter=file_delimiter, has_header=file_header_check)

                    elif file_type == 'txt':
                        delimiter_widget.parent().parent().parent().show()  
                        header, rows, file_encoding = load_txt_or_csv_preview_with_multiple_encodings(fp, encoding_list=common.korea_encodings, row_count=5, delimiter=file_delimiter, has_header=file_header_check)

                    update_progress(self.progressBar, 70)

                    
                    if not header or not rows or frost_echo(fp):
                        self.table_clear(table_widget, path_widget, page_widget)
                        clear_widget_signals(field_widget, True)
                        clear_widget_signals(uid_widget, True)
                        clear_widget_signals(tuid_widget, True)
                        return

                    
                    update_combobox_with_item_and_block_signals(indcoding_widget, file_encoding, True)
                    file_uid = header[0] if header and len(header) > 0 else file_uid  
                    file_tuid = header[0] if header and len(header) > 0 else file_tuid  
                    file_preview = FilePreview(fp, file_encoding, file_delimiter, file_header_check, header, rows, False, [], file_uid, file_tuid)  

                    
                    self.file_manager.file_preview.append(file_preview)

                    update_progress(self.progressBar, 100)

            else:
                
                
                preview_index = self.file_manager.file_preview_index

                file_field = self.file_manager.file_preview[preview_index].field_tracker

                
                if is_field_update:
                    self.file_manager.file_preview[preview_index].set_field_check(file_field_check)
                    self.file_manager.file_preview[preview_index].set_selection_field(file_field)
                    self.file_manager.file_preview[preview_index].set_file_uid(file_uid)
                    self.file_manager.file_preview[preview_index].set_file_tuid(file_tuid)

                else:
                    
                    file_path = self.file_manager.file_preview[preview_index].file_path   

                    
                    file_type = self.file_manager.get_preview_file_type(preview_index)

                    
                    if file_type == 'layer':
                        file_header_check = True
                        header, rows, file_encoding = load_layer_or_shp_preview(layer=layer, file_encoding=file_encoding)

                    elif file_type == 'shp':
                        file_header_check = True
                        header, rows, file_encoding = load_layer_or_shp_preview(file_path=file_path, file_encoding=file_encoding)

                    elif file_type == 'json' or file_type == 'geojson':
                        file_header_check = True
                        header, rows, file_encoding, json_or_geojson_type = load_json_or_geojson_preview(file_path, file_encoding=file_encoding)

                        
                        
                        
                        if file_type == 'json' and json_or_geojson_type == 'geojson':

                            
                            self.file_manager.file_record.set_record('geojson', file_path)

                    elif file_type == 'csv':
                        file_delimiter = ','  
                        delimiter_widget.parent().parent().parent().hide()  
                        header, rows, file_encoding = load_txt_or_csv_preview_with_single_encoding(file_path, encoding=file_encoding, row_count=5, delimiter=file_delimiter, has_header=file_header_check)

                    elif file_type == 'txt':
                        delimiter_widget.parent().parent().parent().show()  
                        header, rows, file_encoding = load_txt_or_csv_preview_with_single_encoding(file_path, encoding=file_encoding, row_count=5, delimiter=file_delimiter, has_header=file_header_check)

                    update_progress(self.progressBar, 70)  

                    
                    if not header or not rows or frost_echo(file_path):
                        self.table_clear(table_widget)
                        clear_widget_signals(field_widget, True)
                        clear_widget_signals(uid_widget, True)
                        clear_widget_signals(tuid_widget, True)
                        return

                    
                    file_field = list(set(file_field) & set(header))  
                    update_combobox_with_item_and_block_signals(indcoding_widget, file_encoding, True)  
                    file_uid = header[0] if file_uid not in header else file_uid   
                    file_tuid = header[0] if file_tuid not in header else file_tuid   
                    file_preview = FilePreview(file_path, file_encoding, file_delimiter, file_header_check, header, rows, file_field_check, file_field, file_uid, file_tuid)  

                    
                    self.file_manager.file_preview[preview_index] = file_preview

                    update_progress(self.progressBar, 100)

            
            self.table_change(0, self.file_manager, input_mapping)

        except Exception as e:
            self.table_clear(table_widget, path_widget, page_widget)
            QMessageBox.information(self, "파일 오류", "파일을 가져오는 중 오류가 발생하였습니다.", QMessageBox.Ok)
            logger.error("에러 발생: %s", e, exc_info=True)

        finally:
            
            show_progress(self.progressBar, False)

    def set_fileResults(self, num=1):
        
        try:
            
            if not self.file_manager or not self.file_manager.file_record.get_record()[1]:
                QMessageBox.information(self, "파일 오류", "파일 또는 레이어를 선택하세요.", QMessageBox.Ok)
                return False

            
            file_types = [
                self.input_mapping_layer,
                self.input_mapping_shp,
                self.input_mapping_json,
                self.input_mapping_txtcsv,
                self.input_mapping_fold
            ]
            for file_type in file_types:
                if file_type["file_type"].isChecked() and not self.if_table_not_empty(file_type["table_widget"]):
                    return False

            
            file_type, file_path, file_name = self.file_manager.file_record.get_record()
            for index, fp in enumerate(self.file_manager.file_preview):
                file_encoding = fp.get_file_encoding()
                file_delimiter = fp.get_file_delimiter()
                field_check = fp.get_field_check()
                field_selection = fp.get_selection_field()
                file_uid = fp.get_file_uid()
                file_tuid = fp.get_file_tuid()

                title = f'{index+1}/{len(self.file_manager.file_preview)} 파일의' if len(self.file_manager.file_preview) > 1 else ''

                if not file_encoding:
                    QMessageBox.information(self, "필드 인코딩 설정", f"{title}'파일 인코딩 설정'을 선택한 후 다시 시도해 주세요.", QMessageBox.Ok)
                    return False

                if not file_uid:
                    QMessageBox.information(self, "필드 UID 설정", f"{title}'UID 필드 설정'을 선택한 후 다시 시도해 주세요.", QMessageBox.Ok)
                    return False

                if not file_tuid:
                    QMessageBox.information(self, "작업 대상 필드 설정", f"{title}'작업 대상 필드'를 선택한 후 다시 시도해 주세요.", QMessageBox.Ok)
                    return False

                if field_check and not field_selection:
                    QMessageBox.information(self, "필드 범위 설정", f"{title}'필드 선택 및 범위 설정'을 선택한 후 다시 시도해 주세요.", QMessageBox.Ok)
                    return False

                if file_type == 'txt' or file_type == 'csv':
                    if not file_delimiter:
                        QMessageBox.information(self, "텍스트 구분자 설정", f"{title}'텍스트 구분자 설정'을 입력 후 다시 시도해 주세요. (예들 들어: ',')", QMessageBox.Ok)
                        return False

            
            if num == 1:
                common.fileInfo_1 = self.file_manager
            else:
                common.fileInfo_2 = self.file_manager

            
            common.fileInfo_1.result_table = {"header": None, "rows": None}
            common.signals.file_preview_updated.emit()

            

            return True

        except Exception as e:
            logger.error("에러 발생: %s", e, exc_info=True)
            return False




