# OBSAH SOUBORU:
# - nastaveni konvertoru

import re
import traceback

import utils
from classes import CLatexSettings, CLegendSettings, CConvertSettings, CWarning, CInputsSettings, CProgramSettings
import configparser
import os
from enums import ELtxTableType

# plna cesta k %appdata% a slozce s nastavenimi
appdata_path = os.getenv('APPDATA')
full_settings_path = appdata_path + '\\excel2tabularray\\'


ltx = CLatexSettings()
conv = CConvertSettings()
legend = CLegendSettings()
warn = CWarning()
inputs = CInputsSettings()
gui = CProgramSettings()
log_str: str = ''
TABS: str = '\t\t'


def reset():
    global ltx, conv, legend, log_str, warn, inputs
    ltx = CLatexSettings()
    conv = CConvertSettings()
    legend = CLegendSettings()
    warn = CWarning()
    inputs = CInputsSettings()


def get_profile_list():
    try:
        # najdu vse v ceste k nastavenim (slozky i soubory)
        os.listdir(f'{full_settings_path}')

        # vytahnu z nich jen soubory
        all_files = \
            [f for f in os.listdir(full_settings_path) if os.path.isfile(os.path.join(full_settings_path, f))]

        # ze souboru jen ini
        ini_files = [f for f in all_files if re.search(r'\.ini$', f) is not None]

        # a smazu jim koncovku. taky se ujistim ze existuje soubor (posledni), do ktereho pak budu ukladat
        if ini_files:
            if '(poslední).ini' not in ini_files:
                save('(poslední)ini')
            return [n[:-4] for n in ini_files]

        else:
            save('(poslední).ini')
            return ['(poslední)']

    except FileNotFoundError:
        save('(posledni).ini')
        return ['(poslední)']


def save(file_name: str):
    global log_str, conv, legend, ltx, warn, inputs

    config = configparser.ConfigParser()
    config.read(file_name)

    config['GUI'] = {
        'dark_mode': gui.dark_mode,
        'scale': gui.scale,
        'scale_int': gui.scale_int
    }

    config['INPUT'] = {
        'convert_all': conv.data_only and conv.convert_all,
        'last_start_cell': inputs.start_cell,
        'last_end_cell': inputs.end_cell,
        'last_conv_type': inputs.last_conv_type
    }

    config['TABLES'] = {
        'enabled': conv.tables.enable,
        'borders': conv.tables.borders,
        'heading_format': conv.tables.head,
        'summary_format': conv.tables.total,
        'force_stripe_h': conv.tables.stripe_h,
        'force_stripe_v': conv.tables.stripe_v
    }

    config['CONDITIONAL'] = {
        'enabled': conv.condi.enable,
        'borders': conv.condi.borders,
        'color_scale': conv.condi.colorscale
    }

    config['COLORS'] = {
        'enabled': conv.color.enable,
        'bg_color': conv.color.bg,
        'border_color': conv.color.border,
        'text_color': conv.color.fg
    }

    config['TEXT FORMATTING'] = {
        'enabled': conv.text.enable,
        'styles': conv.text.flags,
        'text_size': conv.text.textsize
    }

    config['BORDERS'] = {
        'enabled': conv.border.enable,
        'styles': conv.border.style
    }

    config['SIZE + ALIGN'] = {
        'column_widths': conv.width,
        'row_heights': conv.height,
        'halign': conv.halign,
        'valign': conv.valign
    }

    config['LATEX'] = {
        'align_at_ampersand': ltx.align,
        'base_text_size': ltx.default_text_size,
        'base_text_size_index': ltx.default_text_size_index,
        'caption': ltx.caption,
        'caption_top': ltx.caption_top,
        'decimal_separator': ltx.dec_separator,
        'document_class': ltx.document_class,
        'document_class_index': ltx.document_class_index,
        'dont_escape_math': ltx.dont_escape_math,
        'escape_ltx': ltx.escape,
        'excel_frac_as_ltx_frac': ltx.excel_frac_as_ltx_frac,
        'expected_table_type': ltx.table_type.value,
        'long_contfoot': ltx.longt.contfoot_text,
        'long_conthead': ltx.longt.conthead_text,
        'long_head_count': ltx.longt.head_row_count,
        'long_foot_count': ltx.longt.total_row_count,
        'max_char_width': ltx.max_char_width,
        'rotate_ccw': ltx.rotate_ccw,
        'thousand_separator': ltx.thousand_separator,
        'use_scientific_num_fmt': ltx.use_sci_nums,
        'use_setcell': ltx.use_setcell,
        'use_slshape': ltx.use_slshape
    }

    config['LEGEND'] = {
        'enabled': legend.enabled,
        'abbreviations': legend.abbrs,
        'descriptions': legend.expl,
        'on_top': legend.on_top,
        'possible_values': legend.params,
        'syntax': legend.syntax
    }

    config['CUSTOM HEAD'] = {
        'custom_head': conv.custom_head
    }

    if full_settings_path and not os.path.exists(full_settings_path):
        os.mkdir(full_settings_path)

    if full_settings_path and file_name:
        with open(f'{full_settings_path}{file_name}', 'w') as configfile:
            try:
                config.write(configfile)
            except UnicodeEncodeError:
                log_str += traceback.format_exc()
                raise RuntimeError('[PROFIL] Nelze uložit soubor s nastavením. Spusťte program jako správce.\n'
                'Pokud problém přetrvává, zkuste z názvu profilu a textových polí odstranit diakritiku.')
    else:
        raise RuntimeError('[PROFIL] Nelze uložit soubor. Neplatná cesta nebo název souboru.')
            

