import os
import platform
import sys
import re

sys.path.append('.')

try:
    import tklib.tkimport as imp
except Exception as e:
    print()
    print("######################################################################")
    print("###########  ERROR ERROR ERROR ERROR ERROR ERROR #####################")
    print("######################################################################")
    print(f"# Failed to import [tklib.tkimport] module ({e}).")
    print(f"#  Add [tkProg]{os.sep}tklib{os.sep}python to PYTHONPATH variable")
    print(f"#  Current PYTHONPATH:", sys.path)
    print("######################################################################")
    input("Press ENTER to terminate>>")
    exit()

np  = imp.import_lib("numpy", stop_by_error = False)
pd  = imp.import_lib("pandas", stop_by_error = False)
mpl = imp.import_lib("matplotlib", stop_by_error = False)
imp.messages(stop_by_error = True)


from numpy import sqrt, exp, log, log10
from scipy.optimize import minimize
from scipy.signal import savgol_filter 
from matplotlib import pyplot as plt
import pandas


from tklib.tkutils import print_data, pint, pfloat, format_strlist
from tklib.tkvariousdata import tkVariousData
from tklib.tkapplication import tkApplication
from tklib.tkparams import tkParams

from tklib.tksci.tksci import e, kB
from tklib.tksci.tkmlr import tkMLData, tkMLR

from tklib.tkgraphic.tkplot_pyplot import select_plt, tkPlot_pyplot
from tklib.tkgraphic.tkplotevent import tkPlotEvent, RangeSelector
from tklib.tksci.tkFit_lib import conv_input, save_data, add_history

import tklib.tksci.tkoptimize_flex as opt
import optimize_peakfit_mf as mf
import peakfit as omodel


"""
Multiple peak fitting
"""


ProgramName = "Fitting to multiple peaks"


def init(app, cfg, mf):
    print("")
    print("Initialize fitting condition:")
    fit = opt.clean(app, cfg, mf)

    config_path   = cfg.parameterfile
    fitparam_path = fit.fitfile_path

    print(f"  Read [{cfg.infile}]")
    xlabels, ylabels, xdata_list, ydata_list, _ = mf.read_input_data(app, cfg.infile, cfg = cfg)
    xlist = xdata_list[0]
    ylist = ydata_list[0]

    options = {"nsmooth": cfg.nsmooth, "norder": cfg.norder, "threshold": cfg.threshold, "ydiff1_threshold": cfg.ydiff1_threshold}
    app.varname, app.unit, app.pk_scale, app.optid, app.linid, app.x0, app.dx, app.kmin, app.kmax, app.kpenalty \
                = omodel.build_fit_params(xlist, ylist, options)
    for i, varname in enumerate(app.varname):
        app.add_argument(opt = f"--{varname}", type = "float", defval = app.x0[i])

    print()
    fit = mf.init_fit(app, cfg)

# Linear LSQ
    pk_all = opt.linear_fit(app, cfg, mf, omodel, fit, print_level = True)
    fit.pk = pk_all

    print()
    mf.save_parameter_files(config_path, fitparam_path, fit, cfg)

    app.terminate(pause = True)


def main():
    app = tkApplication()

    if len(sys.argv) > 1 and sys.argv[1] == 'mk_config':
        app.make_config_files(arg_config_file = 'arg_config.xlsx', fit_config_file = 'fit_config.xlsx')
        app.terminate("\n", pause = True)


#初期値の確定順序
#intialize(): 起動時引数と初期値の設定
    cfg = mf.initialize(app)

#--infile引数をチェックし、読み込みファイル名を最初に確定する
    cfg.infile = app.check_arg('--infile', defval = None, vartype = 'str')

#cfg.infileからログファイル名を作り、console出力をredirectする
    cfg.logfile = app.replace_path(cfg.infile)
    app.redirect(heading = f"Open logfile [{cfg.logfile}]",
                targets = ["stdout", cfg.logfile], mode = 'w')
    app.set_exception()

    try:
        app.print_title(f"#  {ProgramName}")
    except:
        pass

#cfg.infileが入力ファイルか設定ファイルであるかによって、infileとconfig_pathを返す
#  設定ファイルである場合、infile_storedをNoneにして、後で設定ファイルからcfg.infileを読み込む
    cfg.infile, cfg.parameterfile, infile_stored \
            = app.analyze_infile(cfg.infile, infile_ext = [".xlsx"], config_ext = [".in", ".ini", ".prm"], print_level = 1)

