# For future implement: TkinterDnD2: 
#  https://juu7g.hatenablog.com/entry/Python/csv/viewer#%E3%83%89%E3%83%A9%E3%83%83%E3%82%B0%E3%82%A2%E3%83%B3%E3%83%89%E3%83%89%E3%83%AD%E3%83%83%E3%83%97%E3%81%AE%E5%AE%9F%E8%A3%85TkinterDnD2%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9
import os
import sys

v = os.environ.get('tklibPath', None)
if v:
    pythonlib_path = v
else:
    script_path = sys.argv[0]
    tkProg_Root  = os.path.join(script_path, '../../..')
    pythonlib_path  = os.path.abspath(os.path.join(tkProg_Root, 'tklib/python'))

print(f"Add [{pythonlib_path}] to PYTHONPATH by sys.path.append()")
sys.path.append(pythonlib_path)

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()
    
tk = imp.import_lib("tkinter", stop_by_error = False)
imp.messages(stop_by_error = True)

#tkdnd = imp.import_lib("tkinterdnd2", stop_by_error = False)
#numba = imp.import_lib("numba", stop_by_error = False)

from sys import exit
import time
import platform
import glob
import re
from numpy import exp, log, log10, sin, cos, tan, arcsin, arccos, arctan, sqrt
#import tkinter as tk
import tkinter.font as font


from tklib.tkutils import getarg, getintarg, getfloatarg, pint, pfloat, terminate, desktop_name
from tklib.tkutils import get_os, get_last_directory, add_path, split_file_path, find_executable_path, search_executable_path, convert_by_vars
from tklib.tkutils import get_user_inifile_dir
from tklib.tkparams import tkParams
from tklib.tkfile import open_chardet
from tklib.tkgui.tkapplication_gui import tkApplication_GUI
import tklib.tkgui.tktkinter as tktkinter
from tkLauncherApp import tkLauncherApp
from tklib.tkscript_macro import tkScript
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

from tklib.tkgui.tktooltip import tkTooltip


"""
Launcher
"""


usage_str = "\n" \
          + "(i)   usage: python Launcher.py [-s=script_list_name]\n" \
          + "         ex: python Launcher.py -s={script_list_name}\n"


#=============================
# global variables
#=============================
prog_name = 'Launcher'
version = [1, 4, 0]


#=============================
# Treat argments
#=============================
def usage(app):
#    cparams = app.get_params()
#    app.usage(infile = cparams.infile)
    for s in app.usage_str.split('\n'):
        cmd = 'print({})'.format(s.rstrip())
        eval(cmd)


def initialize(app, config):
#================================
# Global variables
#================================
    cparams = app.get_params()   # 計算エンジンのパラメータオブジェクト

    tkvars  = app.tkvars.get_param_dict()

    app.window_title = f'{prog_name} {version}'

    app.cur_sel            = 0
    app.last_menu          = ''
    app.last_ibutton       = 0
    app.last_button_type   = ''
    app.last_button_action = ''

# tkApplicationで定義済み
#   app.user            = getpass.getuser()
#   app.user_nospace    = self.user.replace(' ', '')
#   app.argv            = sys.argv
#   app.script_path     = sys.argv[0]
#   app.script_fullpath = os.path.abspath(self.script_path)
#   app.script_dir      = dirname
#   app.script_filebody = filebody
#   app.ini_dir = self.get_inifile_dir(use_user_inifile = use_user_inifile,
#   app.inifile = self.get_inifile_path(self.ini_dir)
#    dirname, basename, filebody, ext = split_file_path(app.script_fullpath)

    app.resource_dir   = os.path.join(app.script_dir, 'resource')
    app.extra_scripts  = []
    app.remove_scripts = []

    app.env = os.environ
    v = app.env.get('tkProg_Root', None)
    if v:
        app.tkProg_Root = v
    else:
        app.tkProg_Root = os.path.abspath(os.path.join(app.script_dir, '../..'))

    os_name = platform.system()
    if os_name == 'Linux':
        app.use_tooltip = False
    else:
        app.use_tooltip = True

    app.bin_path        = os.path.join(app.tkProg_Root, 'bin')
    app.tklib_Root      = os.path.join(app.tkProg_Root, 'tklib')
    app.pythonlib_path  = pythonlib_path
    app.perllib_path    = os.path.join(app.tklib_Root, 'Perl', 'lib')
    app.tkprog_path     = os.path.join(app.tkProg_Root, 'tkprog')
    app.tkprog_X_path   = os.path.abspath(os.path.join(app.script_dir, '..'))
    if os_name == 'Windows':
        app.tkapp_path      = os.path.join(app.tkProg_Root, 'tkapp_Win')
    elif os_name == 'Darwin':
        app.tkapp_path      = os.path.join(app.tkProg_Root, 'tkapp_Mac')
    else:
        app.tkapp_path      = os.path.join(app.tkProg_Root, 'tkapp_Linux')
    app.tkapp_open_path = os.path.join(app.tkProg_Root, 'tkapp_open')
    app.tkapp_etc_path  = os.path.join(app.tkProg_Root, 'tkapp_etc')

    app.launcher_script_dir  = os.path.join(app.script_dir, 'scripts')
    app.internal_editor_path = os.path.join(app.tkprog_X_path, 'editor/editor.py')

    add_path(app.bin_path)

    """
    print("")
    print(f"OS: {os_name}")
    print("")
    print("Directories:")
    print("  tkProg_Root         :", app.tkProg_Root)
    print("  tklib_Root          :", app.tklib_Root)
    print("  pythonlib_path      :", app.pythonlib_path)
    print("  perllib_path        :", app.perllib_path)
    print("  tkprog_X_path       :", app.tkprog_X_path)
    print("  internal_editor_path:", app.internal_editor_path)
    print("  tkapp_path          :", app.tkapp_path)
    print("  tkapp_open_path     :", app.tkapp_open_path)
    print("  tkapp_etc_path      :", app.tkapp_etc_path)
    print("  tkprog_path         :", app.tkprog_path)
    print("  launcher_script_dir :", app.launcher_script_dir)
    """

    config.start_path = config.get('start_path', '')
    if config.start_path == '':
        files = app.get_start_apps(filename_only = True)
        if len(files) > 0:
            config.start_path = files[0]
    
    config.shell_path = config.get('shell_path', '')
    if config.shell_path == '':
        files = app.get_shells(filename_only = True)
        if len(files) > 0:
            config.shell_path = files[0]
    
    config.filer_path = config.get('filer_path', '')
    if config.filer_path == '':
        files = app.get_filers(filename_only = True)
        if len(files) > 0:
            config.filer_path = files[0]

    config.editor_path = config.get('editor_path', '')
    if config.editor_path == '':
        files = app.get_editors(filename_only = True)
        if len(files) > 0:
            config.editor_path = files[0]

    config.python3_path = config.get('python3_path', '')
    if config.python3_path == '':
        config.python3_path  = search_executable_path(['python3.exe', 'python3', 'python.exe', 'python'], filename_only = True)
    config.python_path = config.python3_path

    config.pythonw_path = config.get('pythonw_path', '')
    if config.pythonw_path == '':
        config.pythonw_path = search_executable_path(['pythonw.exe', 'pythonw'], filename_only = True)

    config.perl_path = config.get('perl_path', '')
    if config.perl_path == '':
        config.perl_path = search_executable_path(['perl', 'perl.exe'], filename_only = True)

    config.vesta_path = config.get('vesta_path', '')
    if config.vesta_path == '':
        dir = os.path.join(app.tkapp_path, 'VESTA')
        if os.path.exists(dir):
            config.vesta_path = os.path.join(dir, 'VESTA')

    """
    print("")
    print("External applications:")
    print("  editor_path :", config.editor_path)
    print("  python_path :", config.python_path)
    print("  python3_path:", config.python3_path)
    print("  pythonw_path:", config.pythonw_path)
    print("  perl_path   :", config.perl_path)
    print("  vesta_path  :", config.vesta_path)
    """

    for key in ["tkProg_Root", "tklib_Root", "tklib_path", "pythonlib_path", "perllib_path", "tkprog_X_path", "internal_editor_path", 
                "tkapp_path", "tkapp_open_path", "tkapp_etc_path", "tkprog_path", 
                "script_dir", "script_path", "script_fullpath", "launcher_script_dir"]:
        app.env[key] = app.get(key, '')