def load(name: str):
    global log_str, conv, legend, ltx, warn, inputs, gui
    config = configparser.ConfigParser()

    try:
        if full_settings_path and not os.path.exists(full_settings_path):
            os.mkdir(full_settings_path)

        if os.path.exists(full_settings_path + name):
            config.read(full_settings_path + name)

        else:
            return

        # GUI
        gui.scale_int = config.getint('GUI', 'scale_int')
        gui.scale = config.getfloat('GUI', 'scale')
        gui.dark_mode = config.getboolean('GUI', 'dark_mode')

        # VSTUPY
        if inputs.should_load_input:
            inputs.start_cell = config['INPUT']['last_start_cell']
            inputs.end_cell = config['INPUT']['last_end_cell']
            inputs.last_conv_type = config['INPUT']['last_conv_type']
            conv.convert_all = config.getboolean('INPUT', 'convert_all')

        # FORMATOVANE TABULKY
        conv.tables.enable = config.getboolean('TABLES', 'enabled')
        conv.tables.borders = config.getboolean('TABLES', 'borders')
        conv.tables.head = config.getboolean('TABLES', 'heading_format')
        conv.tables.total = config.getboolean('TABLES', 'summary_format')
        conv.tables.stripes = config.getboolean('TABLES', 'force_stripe_h')
        conv.tables.stripes = config.getboolean('TABLES', 'force_stripe_h')

        # PODMINENE FORMATOVANI
        conv.condi.enable = config.getboolean('CONDITIONAL', 'enabled')
        conv.condi.borders = config.getboolean('CONDITIONAL', 'borders')
        conv.condi.colorscale = config.getboolean('CONDITIONAL', 'color_scale')

        # BARVY
        conv.color.enable = config.getboolean('COLORS', 'enabled')
        conv.color.fg = config.getboolean('COLORS', 'text_color')
        conv.color.bg = config.getboolean('COLORS', 'bg_color')
        conv.color.border = config.getboolean('COLORS', 'border_color')

        # FORMAT TEXTU
        conv.text.enable = config.getboolean('TEXT FORMATTING', 'enabled')
        conv.text.flags = config.getboolean('TEXT FORMATTING', 'styles')
        conv.text.textsize = config.getboolean('TEXT FORMATTING', 'text_size')

        # OKRAJE
        conv.border.enable = config.getboolean('BORDERS', 'enabled')
        conv.border.style = config.getboolean('BORDERS', 'styles')

        # VELIKOSTI BUNEK A ZAROVNANI
        conv.width = config.getboolean('SIZE + ALIGN', 'column_widths')
        conv.height = config.getboolean('SIZE + ALIGN', 'row_heights')
        conv.halign = config.getboolean('SIZE + ALIGN', 'halign')
        conv.valign = config.getboolean('SIZE + ALIGN', 'valign')

        # LATEX
        ltx.document_class = config['LATEX']['document_class']
        ltx.document_class_index = config.getint('LATEX', 'document_class_index')
        ltx.default_text_size = config['LATEX']['base_text_size']
        ltx.default_text_size_index = config.getint('LATEX', 'base_text_size_index')
        ltx.caption = config['LATEX']['caption']
        ltx.dec_separator = config['LATEX']['decimal_separator']
        ltx.thousand_separator = config['LATEX']['thousand_separator']
        ltx.align = config.getboolean('LATEX', 'align_at_ampersand')
        ltx.use_setcell = config.getboolean('LATEX', 'use_setcell')
        ltx.excel_frac_as_ltx_frac = config.getboolean('LATEX', 'excel_frac_as_ltx_frac')
        ltx.use_sci_nums = config.getboolean('LATEX', 'use_scientific_num_fmt')
        ltx.escape = config.getboolean('LATEX', 'escape_ltx')
        ltx.dont_escape_math = config.getboolean('LATEX', 'dont_escape_math')
        ltx.table_type = config.getint('LATEX', 'expected_table_type')
        ltx.max_char_width = config.getint('LATEX', 'max_char_width')
        ltx.use_slshape = config.getboolean('LATEX', 'use_slshape')
        ltx.rotate_ccw = config.getboolean('LATEX', 'rotate_ccw')
        ltx.caption_top = config.getboolean('LATEX', 'caption_top')
        ltx.longt.contfoot_text = config['LATEX']['long_contfoot']
        ltx.longt.conthead_text = config['LATEX']['long_conthead']
        ltx.longt.head_row_count = config.getint('LATEX', 'long_head_count')
        ltx.longt.total_row_count = config.getint('LATEX', 'long_foot_count')

        # LEGENDA
        legend.enabled = config.getboolean('LEGEND', 'enabled')
        legend.syntax = config.getboolean('LEGEND', 'syntax')
        legend.abbrs = config.getboolean('LEGEND', 'abbreviations')
        legend.params = config.getboolean('LEGEND', 'possible_values')
        legend.expl = config.getboolean('LEGEND', 'descriptions')
        legend.on_top = config.getboolean('LEGEND', 'on_top')

        # VLASTNI HLAVICKA
        conv.custom_head = config['CUSTOM HEAD']['custom_head']

        # KONTROLA SMYSLUPLNOSTI NASTAVENI
        check()

    except FileNotFoundError:
        log_str += traceback.format_exc()
        return
    except KeyError as e:
        log_str += f'[PROFIL] Chybí nastavení \'{e.args[0]}\'\n'
        log_str += '[PROFIL] Nastavení resetováno na \'(výchozí)\'.\n'
        reset()
    except ValueError as e:
        if 'boolean' in e.args[0]:
            if len(e.args[0]) > 15:
                log_str += f'[PROFIL] Nastavení s hodnotou \'{e.args[0][15:]}\' nemá správný datový ' \
                           f'typ (očekáván bool).\n'
            else:
                log_str += f'[PROFIL] Nesprávný datový typ jedné z položek - \'{e.args[0]}\'\n'

        elif 'invalid literal for int' in e.args[0]:
            if len(e.args[0]) > 40:
                log_str += f'[PROFIL] Nastavení s hodnotou {e.args[0][40:]} nemá správný datový typ (očekáván int). \n'
            else:
                log_str += f'[PROFIL] Nesprávný datový typ jedné z položek - \'{e.args[0]}\'\n'
        else:
            log_str += f'[PROFIL] Nesprávný datový typ jedné z položek - \'{e.args[0]}\'\n'

        log_str += '[PROFIL] Nastavení resetováno na \'(výchozí)\'.\n'
        reset()
    except configparser.NoSectionError as e:
        log_str += f'[PROFIL] Nenalezena sekce \'[{e.args[0]}]\' v .ini profilu. Profil smažte a uložte znovu.\n'
        log_str += '[PROFIL] Nastavení resetováno na \'(výchozí)\'.\n'
        reset()
    except configparser.NoOptionError as e:
        log_str += f'[PROFIL] Chybí nastavení \'{e.args[0]}\' v sekci \'[{e.args[1]}]\' .ini profilu.\n'
        log_str += '[PROFIL] Nastavení resetováno na \'(výchozí)\'.\n'
        reset()
    except configparser.DuplicateOptionError as e:
        log_str += f'[PROFIL] Nastavení \'{e.args[1]}\' v sekci \'[{e.args[0]}]\' .ini profilu máte duplicitní.\n'
        log_str += '[PROFIL] Nastavení resetováno na \'(výchozí)\'.\n'
        reset()
    except configparser.ParsingError as e:
        log_str += '[PROFIL] Nepodařilo se přečíst soubor s nastavením. Profil smažte a uložte znovu.\n'
        log_str += f'{e.message}\n'
        log_str += '[PROFIL] Nastavení resetováno na \'(výchozí)\'.\n'
        reset()
    except configparser.Error as e:
        log_str += '[PROFIL] Neplatný profil. Profil smažte a uložte znovu.\n'
        log_str += f'{e.message}\n'
        log_str += '[PROFIL] Nastavení resetováno na \'(výchozí)\'.\n'
        reset()