#起動時引数で与えられたパラメータをcfgに設定
    print()
    print("Update parameters from command line arguments")
    app.update_vars(cfg, apply_default = True)
#    cfg.print_parameters()

    mf.initialize_minimize_func(app)

#設定ファイルを読み込み、cfgに設定。#cfg.infileも書き換えられる
    print()
    print(f"Read configration file [{cfg.parameterfile}]")
    mf.read_parameters(cfg.parameterfile, cfg)
#    cfg.print_parameters()

#infile_stored = Noneの場合、設定ファイルのcfg.infileをinfile_storedに再保存
#infile_storedが最終的な読み込みファイル名になる
    if infile_stored is None: infile_stored = cfg.infile

#起動時引数で与えられたパラメータを最優先にするため、再設定
    app.force_given_args(cfg)
#    cfg.print_parameters()
#    exit()

# --daemonフラグが立っていたらdaemonize()
    if cfg.get("daemon", 0): 
        print("\ndaemon flag is set:")
        print("  fplot flag is set to -1")
        cfg.fplot = -1
        app.daemonize(stdout_path = 'daemon.log', stderr_path = 'daemon.log', print_level = 1)
# --nohupフラグが立っていたらHUPシグナルを無視
    if cfg.get("nohup", 0): app.nohup()

#cfg.infileを infile_storedに最終設定
    cfg.infile = infile_stored
    cfg.print_parameters()
#    exit()

    cfg.historyfile   = app.replace_path(cfg.infile, template = ["{dirname}", "{filebody}-history.xlsx"])
    cfg.stopfile      = app.replace_path(cfg.infile, template = ["{dirname}", "stop"])
    print(f"infile: {cfg.infile}")
    print(f"parameterfile: {cfg.parameterfile}")
    print(f"stop file: {cfg.stopfile}")
    if os.path.isfile(cfg.stopfile):
        print(f"\nWarning: stop file [{cfg.stopfile}] exisits")
        print("  remove it")
        os.remove(cfg.stopfile)

    if cfg.file1 in ['clean', 'inf', 'kill', 'plot', 'init', 'search', 'lfit', 'lasso', 'fit', 'update', 'sim', "error"]:
        cfg.mode = cfg.file1
        cfg.file1 = ''

    if cfg.mode == 'clean':
        opt.clean(app, cfg, mf)
    elif cfg.mode == 'kill':
        opt.kill(app, 'python')
    elif cfg.mode == 'inf':
        opt.inf(app, cfg, mf)
    elif cfg.mode == 'init':
        init(app, cfg, mf)
    elif cfg.mode == 'search':
        omodel.peak_search(app, cfg, mf)
    elif cfg.mode == 'lfit':
        opt.linear_fit_func(app, cfg, mf, omodel)
    elif cfg.mode == 'lasso':
        opt.lasso(app, cfg, mf, omodel)
    elif cfg.mode == 'sim':
        opt.sim(app, cfg, mf)
    elif cfg.mode == 'scan':
        opt.scan(app, cfg, mf)
    elif cfg.mode == 'mapping':
        opt.mapping(app, cfg, mf)
    elif cfg.mode == 'sampling':
        opt.sampling(app, cfg, mf)
    elif cfg.mode == 'fit1':
        opt.fit1(app, cfg, mf)
    elif cfg.mode == 'fit':
        if cfg.get("daemon", 0):
            opt.fit_simple(app, cfg, mf, optid_mode = 'optid')
        else:
            opt.fit(app, cfg, mf, optid_mode = 'optid')
    elif cfg.mode == 'fitlp':
        if cfg.get("daemon", 0):
            opt.fit_simple(app, cfg, mf, optid_mode = 'linid')
        else:
            opt.fit(app, cfg, mf, optid_mode = 'linid')
    elif cfg.mode == 'error':
        opt.error(app, cfg, mf)
    elif cfg.mode == 'mlr':
        opt.mlr(app, cfg, mf)
    elif cfg.mode == 'plot':
        omodel.plot_input(app, cfg, mf)
    elif cfg.mode == 'plot_history':
        plot_history(app, cfg, mf)
    elif cfg.mode == 'plot_fit':
        plot_fit(app, cfg, mf)
    elif cfg.mode in globals() and callable(globals()[cfg.mode]):
# cfg.modeで与えられた関数が定義されていれば、呼び出す
        globals()[cfg.mode](app, cfg)
    else:
        app.terminate(message = "\n", 
                        usage = app.usage,
                        post_message = f"Error in main: Invalide mode [{cfg.mode}]", 
                        pause = True,
                     )


if __name__ == "__main__":
    main()