# Non-configurable configuration
#    app.prm_file_type  = [('prm file', '*.prm')]
#    app.data_file_type = [('data file', '*.csv')]

# Configurable configuration
    config = app.config
    config.lang            = 'en'
    config.debug           = False
    config.print_level     = 0

    config.confirm_on_exit = False
    app.show_log = False
    config.show_log = app.show_log

    config.minsize         = [200, 200]
    config.geometry        = "470x580"
    config.left_pane_width = 500

    if os_name == 'Windows':
        config.geometry        = "470x580"
        config.default_font_size     = 14
        config.font_size             = 10
        config.help_button_font_size = 8
        config.path_frame_font_size  = 8
        config.width_listbox        = 40
        config.height_listbox       = 15
    elif os_name == 'Darwin':
        config.geometry        = "470x580"
        config.default_font_size     = 14
        config.font_size             = 10
        config.help_button_font_size = 8
        config.path_frame_font_size  = 8
        config.width_listbox        = 40
        config.height_listbox       = 15
    else:
        config.geometry        = "512x664"
        config.default_font_size     = 10
        config.font_size             = 8
        config.help_button_font_size = 6
        config.path_frame_font_size  = 8
        config.width_listbox        = 40
        config.height_listbox       = 12

    config.head_label_width     = 10
    config.width_input_editbox  = 80
    config.height_input_editbox = 15
    config.message_box_width    = 40
    config.message_box_height   = 4
    
    config.width_rbutton        = 10
#    config.height_rbutton       = 7
    config.n_tbutton            = 5
    config.n_rbutton            = 7
    config.nx_button            = 3
    config.ny_button            = 7
    config.n_buttons = config.nx_button * config.ny_button
    config.width_submenu_button  = 18
#    config.height_submenu_button = 3
    config.width_help_button     = 3
    config.height_help_button    = 1

# Define var types in config for auto type conversion in config.read_parameters()
    config.editor_path = ''
#    config.infile      = ''
#    config.outfile     = ''
#    config.score_mode  = 'EI'

    config.program_path = ''


def update_vars(app, cparams):
    app.add_argument(opt = "-s", type = "str", var_name = 'script_list_name',  opt_str = "-s=script_list_name", desc = 'Script list file',
                     defval = "", optional = True);
#    app.add_argument(opt = "-i", type = "float", var_name = 'idx', opt_str = "-i=integer", desc = 'Index to speify alpha', 
#                     defval = 5, optional = False);
#    app.add_argument(opt = None, type = "str", var_name = 'infile', opt_str = "infile",  desc = 'Input file', 
#                     defval = "input.txt", optional = False);
#    app.add_argument(opt = None, type = "str",   var_name = 'nmax', opt_str = "nmax",      desc = 'Max number of interation', 
#                    defval = 5, optional = False);

    args_opt, args_idx, args_vars = app.read_args(vars = cparams.__dict__, check_allowed_args = True)
    if args_opt is None:
        error_no = args_idx
        error_message = args_vars
        app.terminate("\n\n"
                   +  "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
                   + f"!  {error_message}\n"
                   +  "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",
                   usage = app.usage)

    app.set_usage(usage_str)

    """
    print("cparams.__dict__=", cparams.__dict__)
    app.usage()
    exit()
    """

def create_menu(app, config, cparams, root_window):
    def onOpen(event = None):
#        data_path_button_click(app, tkvars)
        print("")
        self.print_warning("Warning: This function is not impletmented")
    
    menu_bar  = app.create_menu()

    menu_file = tktkinter.Menu(app, menu_bar, tearoff = 0)
    menu_file.add_command(label = app.p('Open'), accelerator = 'Alt+O', 
                command = onOpen)
