import xml.etree.ElementTree as ET 
from   logging             import getLogger, config, DEBUG, INFO, WARN, ERROR, CRITICAL
import csvExcelConst
import os
import sys
import errno
import json
import configparser

class csv_excel_import_xml:
    class csv_excel_import_xml_error(FileNotFoundError):
        pass

    def __init__(self, log):
        
        # =================================
        # 構成ファイルの読込み
        # ---------------------------------
        execute_dir = os.path.dirname(__file__)
        config_ini      = configparser.ConfigParser()
        config_ini_path = f"{execute_dir}/config.ini"
        if  not os.path.exists(config_ini_path):
            print(f"File Not Found = {config_ini_path}")
            raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), config_ini)
    
        config_ini.read(config_ini_path, encoding='utf-8')

        #! conf_default  = config_ini['DEFAULT']
        #! log_conf_file = conf_default['LOG_CONF']
        # =================================
        # Logger 設定
        # ---------------------------------
        #! with open(log_conf_file, "r") as f:
        #!    log_conf = json.load(f)
        #!    config.dictConfig(log_conf)
        #!     _EXEC_FILE_NAME = os.path.basename(__file__)[:-3]

        #! self.__log = getLogger(_EXEC_FILE_NAME)
        self.__log = log

    @property
    def log(self):
        return self.__log
    # ==========================================
    # <bnd>の情報を取得
    # ------------------------------------------
    def get_bnd_data(self, element) -> dict:

        dict_bnd     = {}
        dict_bnd_bnd = {}
        # <csvBind/*>
        for child_element in element:
            child_tag = child_element.tag
            
            self.log.log(DEBUG, f"bnd child Tag = {child_tag}")

            # <csvBind/outputFIle>
            if  child_tag.casefold() == csvExcelConst.BND_OUTPUT_FILE.casefold():
                child_val = child_element.text.strip()
                dict_bnd[child_tag] = child_val

            # <csvBind/mail>
            if  child_tag.casefold() == csvExcelConst.MAIL.casefold():
                dict_mail = self.get_def_mail(child_element)
                dict_bnd[csvExcelConst.MAIL]    =   dict_mail

            # <csvBind/ftp>
            if  child_tag.casefold() == csvExcelConst.FTP.casefold():
                dict_ftp = self.get_def_ftp(child_element)
                dict_bnd[csvExcelConst.FTP]    =   dict_ftp

            # <csvBind/pivot>
            if  child_tag.casefold() == csvExcelConst.PIVOT.casefold():
                dict_pivot = self.get_def_pivot(child_element)
                dict_bnd[csvExcelConst.PIVOT]   =   dict_pivot

            # <csvBind/graph>
            if  child_tag.casefold() == csvExcelConst.GRAPH.casefold():
                dict_graph = self.get_def_graph(child_element)
                dict_bnd[csvExcelConst.GRAPH]   =   dict_graph

            # <csvBind/bind>
            if  child_tag.casefold() == csvExcelConst.BND_BIND.casefold():
                
                bnd_num      = 0
                # <csvBind/bind/*>
                for bnd_element in child_element:
                    dict_bnd_mem = {}
                
                    bnd_tag = bnd_element.tag
                    self.log.log(DEBUG, f"bingTag = {bnd_tag}")
                
                    for bnd_attr in bnd_element.attrib:                         # <csvBind>の属性のキー名を取得する
                        bnd_attr_name = bnd_attr
                        bnd_attr_val  = bnd_element.get(bnd_attr_name).strip()  # <csvBind>の属性キー名から属性値を取得する

                        self.log.log(DEBUG, f"bnd Attr Name = {bnd_attr_name}, Value = {bnd_attr_val}")

                        if  bnd_attr_name.casefold() in [csvExcelConst.BND_CSV_FILE.casefold(), csvExcelConst.BND_SHEET_NAME.casefold()]:
                            dict_bnd_mem[bnd_attr_name] = bnd_attr_val
                    mem_key = "{:03}".format(bnd_num)
                    dict_bnd_bnd[mem_key] = dict_bnd_mem
                    bnd_num += 1

        dict_bnd[csvExcelConst.BND_BIND] = dict_bnd_bnd

        self.log.log(DEBUG, dict_bnd)

        return dict_bnd
    
    # ==========================================
    # <file>の情報を取得
    # ------------------------------------------
    def get_def_head(self, element) -> dict:
        
        dict_head = {}
        # ==========================================
        # <file>の属性を取得
        # ------------------------------------------
        for attr in element.attrib:
            attr_name = attr                                # <csvBind/file>の属性キー名を取得する
            attr_val  = element.get(attr_name).strip()      # <csvBind/file>の属性値を取得する
            self.log.log(DEBUG, f"attr name={attr_name}, Value={attr_val}")
            
            if  attr_name.casefold() in [csvExcelConst.MAX_ROWS.casefold(), csvExcelConst.MAX_COLS.casefold()]:
                dict_head[attr_name] = attr_val
            
        # ==========================================
        # <file>の子ノードを取得
        # ------------------------------------------
        for head_child in element:
            head_tag_name = head_child.tag
            head_tag_val  = head_child.text.strip()
            self.log.log(DEBUG, f"head tag = {head_tag_name}, value={head_tag_val}")

            if head_tag_name.casefold() in [csvExcelConst.DETAIL_NAME.casefold(),       csvExcelConst.USER_ID.casefold(),
                                            csvExcelConst.USER_NAME.casefold(),         csvExcelConst.REQUEST_TIME.casefold(),
                                            csvExcelConst.TABLE_STYLE.casefold(),       csvExcelConst.TABLE_NAME.casefold(),
                                            csvExcelConst.FONT_NAME.casefold(),         csvExcelConst.FONT_SIZE.casefold(),
                                            csvExcelConst.OUTPUT_FILE.casefold()]:
                dict_head[head_tag_name] = head_tag_val
               
        self.log.log(DEBUG, f"dict_head = {dict_head}")
        return dict_head

    # ==========================================
    # <columns>の情報を取得
    # ------------------------------------------
    def get_def_item(self, element) -> dict:

        dict_item = {}
        dict_item_item = {}
        
        # ==========================================
        # <columns>の属性を取得
        # ------------------------------------------
        for attr in element.attrib:
            attr_name = attr
            attr_val  = element.get(attr_name).strip()
            self.log.log(DEBUG, f"attr name={attr_name}, Value={attr_val}")
            
            if  attr_name.casefold() in [csvExcelConst.DEFAULT_TYPE.casefold(),         csvExcelConst.CSV_HEADER.casefold(),
                                         csvExcelConst.WINDOW_LOCK_ROW.casefold(),      csvExcelConst.WINDOW_LOCK_COL.casefold()]:
                dict_item[attr_name] = attr_val

        # ==========================================
        # <columns/*>の子ノードを取得
        # ------------------------------------------
        for item_child in element:

            dict_item_member = {}
            #! item_tag_name    = item_child.tag
            #! self.log.log(DEBUG, f"item tag = {item_tag_name}")

            # ==========================================
            # <column>の属性を取得
            # ------------------------------------------
            for attr in item_child.attrib:
                attr_name = attr
                attr_val  = item_child.get(attr_name).strip()
                #! self.log.log(DEBUG, f"attr name={attr_name}, Value={attr_val}")
                
                if  attr_name.casefold() in [csvExcelConst.COL_NO.casefold(),       csvExcelConst.DATA_TYPE.casefold(),
                                             csvExcelConst.EDIT_FORMAT.casefold()]:
                    dict_item_member[attr_name] = attr_val

            if  dict_item_member.get(csvExcelConst.COL_NO) != None:
                col_no = dict_item_member[csvExcelConst.COL_NO]
                dict_item_item[col_no] = dict_item_member

        self.log.log(DEBUG, f"dict_item = {dict_item}")
        dict_item[csvExcelConst.COLS] = dict_item_item
        return dict_item
    # ==========================================
    # <pivot>の情報を取得
    # ------------------------------------------
    def get_def_pivot(self, element) -> dict:
        dict_pivot = {}

        for attr in element.attrib:
            attr_name = attr
            attr_val  = element.get(attr_name).strip()

            self.log.log(DEBUG, f"attr name={attr_name}, Value={attr_val}")
            if  attr_name.casefold() in [csvExcelConst.PIVOT_SHEET_NAME.casefold(), csvExcelConst.PIVOT_COLUMN_X.casefold(), 
                                         csvExcelConst.PIVOT_COLUMN_Y.casefold(),   csvExcelConst.PIVOT_COLUMN_SUM.casefold(),
                                         csvExcelConst.PIVOT_TYPE.casefold(),       csvExcelConst.PIVOT_TOTAL.casefold(),
                                         csvExcelConst.PIVOT_TOTAL_NAME.casefold(), csvExcelConst.PIVOT_CUMULATIVE_SUM.casefold()]:
                dict_pivot[attr_name]   =   attr_val
            
        dict_convert = []

        #! <pivot/*>
        for element_convert in element:
            
            self.log.log(DEBUG, f"convertYM Node = {element_convert.tag}")
            if  element_convert.tag.casefold() != csvExcelConst.PIVOT_CONVERT_YM.casefold():
                continue
            
            #! <pivot/convertYM>
            dict_convert_mem = {}

            for attr in element_convert.attrib:
                attr_name = attr
                attr_val  = element_convert.get(attr_name).strip()

                if  attr_name.casefold() in [csvExcelConst.PIVOT_CONVERT_YM_SRC.casefold(), csvExcelConst.PIVOT_CONVERT_YM_TARGET.casefold()]:
                    dict_convert_mem[attr_name] = attr_val

            dict_convert.append(dict_convert_mem)
        
        dict_pivot[csvExcelConst.PIVOT_CONVERT_YM]  =   dict_convert

        self.log.log(DEBUG, dict_pivot)
        return dict_pivot
    
    # ==========================================
    # <graph>の情報を取得
    # ------------------------------------------
    def get_def_graph(self, element) -> dict:
        dict_graph = {}

        for attr in element.attrib:
            attr_name   =   attr
            attr_val    =   element.get(attr_name).strip()

            self.log.log(DEBUG, f"attr name={attr_name}, Value={attr_val}")
            if  attr_name.casefold() in [csvExcelConst.GRAPH_DATA.casefold(),           csvExcelConst.GRAPH_TYPE.casefold(),        
                                         csvExcelConst.GRAPH_SHEET_NAME.casefold(),     csvExcelConst.GRAPH_POSITION.casefold(),
                                         csvExcelConst.GRAPH_GROUP.casefold(),          csvExcelConst.GRAPH_STYLE.casefold(),
                                         csvExcelConst.GRAPH_X_SIZE.casefold(),         csvExcelConst.GRAPH_Y_SIZE.casefold(),
                                         csvExcelConst.GRAPH_X_UNITS.casefold(),        csvExcelConst.GRAPH_Y_UNITS.casefold(),
                                         csvExcelConst.GRAPH_TITLE.casefold(),          csvExcelConst.GRAPH_X_AXIS.casefold(),
                                         csvExcelConst.GRAPH_Y_AXIS.casefold()]:
                dict_graph[attr_name]   =   attr_val
        
        self.log.log(DEBUG, dict_graph)
        return dict_graph
    
    # ==========================================
    # <mail>の情報を取得
    # ------------------------------------------
    def get_def_mail(self, element) -> dict:
        dict_mail = {}
        self.log.log(DEBUG, f"def mail = {element.tag}")
        for mail_child in element:
            tag_name   =   mail_child.tag
            
            if  tag_name.casefold() in [csvExcelConst.MAIL_REQUEST_TIME.casefold(),     csvExcelConst.MAIL_TO_ADDRESS.casefold(),
                                        csvExcelConst.MAIL_CC_ADDRESS.casefold(),       csvExcelConst.MAIL_BCC_ADDRESS.casefold(),
                                        csvExcelConst.MAIL_SUBJECT.casefold()]:
                self.log.log(DEBUG, f"mail tag name = {tag_name}")
                tag_val =  ""
                if  mail_child.text is not None:
                    tag_val = mail_child.text.strip()
                
                dict_mail[tag_name]     =   tag_val 
                        
        self.log.log(DEBUG, f"dict mail = {dict_mail}")

        return dict_mail
    
    # ==========================================
    # <ftp>の情報を取得
    # ------------------------------------------
    def get_def_ftp(self, element) -> dict:
        dict_ftp = {}
        self.log.log(DEBUG, f"def ftp = {element.tag}")
        for ftp_child in element:
            tag_name   =   ftp_child.tag

            self.log.log(DEBUG, f"ftp tag name = {tag_name}")    
            if  tag_name.casefold() in [csvExcelConst.FTP_FOLDER.casefold()]:
                self.log.log(DEBUG, f"ftp tag name = {tag_name}")
                tag_val =  ""
                if  ftp_child.text is not None:
                    tag_val = ftp_child.text.strip()
                
                dict_ftp[tag_name]     =   tag_val 
                        
        self.log.log(DEBUG, f"dict ftp = {dict_ftp}")

        return dict_ftp
    
    # ==========================================
    # bndファイルの情報を取得
    # ------------------------------------------
    def get_bnd(self, bnd_file_name:str) -> dict:
        
        #
        # ファイルの拡張子確認
        if  not bnd_file_name.casefold().endswith(".BND".casefold()):
            raise self.csvExcelImportXmlError(f"Can't .bnd File {bnd_file_name}")
        #
        # ファイルの存在確認
        if  not os.path.isfile(bnd_file_name):
            raise self.csvExcelImportXmlError(f"File Not Found {bnd_file_name}")

        # XMLファイルを解析
        #! with open(bnd_file_name, encoding="shift_jis") as file:
        with open(bnd_file_name, encoding="cp932") as file:
            xml = file.read()

        # XMLを取得
        root = ET.fromstring(xml)
        dict_bnd = self.get_bnd_data(root)
            
        return dict_bnd
    # ==========================================
    # defファイルの情報を取得
    # ------------------------------------------
    def get_def(self, def_file_name:str) -> (dict, dict):

        dict_head  = {}
        dict_item  = {}
        
        #
        # ファイルの拡張子確認
        if not def_file_name.casefold().endswith(".def".casefold()):
            err_name = f"Can't .def File ({def_file_name})"
            self.log.log(CRITICAL, err_name)
            raise self.csvExcelImportXmlError(err_name)
        #
        # ファイルの存在確認
        if not os.path.isfile(def_file_name):
            err_name = f"File Not Found ({def_file_name})"
            self.log.log(CRITICAL, err_name)
            raise self.csvExcelImportXmlError(err_name)

        #! XMLファイルを解析
        #! with open(def_file_name, encoding="shift_jis") as file:
        with open(def_file_name, encoding="cp932") as file:
            xml = file.read()

        # XMLを取得
        root = ET.fromstring(xml)

        for child in root:

            child_name = child.tag
            self.log.log(DEBUG, f"node = {child_name}")
            if  child_name.casefold() == csvExcelConst.FILE.casefold():
                dict_head[child_name] = self.get_def_head(child)
            
            if  child_name.casefold() == csvExcelConst.COLUMNS.casefold():
                dict_item = self.get_def_item(child)

            if  child_name.casefold() == csvExcelConst.MAIL.casefold():
                dict_head[child_name] = self.get_def_mail(child)

            if  child_name.casefold() == csvExcelConst.FTP.casefold():
                dict_head[child_name] = self.get_def_ftp(child)

            if  child_name.casefold() == csvExcelConst.PIVOT.casefold():
                dict_head[child_name] = self.get_def_pivot(child)

            if  child_name.casefold() == csvExcelConst.GRAPH.casefold():
                dict_head[child_name] = self.get_def_graph(child)

        return dict_head, dict_item