def check():
    """Funkce zkontroluje, zda si uzivatel v souboru s nastavenim nekdo neco rucne nezmenil na nesmyslnou hodnotu.
    Pokud neco odstranil, zachyti se to ve funkci load()."""

    global log_str, conv, legend, ltx, warn, inputs, gui

    # textove hodnoty
    conv_types = ['convert_area', 'convert_table', 'convert_tables_all', 'data_only']
    if inputs.should_load_input and inputs.last_conv_type not in conv_types:
        inputs.last_conv_type = 'convert_area'

    if inputs.should_load_input and inputs.start_cell and None in utils.cell_to_index(inputs.start_cell.upper()):
        a = inputs.start_cell
        inputs.start_cell = ''
        log_str += '[PROFIL] Neplatné nastavení u \'Start\'.\n'

    if inputs.should_load_input and inputs.end_cell and None in utils.cell_to_index(inputs.end_cell.upper()):
        inputs.end_cell = ''
        log_str += '[PROFIL] Neplatné nastavení u \'Konec\'.\n'

    document_classes = ['article', 'memoir', 'report', 'book', 'letter', 'beamer']
    if ltx.document_class not in document_classes:
        ltx.document_class = 'article'
        log_str += '[PROFIL] Neplatné nastavení u \'Třída LaTeX dokumentu\'. Změneno na \'article\'\n'

    if ltx.document_class_index not in range(len(document_classes)):
        ltx.document_class = 'article'
        log_str += '[PROFIL] Neplatné nastavení u \'Třída LaTeX dokumentu\'. Změneno na \'article\'\n'
    else:
        try:
            ltx.document_class_index = document_classes.index(ltx.document_class)
        except ValueError:
            ltx.document_class_index = 0
            log_str += '[PROFIL] Neplatné nastavení u \'Třída LaTeX dokumentu\'. Změneno na \'article\'\n'

    if isinstance(ltx.dec_separator, str):
        if len(ltx.dec_separator) > 1:
            ltx.dec_separator = '.'
            log_str += '[PROFIL] Neplatné nastavení u \'Desetinný oddělovač\'. Změneno na \'.\'\n'
    else:
        ltx.dec_separator = '.'
        log_str += '[PROFIL] Neplatné nastavení u \'Desetinný oddělovač\'. Změneno na \'.\'\n'

    if isinstance(ltx.thousand_separator, str):
        if len(ltx.thousand_separator) > 1:
            ltx.thousand_separator = '.'
            log_str += '[PROFIL] Neplatné nastavení u \'Oddělovač tisíců\'. Změneno na \'~\'\n'
    else:
        ltx.thousand_separator = '~'
        log_str += '[PROFIL] Neplatné nastavení u \'Oddělovač tisíců\'. Změneno na \'~\'\n'

    # ciselne hodnoty
    if not (74 < gui.scale_int < 251):
        log_str += '[VZHLED] Neplatné nastavení u \'Měřítko GUI\'. Změneno na \'100\'.\n'
        gui.scale_int = 100
        gui.scale = 1

    if not (0.74 < gui.scale < 2.51):
        log_str += '[VZHLED] Neplatné nastavení u \'Měřítko GUI\'. Změneno na \'100\'.\n'
        gui.scale_int = 100
        gui.scale = 1

    if ltx.longt.head_row_count < 0:
        log_str += '[VZHLED] Neplatné nastavení u \'Počet stálých řádků hlavičky\'. Změneno na \'0\'.\n'
        ltx.longt.head_row_count = 0

    if ltx.longt.total_row_count < 0:
        log_str += '[VZHLED] Neplatné nastavení u \'Počet stálých řádků paty\'. Změneno na \'0\'.\n'
        ltx.longt.total_row_count = 0

    font_sizes = ['9pt', '10pt', '11pt', '12pt', '14pt', '17pt', '20pt', '25pt', '30pt', '36pt', '48pt', '60pt']
    if str(ltx.default_text_size) + 'pt' not in font_sizes:
        ltx.default_text_size = '12'
        log_str += '[PROFIL] Neplatné nastavení u \'Základní velikost písma\'. Změneno na \'12pt\'.\n'

    if not isinstance(ltx.default_text_size_index, int):
        ltx.default_text_size_index = 3
        log_str += '[PROFIL] Neplatné nastavení u \'Základní velikost písma\'. Změneno na \'12pt\'.\n'

    elif isinstance(ltx.default_text_size_index, int) and ltx.default_text_size_index not in range(12):
        ltx.default_text_size_index = 3
        log_str += '[PROFIL] Neplatné nastavení u \'Základní velikost písma\'. Změneno na \'12pt\'.\n'

    if isinstance(ltx.table_type, int):
        if ltx.table_type not in [0, 1, 2, 3]:
            ltx.table_type = ELtxTableType.NORMAL
            log_str += '[PROFIL] Neplatné nastavení u \'Typ výstupní tabulky\'. Změneno na \'Normální\'.\n'
        else:
            # int -> enum
            ltx.table_type = ELtxTableType(ltx.table_type)
    else:
        ltx.table_type = ELtxTableType.NORMAL
        log_str += '[PROFIL] Neplatné nastavení u \'Typ výstupní tabulky\'. Změneno na \'Normální\'.\n'

    if not isinstance(ltx.max_char_width, int):
        ltx.max_char_width = 80
        log_str += '[PROFIL] Neplatné nastavení u \'Maximální šířka výstupu\'. Změneno na \'80 znaků\'.\n'
    elif not 39 < ltx.max_char_width < 201:
        ltx.max_char_width = 80
        log_str += '[PROFIL] Neplatné nastavení u \'Maximální šířka výstupu\'. Změneno na \'80 znaků\'.\n'

    # bool (kontrola toho, ze nejsou zapnute 'zasednuta' nastaveni)
    if conv.convert_all and inputs.last_conv_type != 'data_only':
        conv.convert_all = False

    if not conv.tables.enable:
        conv.tables.disable()

    if not conv.condi.enable:
        conv.condi.disable()

    if not conv.color.enable:
        conv.color.disable()

    if not conv.text.enable:
        conv.text.disable()

    if not conv.border.enable:
        conv.border.disable()

    if not legend.enabled:
        legend.disable()

    if not ltx.escape:
        ltx.dont_escape_math = False