#                command = lambda: data_path_button_click(app, tkvars))
    menu_file.add_command(label = app.p('exit'), accelerator='Alt+E', command = lambda e: app.on_closing())
    menu_bar.add_cascade(label = app.p('File'), menu = menu_file)

    setup_widgets = 'editor_path|python3_path|pythonw_path|perl_path|' \
                  + 'start_path|shell_path|filer_path|vesta_path|confirm_on_exit|debug_mode'

    menu_tool = tk.Menu(menu_bar, tearoff = 0)
    menu_tool.add_command(label = app.p('Setup'), accelerator = 'Alt+u', 
                command = lambda e: app.dialog_setup(widgets = setup_widgets))
    menu_bar.add_cascade(label = app.p('Tool'), menu = menu_tool)
    menu_bar.bind_all("<Alt-o>", onOpen)
    menu_bar.bind_all("<Alt-e>", lambda e: app.on_closing())
    menu_bar.bind_all("<Alt-u>", lambda e: app.dialog_setup(widgets = setup_widget))

    return menu_bar

def create_toolbar(app, config, cparams, root_window):
    ctkvars = app.tkvars
    tkvars  = ctkvars.get_param_dict()
    config  = app.configparams

    def toolbar_clickevent(event):
        pass

    tool_bar = app.create_toolbar(bg = 'blue')

#==============================================================
# Setup and edit inifile buttons
#==============================================================
    setupButton = tktkinter.Button(app, tool_bar, text = "Setup", font = ("", config.font_size),
                        command = lambda: app.setup())
    setupButton.pack(side = tk.LEFT, anchor = 'w', padx = 1, pady = 0)

    """
    def edit_inifile():
        editor = config.get("editor_path", None)
        if editor is None== '' or editor == '':
            editors = app.get_editors()
            if len(editors) > 0:
                editor = editors[0]
                tktkinter.dialog_okcancel(app, title = app.p('Error'), 
                        message = 'editor_path is not specified.\nUse {editor}')
            else:
                tktkinter.dialog_okcancel(app, title = app.p('Error'), message = 'editor_path is not specified.')
                return
         
#        tktkinter.execute_command(app, config.editor_path, working_dir = '.', is_print = True, files = [config.inifile])
        tkScript().execute_external_command(app, config.editor_path, [config.inifile], is_print = False)

    edit_ini_Button = tktkinter.Button(app, tool_bar, text = 'Edit ini file', font = ("", config.font_size),
                    command = edit_inifile)
    edit_ini_Button.pack(side = tk.LEFT, anchor = 'w', padx = 1, pady = 0)
    """

#==============================================================
# Extra buttons
#==============================================================
    def tbutton_clicked2(e, app, menu, i_mouse_button):
        app.s_engine.execute(app, cparams, menu, idx = None, action = None, i_mouse_button = i_mouse_button, event = e)

    def tbutton_clicked(e, app, menu, i_mouse_button):
        app.root_window.after(1, 
            lambda: tbutton_clicked2(e, app, menu, i_mouse_button))

    def bind_tbutton(i):
        b = tkvars['tbuttons'][i]
        b.bind("<Button-1>",        lambda e: tbutton_clicked(e, app, f'[TButton{i+1}]', '1'))
        b.bind("<Button-2>",        lambda e: tbutton_clicked(e, app, f'[TButton{i+1}]', '2'))
        b.bind("<Button-3>",        lambda e: tbutton_clicked(e, app, f'[TButton{i+1}]', '3'))
        b.bind("<Double-Button-1>", lambda e: tbutton_clicked(e, app, f'[TButton{i+1}]', 'd'))

    tkvars['tbuttons'] = []
    for i in range(1, config.n_tbutton+1):
        tbutton = tktkinter.Button(app, tool_bar, text = '', font = ("", config.font_size))
        tbutton.pack(side = tk.LEFT, anchor = 'w', padx = 1, pady = 0)
        tkvars['tbuttons'].append(tbutton)

    for i in range(config.n_tbutton):
        bind_tbutton(i)

#==============================================================
# Exit button
#==============================================================
    def lang_sel_change(e):
        config.lang = tkvars['lang_combobox'].get()
        phrases = {'WillApplyUponReboot': {'ja': f'日本語に変更されました。再起動後に有効になります',
                                           'en': f'Changed to English. Will be applied after reboot'
                                          }
                  }
        app.phrases = app.read_resource(app.resource_path, config.lang, app.phrases)
        tktkinter.dialog_okcancel(app, title = app.p('Quit'), message = app.p('WillApplyUponReboot', phrases = phrases))

    tkvars['lang'] = tk.StringVar(value = config.lang)
#    optionmenu = tk.OptionMenu(tool_bar, tool_bar, 'ja', 'en')
#    optionmenu.pack(side = tk.LEFT, anchor = 'w', padx = 1, pady = 0)
    combobox = tk.ttk.Combobox(tool_bar, values = ('ja', 'en'), width = 2)
    combobox.set(tkvars['lang'].get())
    combobox.bind('<<ComboboxSelected>>', lang_sel_change)
    combobox.pack(side = tk.LEFT, anchor = 'w', padx = 1, pady = 0)
    tkvars['lang_combobox'] = combobox

    exitButton = tktkinter.Button(app, tool_bar, text = 'Exit', font = ("", config.font_size),
                        command = lambda: app.on_closing(), 
                        anchor = tk.NW)
    exitButton.pack(side = tk.LEFT, anchor = 'w', padx = 1, pady = 0)

def input_path_button_click(app, cparams, tkvars, 
                varname = "infile", parameterpath_varname = "prmpath", outputpath_varname = "outfile",
                file_type = [("Excel", "*.xlsx"), ("All", "*.*")], ini_dir = '.'):
    selpath = tk.filedialog.askopenfilename(filetypes = file_type, initialdir = ini_dir)

    header, ext            = os.path.splitext(selpath)
    filebody               = os.path.basename(header)
    cparams.outfile        = header + f'-predict.xlsx'
    cparams.parameter_path = header + '.prm'

    tkvars[varname].set(selpath)
    tkvars[outputpath_varname].set(cparams.outfile)
