import os
from numpy import exp, log, log10, sin, cos, tan, arcsin, arccos, arctan, sqrt
import openpyxl
import pandas as pd


from tklib.tkobject import tkObject
from tklib.tkfile import tkFile
from tklib.tkutils import pint as _pint, pfloat as _pfloat, pconv
from tklib.tksci.tksci import h, h_bar, hbar, e, kB, NA, c, pi, pi2, torad, todeg, basee
from tklib.tksci.tksci import me, mp, mn, u0, e0, e2_4pie0, a0, R, F, g
from tklib.tksci.tksci import acos, asin, atan, cosh, sinh, tanh
from tklib.tksci.tksci import degcos, degsin, degtan, degacos, degasin, degatan
from tklib.tksci.tksci import eVTonm, nmToeV
from tklib.tksci.tksci import factorial, Factorize, Gaussian, Lorentzian, combination, gamma


def pint(s):
    return _pint(s, defval = None, strict = False)

def pfloat(s):
    return _pfloat(s, defval = None, strict = False)

def process_template(template: str, context: dict) -> str:
    """
    Replace special characters like \t, \n, \r and template tags {{ key }} with their corresponding values.

    Args:
        template (str): The input string containing template tags and special characters.
        context (dict): A dictionary containing key-value pairs for template replacement.

    Returns:
        str: The processed string with replacements applied.
    """
    # Replace special characters
    template = template.replace(r'\t', '\t')
    template = template.replace(r'\n', '\n')
    template = template.replace(r'\r', '\r')
    
    # Replace {{ key }} with context values
    def replace_placeholder(match):
        key = match.group(1).strip()
        return str(context.get(key, f'{{{{ {key} }}}}'))  # Keep original if key not found

    template = re.sub(r'\{\{\s*(.*?)\s*\}\}', replace_placeholder, template)

    return template

def convert_str(s, replace_dict, blank_nodata = False, print_level = 0):
    if print_level:
        print("")
        print(f"str: [{s}]")
#        print(f"replace_dict:", replace_dict)
        for key, val in replace_dict.items():
            print(f"  {key:10}: [{val}]")

    if s is None:
        return s

    for key, val in replace_dict.items():
        if blank_nodata and val is None:
            s = s.replace(sstr, '')
        else:
            sstr = '{{' + key + '}}'
            s = s.replace(sstr, str(val))

    if len(s) > 6 and s[:6] == "@eval:":
#        print(f"eval: {s}")
#        print(f"   eval: {s[6:]}")
        s = eval(s[6:], globals(), {})
        s = str(s)

    if print_level:
        print(f"  => [{s}]")

    return s

def convert_file(template_path, output_path, replace_dict, print_level = 0):
    if print_level:
        print("")
        print(f"Read [{template_path}] to read")
        print(f"Open [{output_path}] to save")

    if os.path.isfile(template_path):
        infp  = tkFile(template_path, 'r', encoding = None)
    else:
        if print_level:
            print()
            print(f"Error in tktemplate.convert_file(): Template file [{template_path}] does not exist")
        return False
        infp = None

    outfp = tkFile(output_path, 'w', encoding = None)
    if not outfp.fp:
        print()
        print(f"Error in tktemplate.convert_file(): Can not write to [{output_path}]")
        return False

    for line in infp.fp:
        for key, val in replace_dict.items():
            sstr = '{{' + key + '}}'
            line = line.replace(sstr, str(val))
        outfp.write(line)

    infp.close()
    outfp.close()

    return True

def read_template_xlsx(template_path, data_only = False, print_level = 0):
    if not os.path.isfile(template_path):
        if print_level:
            print()
            print(f"Error in tktemplate.read_template_xlsx(): File [{template_path}] does not exist")
        return None, None

    wb = openpyxl.load_workbook(template_path, data_only = data_only)
    if not wb:
        if print_level:
            print()
            print(f"Error in tktemplate.read_template_xlsx(): Can not read file [{template_path}]")
        return None, None

    ws = wb.active

    labels = []
    templates = []
    for i in range(1, ws.max_column + 1):
        labels.append(ws.cell(row = 1, column = i).value)
        templates.append(ws.cell(row = 2, column = i).value)    
    if print_level:
        print("labels=", labels)
        print("templates=", templates)
    return labels, templates

def replace_df_columns(df, replace_dict, print_level = 0):
    labels_original = df.columns.tolist()
    replace_keys = replace_dict.keys()
    print("labels_original=", labels_original)
    print("replace_keys=", replace_keys)
    for i, l in enumerate(labels_original):
        print("label: ", l)
        if l in replace_keys and replace_dict[l] is not None and replace_dict[l] != '':
#            print("replace", i, l, replace_dict[l])
            labels_original[i] = replace_dict[l]
        else:
#            print("no replace", i, l)
            pass

    df.columns = labels_original

    return df

def add_to_excel(template_path, outfile, data_list, replace_dict, data_only = False, print_level = 1):
    if print_level >= 1:
        print("")
        print(f"Read template [{template_path}]")

    labels, templates = read_template_xlsx(template_path, data_only = data_only, print_level = print_level)
    wb = openpyxl.load_workbook(template_path, data_only = data_only)
    ws = wb.active

    if os.path.exists(outfile):
        if print_level >= 1:
            print(f"Read output file [{outfile}]")
        wb_out = openpyxl.load_workbook(outfile)
        ws_out = wb_out.active
    else:
        if print_level >= 1:
            print(f"Output file [{outfile}] does not exist. Create Excel workbook.")
        wb_out = openpyxl.Workbook()
        ws_out = wb_out.active
        for icol, label in enumerate(labels):
            ws_out.cell(row = 1, column = icol + 1).value = label

    irow = ws_out.max_row

    if print_lvel:
        print(f"Add data to [{outfile}]")
    for icol, t in enumerate(templates):
        s_conv = convert_by_template(t, replace_dict, print_level = 0)
        s_conv = pconv(s_conv)
        if print_level >= 2:
            print(f"    {irow}: {t}: {s_conv}")  
        ws_out.cell(row = irow + 1, column = icol + 1).value = s_conv

    if print_level:
        print()
        print(f"Save to [{outfile}]")
    wb_out.save(outfile)

    return True

def read_replacement_db(replacement_db_path, print_level = 1):
    if print_level:
        print()
        print(f"Read replacement DB {replacement_db_path}")

    wb = openpyxl.load_workbook(replacement_db_path, data_only = False)
    ws = wb.active
    labels = []
    replace_str = []
    for icol in range(ws.max_column):
        if icol == 0:
            top_value = ws.cell(row = 1, column = 1).value
            if top_value[-2:] == '@@':
                continue

        replace_str.append(ws.cell(row = 1, column = icol + 1).value)
        labels.append(ws.cell(row = 2, column = icol + 1).value)    

    replace_dict = {}
    for label, str in zip(labels, replace_str):
        replace_dict[label] = str

    if print_level >= 2:
        print("Replacement rules:")
        for l, r in zip(labels, replace_str):
            print(f"  {l} => {r}")

    return replace_dict