#    tkvars[parameterpath_varname].set(cparams.parameter_path)

def copy_tkvars(app, cparams, tkvars):
    config  = app.configparams
    return

    for key in ["fontsize", "legend_fontsize"]:
        cparams.set_attr(key, tkvars[key].get())

    for key in ["fontsize", "legend_fontsize"]:
        config.set_attr(key, tkvars[key].get())

    cparams.set_attr('figsize', [tkvars['fig_width'].get(), tkvars['fig_height'].get()])

def copy_config(app, cparams, config):
    keys0 = config.__dict__.keys()
    for key in cparams.__dict__.keys():
        if key in keys0:
            cparams.set_attr(key, cparams.get(key, None))

def listbox_dbl_clicked(app, cparams, event):
        ctkvars = app.tkvars
        tkvars  = ctkvars.get_param_dict()
#        print("double clicked")

def set_tbutton_captions(app, cparams):
    tkvars  = app.tkvars.get_param_dict()
    for i in range(app.config.n_tbutton):
        caption = app.s_engine.get_caption(app, f'[TButton{i+1}]')
        tkvars["tbuttons"][i].set_text(caption)

def set_rbutton_captions(app, cparams):
    tkvars  = app.tkvars.get_param_dict()
    for i in range(app.config.n_rbutton):
        caption = app.s_engine.get_caption(app, f'[RButton{i+1}]')
#        print(f"{caption=}")
        tkvars["rbuttons"][i].set_text(caption)
#        tkvars["rbuttons"][i]["text"] = app.p(caption)
    
def listbox_selected(app, cparams, event):
#        print("selected")
#        print("e=", type(event), event, event.widget)
        tkvars  = app.tkvars.get_param_dict()

        sel = tkvars["menu_listbox"].curselection()
#        print("343 sel=", sel)
        if len(sel) == 0:
            return

        app.cur_sel = sel[0]
#        print("348 cur_sel=", app.cur_sel)

        menu = tkvars["menu_listbox"].get(sel)

        nbutton = len(tkvars["submenu_buttons"])
        os_name = platform.system()
        if app.use_tooltip:
            for idx in range(nbutton):
                button = tkvars["submenu_buttons"][idx]
                tip = tkTooltip(widget = button, text = f'button {idx+1}', bg = '#dcfbfe', fg = '#0000ff')
        app.s_engine.execute(app, app.get_params(), menu, idx = None, action = 'select', i_mouse_button = 1, max_num = 1, event = event)

        submenus = app.s_engine.get_submenus(app, menu)
        for i in range(len(tkvars["submenu_buttons"])):
            tkvars["submenu_buttons"][i]["state"] = tk.DISABLED
            tkvars["submenu_buttons"][i]["text"] = ''
            tkvars["help_buttons"][i]["state"] = tk.DISABLED
            tkvars["help_buttons"][i].set_text('x')
#            tkvars["help_buttons"][i]["text"] = 'x'

#        print("submenus=", submenus)
        for m in submenus:
            idx     = m['idx']
            task    = m['task']
            caption = m['caption']
            if task == '':
#                print(f"button {idx}={menu}")
                try:
                    button = tkvars["submenu_buttons"][idx-1]
                except:
                    print("")
                    print(f"Error in Launcher.listbox_selected(): Menu idx {idx} for {menu} exceeds [{app.config.n_buttons}]")
                    continue

                button.set_text(caption)
#                button.configure(text = app.p(caption))
#                button["text"] = caption
                button["state"] = tk.NORMAL
            elif task == 'help':
                button = tkvars["help_buttons"][idx-1]
                button.set_text('?')
#                button["text"] = '?'
                button["state"] = tk.NORMAL

def initialize_launcher_menus(app, cparams):
    tkvars  = app.tkvars.get_param_dict()

    print("")
    app.print_warning("Script files:")
    files = glob.glob(os.path.join(app.launcher_script_dir, '*.ini'))

    app.script_files = {}
    for f in app.extra_scripts + files:
        dirname, basename, filebody, ext = split_file_path(f)
        app.print_warning(f"  [{basename}] in [{dirname}]")
        if basename in app.remove_scripts:
            app.print_warning(f"      [{basename}] is in the remove script list. Skipped.")
            continue

        if not os.path.isfile(f):
            app.print_warning(f"      [{basename}] does not exist. Skipped.")
            continue

        app.script_files[basename] = { "dir": dirname, "filebody": filebody, "filename": basename, "path": f }

    script_files = sorted(app.script_files.items())
    app.s_engine = app.create_script()

    print("")
    app.print_warning(f"Read resource file [{app.resource_path}]:")
    app.phrases = app.read_resource(app.resource_path, app.config.lang) #, app.phrases)

    os_name = platform.system()
    menus = ['[Boot]', f'[Boot-{os_name}]']
    if os_name == 'Linux':
        desktop = desktop_name()
        if desktop is not None:
            menus = ['[Boot]', f'[Boot-{os_name}]', f'[Boot-{desktop}']

    for menu in menus:
        for fname in app.script_files.keys():
            d = app.script_files[fname]
            path = d["path"]
            path, fp, line = app.s_engine.find_file_from_menu(app, menu, idx = None, task = None, script_files = [path])
            if fp is None:
                continue
            else:
                app.s_engine.execute(app, app.get_params(), menu, idx = None, action = None, i_mouse_button = None, script_files = [path])

    app.update_script_vars(app.get_params())
    v = app.s_engine.update_script_vars(app, cparams)

    menus = app.s_engine.get_main_menus(app)

    print("")
    print("Initialize Launcher menu")
#    tkvars["menu_listbox"].delete(0, tk.END)
    tkvars["menu_listbox"].clear()
    for menu in menus:
        tkvars["menu_listbox"].insert(tk.END, app.p(menu))

    tkvars["menu_listbox"].select_set(0)
    listbox_selected(app, cparams, None)

# button_type: menu, help
# action: 1, 2, d1 for left button, right button, double click
def button_clicked2(app, cparams, ibutton, button_type, action, event = None):
    ctkvars = app.tkvars
    tkvars  = ctkvars.get_param_dict()
    sel = tkvars["menu_listbox"].curselection()
#    print(f"441 sel={sel}")
    if sel is None or len(sel) == 0:
        sel = app.cur_sel
        if sel is not None:
            tkvars["menu_listbox"].select_set(sel)
        if sel is None:
            tktkinter.dialog_showerror(app, title = 'Error', message = 'Select main menu')
            return

#    print(f"448 sel={sel}")
    menu = tkvars["menu_listbox"].get(sel)
    app.last_ibutton = ibutton
    
    app.update_script_vars(cparams)

#    print(f"cliecked: {ibutton} {button_type} {action}")
    button_type = button_type.lower()
    if button_type == 'submenu':
        if action == '1':
            app.s_engine.execute(app, cparams, menu, ibutton, '', 1, event = event)
        elif action == '2':
            app.s_engine.execute(app, cparams, menu, ibutton, '', 2, event = event)
        elif action == '3':
            app.s_engine.execute(app, cparams, menu, ibutton, '', 3, event = event)
        elif action == 'd1':
            app.s_engine.execute(app, cparams, menu, ibutton, 'dblclick', 1, event = event)
    elif button_type == 'help':
        if action == '1':
            app.s_engine.execute(app, cparams, menu, ibutton, 'help', 1, event = event)
        elif action == '2':
            app.s_engine.execute(app, cparams, menu, ibutton, 'help', 2, event = event)
        elif action == '3':
            app.s_engine.execute(app, cparams, menu, ibutton, 'help', 3, event = event)
        elif action == 'd1':
            app.s_engine.execute(app, cparams, menu, ibutton, 'dblclick', 1, event = event)

def button_clicked(app, cparams, ibutton, button_type, action, event = None):
    app.root_window.after(1, 
        lambda: button_clicked2(app, cparams, ibutton, button_type, action, event = event))


def create_frame(app, config, cparams, parent_notebook):
    config  = app.configparams
    ctkvars = app.tkvars
    tkvars  = ctkvars.get_param_dict()

    page1_frame = tktkinter.Frame(app, parent_notebook, bg = 'dim gray')
    page1_frame.pack(fill = tk.BOTH, expand = True)
    parent_notebook.add(page1_frame, text = app.p('Menu'))
    notebook_frame = page1_frame

    applications = app.get_external_apps(config.get("editor_path", None))

# Listbox
# https://www.shido.info/py/tkinter6.html
    top_frame = tk.Frame(page1_frame, bg = 'white')

    listbox_frame = tk.Frame(top_frame)
    listbox = tktkinter.tkScrolledListbox(listbox_frame, width = config.width_listbox, height = config.height_listbox, 
                              font = ("", config.font_size), borderwidth = 2, relief = "groove", cursor = 'arrow', fg = 'black',
                              selectmode = 'browse', takefocus = True, state = 'normal', justify = 'left')
    listbox.frame.pack(side = tk.TOP, anchor = 'w', padx = 2, pady = 2)
#    listbox.frame.pack(side = tk.TOP, anchor = 'w', padx = 2, pady = 2, fill = tk.BOTH, expand = True)
#    listbox.bind("<<Button-1>>", listbox_selected)
#    listbox.bind("<<Button-2>>", listbox_selected)
    listbox.bind("<<ListboxSelect>>", lambda e: listbox_selected(app, cparams, e))
    listbox.bind("<Double-Button-1>", lambda e: listbox_dbl_clicked(app, cparams, e))
    tkvars["menu_listbox"] = listbox
    listbox_frame.pack(side = tk.LEFT, padx = 2, pady = 2, fill = tk.BOTH, expand = True)

# Right buttons
    rbutton_frame = tk.Frame(top_frame)
    tkvars['rbuttons'] = []
    for i in range(1, config.n_rbutton+1):
        rbutton = tktkinter.Button(app, rbutton_frame, text = '', width = config.width_rbutton, font = ("", config.font_size))
        rbutton.pack(side = tk.TOP, fill = tk.BOTH, expand = True)
        tkvars['rbuttons'].append(rbutton)

    def rbutton_clicked2(e, app, menu, i_mouse_button):
        app.s_engine.execute(app, cparams, menu, idx = None, action = None, i_mouse_button = i_mouse_button, event = 3)

    def rbutton_clicked(e, app, menu, i_mouse_button):
        app.root_window.after(1, 
            lambda: rbutton_clicked2(e, app, menu, i_mouse_button))

    def bind_rbutton(i):
        b = tkvars['rbuttons'][i]
        b.bind("<Button-1>",        lambda e: rbutton_clicked(e, app, f'[RButton{i+1}]', '1'))
        b.bind("<Button-2>",        lambda e: rbutton_clicked(e, app, f'[RButton{i+1}]', '2'))
        b.bind("<Button-3>",        lambda e: rbutton_clicked(e, app, f'[RButton{i+1}]', '3'))
        b.bind("<Double-Button-1>", lambda e: rbutton_clicked(e, app, f'[RButton{i+1}]', 'd'))

    for i in range(config.n_rbutton):
        bind_rbutton(i)

    rbutton_frame.pack(side = tk.LEFT, anchor = 'nw', padx = 2, pady = 2, fill = tk.BOTH, expand = True)

    top_frame.pack(side = tk.TOP, anchor = 'nw', padx = 2, pady = 2, fill = tk.BOTH, expand = True)

# Input path frame
    def open_file(input_entry, output_entry = None, file_type = None, ini_dir = '.'):
        tktkinter.path_button_click(app, input_entry,
                                    file_type = file_type, ini_dir = ini_dir)
        if output_entry:
            header, ext = os.path.splitext(input_entry.get())
            filebody    = os.path.basename(header)
            outfile     = header + f'-predict.xlsx'
            output_entry.set(outfile)

    path_grid_frame = tk.Frame(page1_frame)
    tktkinter.make_path_frame(app, cparams, page1_frame, path_grid_frame, 
                    tkvars, varname = 'sel_file', 
                    file_type = [("All", "*.*")],
                    working_dir = '.', 
                    head_label_width = 6,
                    entry_width = 35,
                    button_width = 4,
                    edit_button_width = 0,
                    shell_button_width = 0,
                    optionmenu_width = 4,
                    open_command = lambda app, cparams, file_type, ini_dir: \
                                        open_file(tkvars["sel_file"], None, file_type, ini_dir),
#                    post_open_command = lambda: post_open(tkvars["infile"], tkvars["outfile"]),
                    font_size = config.path_frame_font_size,
                    is_print = True, 
                    igridrow = 0)

    path_grid_frame = tk.Frame(page1_frame)
    tktkinter.make_path_frame(app, cparams, page1_frame, path_grid_frame, 
                    tkvars, varname = 'argument', 
                    head_label_width = 5,
                    entry_width = 40,
                    button_width = 0,
                    edit_button_width = 0,
                    shell_button_width = 0,
                    optionmenu_width = 0,
                    font_size = config.path_frame_font_size,
                    igridrow = 1)


# Menu buttons
    def add_buttons(parent, idx):
        frame = tk.Frame(parent, bg = 'white')
        button = tktkinter.Button(app, frame, text = '',  #f'Button {c}', 
                                width = config.width_submenu_button,
                                font = ("", config.font_size),
#                                command = lambda: analyze(app, cparams, tkvars)
                                )
#            button.grid(row = iy, column = ix)
        button.pack(side = tk.LEFT, anchor = 'w', padx = 0, pady = 0, fill = tk.X, expand = True)

        hbutton = tktkinter.Button(app, frame, text = '?', 
#                                width = config.width_help_button, height = config.height_help_button, 
                                 font = ("", config.help_button_font_size),
#                                command = lambda: analyze(app, cparams, tkvars),
                                )
        hbutton.pack(side = tk.LEFT, anchor = 'nw', padx = 0, pady = 0)
        frame.grid(row = iy, column = ix)

        button.bind("<Button-1>",        lambda e: button_clicked(app, cparams, idx, 'submenu', '1', event = e))
        button.bind("<Button-2>",        lambda e: button_clicked(app, cparams, idx, 'submenu', '2', event = e))
        button.bind("<Button-3>",        lambda e: button_clicked(app, cparams, idx, 'submenu', '3', event = e))
        button.bind("<Double-Button-1>", lambda e: button_clicked(app, cparams, idx, 'submenu', 'd1', event = e))
            
        hbutton.bind("<Button-1>",        lambda e: button_clicked(app, cparams, idx, 'help', '1', event = e))
        hbutton.bind("<Button-2>",        lambda e: button_clicked(app, cparams, idx, 'help', '2', event = e))
        hbutton.bind("<Button-3>",        lambda e: button_clicked(app, cparams, idx, 'help', '3', event = e))
        hbutton.bind("<Double-Button-1>", lambda e: button_clicked(app, cparams, idx, 'help', 'd1', event = e))

        tkvars["submenu_buttons"].append(button)
        tkvars["help_buttons"].append(hbutton)

        return button, hbutton

    button_grid_frame = tk.Frame(page1_frame, bg = 'white')
    tkvars["submenu_buttons"] = []
    tkvars["help_buttons"] = []
    c = 0
    for iy in range(config.ny_button):
        for ix in range(config.nx_button):
            button, hbutton = add_buttons(button_grid_frame, c)
            c += 1

#    button_grid_frame.grid_propagate(True)
    button_grid_frame.pack(side = tk.TOP, anchor = 'w', padx = 2, pady = 2, fill = tk.X, expand = True)

# message box grid
    message_grid_frame = tk.Frame(page1_frame)
    tktkinter.make_path_frame(app, cparams, page1_frame, message_grid_frame, 
                    tkvars, varname = 'command_line_org', 
                    head_label_width = 10,
                    entry_width = config.message_box_width,
                    button_width = 0,
                    edit_button_width = 0,
                    shell_button_width = 0,
                    optionmenu_width = 0,
                    font_size = config.path_frame_font_size,
                    igridrow = 0)

    def copy_to_clipboard(parent, entry_variable):
        print(f"copy [{entry_variable.get()}] to clipboard")
        parent.clipboard_clear()
        parent.clipboard_append(entry_variable.get())
        parent.update_idletasks()
                    
    tktkinter.make_path_frame(app, cparams, page1_frame, message_grid_frame, 
                    tkvars, varname = 'command_line', 
                    head_label_width = config.head_label_width,
                    entry_width = config.message_box_width,
                    button_width = 5,
                    button_args  = {'text': 'run', "width": 4, 'font': ("", config.font_size), 
                               'command': lambda: app.s_engine.execute_a_command(app, cparams, tkvars['command_line'].get())
                             },
                    edit_button_width = 0,
                    shell_button_width = 0,
                    optionmenu_width = 0,
                    copy_button_args = {'text': 'copy', "width": 4, 'font': ("", config.font_size), 
#                               'command': lambda: copy_to_clipboard(page1_frame, tkvars['command_line'].get()),
                                'igridcol': 4
                             },
                    font_size = config.path_frame_font_size,
                    igridrow = 1)
    tktkinter.make_path_frame(app, cparams, page1_frame, message_grid_frame, 
                    tkvars, varname = 'message', 
                    head_label_width = config.head_label_width,
                    entry_width = config.message_box_width,
                    button_width = 0,
                    edit_button_width = 0,
                    shell_button_width = 0,
                    optionmenu_width = 0,
                    font_size = config.path_frame_font_size,
                    igridrow = 2)

    initialize_launcher_menus(app, cparams)
    set_tbutton_captions(app, cparams)
    set_rbutton_captions(app, cparams)
    os_name = platform.system()
    if app.use_tooltip:
        for idx in range(len(tkvars["tbuttons"])):
            button = tkvars["tbuttons"][idx]
            tkTooltip(widget = button, text = f'tbutton {idx+1}', bg = '#FFFF00', fg = '#0000FF')
        for idx in range(len(tkvars["rbuttons"])):
            button = tkvars["rbuttons"][idx]
            tkTooltip(widget = button, text = f'rbutton {idx+1}', bg = '#FFFF00', fg = '#0000FF')

    app.s_engine.execute(app, cparams, '[TButton].onload', idx = None, action = 'onload', i_mouse_button = None)
    app.s_engine.execute(app, cparams, '[RButton].onload', idx = None, action = 'onload', i_mouse_button = None)

    return notebook_frame


def create_window(app, config, cparams):
    ctkvars = app.tkvars
    tkvars  = ctkvars.get_param_dict()

#    iconfile = 'tkProg.ico'
    iconfile = os.path.join(os.environ.get('tkprog_X_path', '../'), 'tkProg.ico')
    os_name = platform.system()
#    print("os_name=", os_name)
#    print(f"Use icon file [{iconfile}]")
    if os_name == 'Linux' or not os.path.isfile(iconfile):
        root_window = app.create_window(title = app.window_title,
                    geometry = config.geometry, minsize = config.minsize, 
                    default_font = ('FixedSys', config.default_font_size)) #None) #('FixedSys', 14))
    else:
        root_window = app.create_window(title = app.window_title, iconfile = iconfile,
                    geometry = config.geometry, minsize = config.minsize, 
                    default_font = ('FixedSys', config.default_font_size)) #None) #('FixedSys', 14))
    app.main_window = root_window
#    menu_bar = create_menu(app, config, cparams, root_window)
    tool_bar = create_toolbar(app, config, cparams, root_window)
# 2分割ウインドウを使う場合。いづれのpaneもnotebook型ウインドウが作られる
#    panes = app.add_root_panes(root = root_window, type = "pane2", sashwidth = 2, left_pane_width = 600)
#    main_pane, left_notebook, right_notebook = panes
# 1p分割ウインドウを使う場合。paneはnotebook型ウインドウが作られる
    panes = app.add_root_panes(type = "pane1", sashwidth = 2)
    left_notebook = panes[0]

# 左ペインのnotebookにタブを追加。２ページ目は開発者用タブ
    ltabframe1 = create_frame(app, config, cparams, left_notebook)
    ltabframe2 = app.create_development_tab(config, cparams, left_notebook, copy_tkvars = copy_tkvars)
    ltabframe3 = app.create_editor_tab(config, cparams, left_notebook, title = 'Editor test',
                    entry_width = 35, 
                    eval_button_args = { 'text': 'eval', 'width': 4, "command": None },
                    )

def main():
#==================================================================
# Initialize parameters
#==================================================================
    if platform.system() == 'Windows':
        user_ini_dir = get_user_inifile_dir(env_vars = [], dir_names = ['launcher'])
    else:
        user_ini_dir = get_user_inifile_dir(env_vars = ['HOME'], dir_names = ['.launcher', 'launcher'])

    app = tkLauncherApp(usage_str = usage_str, globals = globals(), locals = locals(), 
                        use_user_inifile = True,
                        inifile_dirs = [user_ini_dir, '../../user', 'user'], create_inidir = False)
    print(f"Configuration file: {app.config.inifile}")

# 設定パラメータオブジェクト
    config  = app.config
    cparams = app.get_params()   # 計算エンジンのパラメータオブジェクト

# GUIウィジェット変数オブジェクトと辞書型
    ctkvars = app.tkvars
    tkvars  = ctkvars.get_param_dict()

    mtime = time.localtime(os.path.getctime(app.script_path))

    print("")
    print( "################################################")
    print(f"# {app.script_path} ver {version[0]}.{version[1]}.{version[2]}")
    print(f"#       Modified time: {mtime.tm_year}/{mtime.tm_mon}/{mtime.tm_mday} {mtime.tm_hour}:{mtime.tm_min}:{mtime.tm_sec}")
    print( "################################################")

    print(f"Initialize parameters")
    initialize(app, config)

    print("")
    print(f"Read configration from [{config.inifile}]")
    config.read_parameters(path = config.inifile)
    if config.get('show_log', None):
        app.show_log = config.show_log

    print("")
    print(f"Add _path variables in {config.inifile} to environment vars")
    for key in config.__dict__.keys():
        if '_path' in key:
            val = config.get(key, '')
            if val is not None:
                app.print(f"  config[{key}]={val} is added")
                app.env[key] = val

    print("")
    print(f"Update parameters by command-line arguments")
    update_vars(app, cparams)
    config.n_buttons = config.nx_button * config.ny_button

#===========================================
# logファイルへ保存
#===========================================
    logfiles = []
    if user_ini_dir:
        logfiles.append(os.path.join(user_ini_dir, "Launcher-out.txt"))
    logfiles.append(app.replace_path())
    logfiles.append("Launcher-out.txt")
    config.logfile = None
    for path in logfiles:
#ファイルが無いときもFalseが返るので、tryに置き換え
#        if os.access(path, os.W_OK):
        try:
            fout = open(path, "a")
            fout.close()
            config.logfile = path
            break
        except:
            path = os.path.join(user_ini_dir, "Launcher-out.txt")
            try:
                fout = open(path, "a")
                fout.close()
                config.logfile = path
                break
            except:
                print(f"\nWarning in main(): Can not write log to [{path}]")

    if config.logfile is None:
        print(f"\nWarning in main(): Can not create log file. Output log will not be save")

#    app.show_log = True
    print(f"Open logfile [{config.logfile}]")

    if  app.show_log:
        app.redict(targets = ["stdout", config.logfile], mode = 'w')
    else:
        app.redict(targets = [config.logfile], mode = 'w')

# python_pathとpython3_pathを強制的に一致させる
    config.python_path = config.python3_path

# VSCodeのパスにスペースが入っていて問題が起こるので、実行ファイル名だけにする
    if re.search(r'code(\.exe)?$', config.editor_path):
        config.editor_path = 'code'

#===========================================
# 内部パラメータ
#===========================================
    os_name = platform.system()
    print("")
    print(f"OS: {os_name}")
    print("")
    print("Directories:")
    print("  tkProg_Root         :", app.tkProg_Root)
    print("  tklib_Root          :", app.tklib_Root)
    print("  pythonlib_path      :", app.pythonlib_path)
    print("  perllib_path        :", app.perllib_path)
    print("  tkprog_X_path       :", app.tkprog_X_path)
    print("  internal_editor_path:", app.internal_editor_path)
    print("  tkapp_path          :", app.tkapp_path)
    print("  tkapp_open_path     :", app.tkapp_open_path)
    print("  tkapp_etc_path      :", app.tkapp_etc_path)
    print("  tkprog_path         :", app.tkprog_path)
    print("  launcher_script_dir :", app.launcher_script_dir)

    print("")
    print("External applications:")
    print("  editor_path :", config.editor_path)
    print("  python_path :", config.python_path)
    print("  python3_path:", config.python3_path)
    print("  pythonw_path:", config.pythonw_path)
    print("  perl_path   :", config.perl_path)
    print("  vesta_path  :", config.vesta_path)
    
# Group名を取得
    dirname, basename, filebody, ext = split_file_path(app.script_fullpath) # dirname = ".. /Launcher"
#    dirname, basename, filebody, ext = split_file_path(dirname)             # dirname = "tkprog_X_path"
    last_dirname = get_last_directory(dirname)
    m = re.search(r"_(.*?)$", last_dirname)
    if m:
        app.group = m.groups()[0]
    else:
        app.group = ""

# group listファイルの有無を確認
    if cparams.script_list_name == '':
        if app.group != '':
            filename = f'{app.group}.list'
        else:
            filename = 'default.list'

        path = os.path.join(app.launcher_script_dir, '../../config/Launcher', filename)
        
        if os.path.exists(path):
            cparams.script_list_name = os.path.join('../config/Launcher', filename)
        else:
            cparams.script_list_name = "default"

    print("")
    print(f"Group      : {app.group}")
    print(f"Script list: {cparams.script_list_name}")

    editor = config.get("editor_path", None)
    if editor is None or editor == '':
        editors = app.get_editors()
        if len(editors) > 0:
            config.editor_path = editors[0]
    if config.editor_path is not None:
        app.env["tkEDITOR_PATH"] = config.editor_path
    
    def read_script_list(app, filebody):
        if '.list' in filebody:
            path = os.path.join(app.launcher_script_dir, '../', filebody)
            path = os.path.abspath(path)
        else:
            path = os.path.join(app.launcher_script_dir, f'{filebody}.list')
        cparams.script_list_path = path

        print("")
        app.print_warning(f"Read script lists from [{path}]")

        app.extra_scripts  = []
        app.remove_scripts = []
        fp = open_chardet(path, 'r', def_encoding = 'shift_jis')
#        print("line", fp.readline())
        try:
            fp = open_chardet(path, 'r', def_encoding = 'shift_jis')
#            fp = open(path, 'r')
        except:
            app.print_warning(f"  Warning: Can not read [{path}]")
            return None, None

        section = ''
        while 1:
            line = fp.readline()
            if not line:
                break
            
            if '[extra_scripts]' in line:
                section = 'add'
                continue
            elif '[remove_scripts]' in line:
                section = 'remove'
                continue

            line = line.strip()
            if line == '':
                continue
            if line[0] in "#:;":
                continue

            if section == 'add':
                path = os.path.abspath(os.path.join(app.launcher_script_dir, line))
                app.extra_scripts.append(path)
            elif section == 'remove':
                app.remove_scripts.append(line)

        return app.extra_scripts, app.remove_scripts

    if cparams.script_list_name:
        read_script_list(app, cparams.script_list_name)

#    print("add=", app.extra_scripts)
#    print("remove=", app.remove_scripts)
#    exit()
    app.resource_path = os.path.join(app.resource_dir, f'resource_{app.config.lang}.resource')
    app.phrases       = app.read_resource(app.resource_path, app.config.lang) #, app.phrases)
#    app.write_resource("resource.txt", 'w')

    create_window(app, config, cparams)

    app.mainloop()

# ウィジェットの設定tkvarsをcparams,confgにコピー
    copy_tkvars(app, cparams, tkvars)
    copy_tkvars(app, config, tkvars)
# メインウインドウサイズをconfigにコピー
    config.geometry = app.root_window_geometry
    print("")
    print(f"Save configuration to [{config.inifile}]")
#    app.print_warning(f"main(): config:")
#    config.print_parameters(use_warning = True)
    ret = config.save_parameters(path = config.inifile, section = 'Configure', sort_by_keys = True, IsPrint = False)
    if not ret:
        app.print_warning(f"\nError: Can not write configuration to [{config.inifile}]\n")


# 必要があれば、入力ファイルに対応するパラメータファイルを保存
    if tkvars.get("prmpath", None) is not None:
        cparams.prmpath  = tkvars["prmpath"].get()  #os.path.splitext(cparams.path)[0] + '.prm'
        if cparams.prmpath != '':
            print(f"Save parameters to [{cparams.prmpath}]")
            ret = cparams.save_parameters(cparams.prmpath, section = 'Parameters', 
                    sort_by_keys = True, IsPrint = False, update_commandline = False)
            if not ret:
                app.terminate(f"Error in main(): Cannot write parameters to [{cparams.prmpath}]", usage = usage)

    app.terminate(usage = app.usage())


if __name__ == "__main__":
    main()


