# 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 sys
from sys import exit
import os
import time
import time
import platform
import glob
import re
from numpy import exp, log, log10, sin, cos, tan, arcsin, arccos, arctan, sqrt
import tkinter


v2 = os.environ.get('tklibPath', None)
if v2:
    pythonlib_path = v2
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)


from tklib.tkutils import getarg, getintarg, getfloatarg, pint, pfloat, terminate
from tklib.tkutils import get_os, split_file_path, find_executable_path, search_executable_path, convert_by_vars
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, hbar, e, kB, NA, c, pi, pi2, torad, todeg, me, mp, mn, u0, e0, a0, R, F, g


"""
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, 3, 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
#================================
    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, '../..'))

    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')
    app.perllib_path    = os.path.join(app.perllib_path, 'lib')
    app.tkprog_path     = os.path.join(app.tkProg_Root, 'tkprog')
    app.tkprog_X_path   = os.path.abspath(os.path.join(app.script_dir, '..'))
    app.tkapp_path      = os.path.join(app.tkProg_Root, 'tkapp')
    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')
    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.editor_path = config.get('editor_path', '')
    if config.editor_path == '':
        config.editor_path  = search_executable_path(['code', 'notepad.exe', 'xemacs', 'emacs', 'vim', 'vi'])

    config.python3_path = config.get('python3_path', '')
    if config.python3_path == '':
#        app.python_path     = search_executable_path(['python.exe', 'python', 'python3.exe', 'python3'])
#        config.python3_path = app.python_path
        config.python3_path  = search_executable_path(['python.exe', 'python', 'python3.exe', 'python3'])
#    if app.get('python_path', None) is None:
    config.python_path = config.python3_path

    config.python2_path = config.get('python2_path', '')
    if config.python2_path == '':
        config.python2_path     = search_executable_path(['python2.exe', 'python2'])

    config.perl_path = config.get('perl_path', '')
    if config.perl_path == '':
#        app.perl_path    = search_executable_path(['perl', 'perl.exe'])
        config.perl_path    = search_executable_path(['perl', 'perl.exe'])
#        config.perl_path = app.perl_path
#    if app.get('perl_path', None) is None:
#        app.perl_path    = search_executable_path(['perl', 'perl.exe'])

    config.vesta_path = config.get('vesta_path', '')
    if config.vesta_path == '':
        dir = os.path.join(app.tkProg_Root, 'tkapp_open', 'Structure', 'VESTA3-64')
        if not os.path.exists(dir):
            dir = os.path.join(app.tkProg_Root, 'tkapp_open', 'Structure', 'VESTA3-32')
        if os.path.exists(dir):
            config.vesta_path = os.path.join(dir, 'VESTA.exe')

    print("")
    print("External applications:")
    print("  editor_path :", config.editor_path)
    print("  python_path :", config.python_path)
    print("  python3_path:", config.python3_path)
    print("  python2_path:", config.python2_path)
    print("  perl_path   :", config.perl_path)
    print("  vesta_path  :", config.vesta_path)
        
    for key in ["tkProg_Root", "tklib_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 = True

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

    config.font_size             = 10
    config.help_button_font_size = 8

    config.width_input_editbox  = 80
    config.height_input_editbox = 15
    config.message_box_width    = 40
    config.message_box_height   = 4
    
    config.width_listbox        = 40
    config.height_listbox       = 15
    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("")
        print("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)

    menu_tool = tkinter.Menu(menu_bar, tearoff = 0)
    menu_tool.add_command(label = app.p('Setup'), accelerator = 'Alt+u', 
                command = lambda e: app.dialog_setup(
                        widgets = 'editor_path|python3_path|python2_path|perl_path|vesta_path|confirm_on_exit|debug_mode'))
    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 = 'editor_path|python3_path|python2_path|perl_path|vesta_path|confirm_on_exit|debug_mode'))

    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')

    setupButton = tktkinter.Button(app, tool_bar, text = "Setup", font = ("", 10),
                    command = lambda: app.dialog_setup(
                                    entry_width = 50, button_width = 10, edit_button_width = 10, 
                                    shell_button_width = 0, 
                                    widgets = 'editor_path|python3_path|python2_path|perl_path|vesta_path|confirm_on_exit|debug_mode',
                                    is_print = True)
                   )
    setupButton.pack(side = tkinter.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 = ("", 10),
                    command = edit_inifile)
    edit_ini_Button.pack(side = tkinter.LEFT, anchor = 'w', padx = 1, pady = 0)

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

    exitButton = tktkinter.Button(app, tool_bar, text = 'Exit', font = ("", 10),
                        command = lambda: app.on_closing(), 
                        anchor = tkinter.NW)
    exitButton.pack(side = tkinter.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 = tkinter.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 ["script_path", "prmpath", "infile", "outfile", "max_num_probes", "num_search_each", "num_rand_basis", "interval", "score_mode"]:
    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_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)
        app.s_engine.execute(app, app.get_params(), menu, idx = None, action = 'select', i_mouse_button = 1, max_num = 1)

        submenus = app.s_engine.get_submenus(app, menu)
        for i in range(len(tkvars["submenu_buttons"])):
            tkvars["submenu_buttons"][i]["state"] = tkinter.DISABLED
            tkvars["submenu_buttons"][i]["text"] = ''
            tkvars["help_buttons"][i]["state"] = tkinter.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"] = tkinter.NORMAL
            elif task == 'help':
                button = tkvars["help_buttons"][idx-1]
                button.set_text('?')
#                button["text"] = '?'
                button["state"] = tkinter.NORMAL

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

    print("")
    print("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)
        print(f"  [{basename}] in [{dirname}]")
        if basename in app.remove_scripts:
            print(f"      [{basename}] is in the remove script list. Skipped.")
            continue

        if not os.path.isfile(f):
            print(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("")
    print(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}]']
    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, tkinter.END)
    tkvars["menu_listbox"].clear()
    for menu in menus:
        tkvars["menu_listbox"].insert(tkinter.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, e):
    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)
        elif action == '2':
            app.s_engine.execute(app, cparams, menu, ibutton, '', 2)
        elif action == '3':
            app.s_engine.execute(app, cparams, menu, ibutton, '', 3)
        elif action == 'd1':
            app.s_engine.execute(app, cparams, menu, ibutton, 'dblclick', 1)
    elif button_type == 'help':
        if action == '1':
            app.s_engine.execute(app, cparams, menu, ibutton, 'help', 1)
        elif action == '2':
            app.s_engine.execute(app, cparams, menu, ibutton, 'help', 2)
        elif action == '3':
            app.s_engine.execute(app, cparams, menu, ibutton, 'help', 3)
        elif action == 'd1':
            app.s_engine.execute(app, cparams, menu, ibutton, 'dblclick', 1)

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


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 = tkinter.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 = tkinter.Frame(page1_frame, bg = 'white')

    listbox_frame = tkinter.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 = tkinter.TOP, anchor = 'w', padx = 2, pady = 2)
#    listbox.frame.pack(side = tkinter.TOP, anchor = 'w', padx = 2, pady = 2, fill = tkinter.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 = tkinter.LEFT, padx = 2, pady = 2, fill = tkinter.BOTH, expand = True)

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

    def rbutton_clicked(e, app, menu, i_mouse_button):
        app.s_engine.execute(app, cparams, menu, idx = None, action = None, i_mouse_button = 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 = tkinter.LEFT, anchor = 'nw', padx = 2, pady = 2, fill = tkinter.BOTH, expand = True)

    top_frame.pack(side = tkinter.TOP, anchor = 'nw', padx = 2, pady = 2, fill = tkinter.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 = tkinter.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"]),
                    is_print = True, 
                    igridrow = 0)

    path_grid_frame = tkinter.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,
                    igridrow = 1)

# Menu buttons
    def add_buttons(parent, idx):
        frame = tkinter.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 = tkinter.LEFT, anchor = 'w', padx = 0, pady = 0, fill = tkinter.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 = tkinter.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', e))
        button.bind("<Button-2>",        lambda e: button_clicked(app, cparams, idx, 'submenu', '2', e))
        button.bind("<Button-3>",        lambda e: button_clicked(app, cparams, idx, 'submenu', '3', e))
        button.bind("<Double-Button-1>", lambda e: button_clicked(app, cparams, idx, 'submenu', 'd1', e))
            
        hbutton.bind("<Button-1>",        lambda e: button_clicked(app, cparams, idx, 'help', '1', e))
        hbutton.bind("<Button-2>",        lambda e: button_clicked(app, cparams, idx, 'help', '2', e))
        hbutton.bind("<Button-3>",        lambda e: button_clicked(app, cparams, idx, 'help', '3', e))
        hbutton.bind("<Double-Button-1>", lambda e: button_clicked(app, cparams, idx, 'help', 'd1', e))

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

        return button, hbutton

    button_grid_frame = tkinter.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 = tkinter.TOP, anchor = 'w', padx = 2, pady = 2, fill = tkinter.X, expand = True)

# message box grid
    message_grid_frame = tkinter.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,
                    igridrow = 0)
    tktkinter.make_path_frame(app, cparams, page1_frame, message_grid_frame, 
                    tkvars, varname = 'command_line', 
                    head_label_width = 10,
                    entry_width = config.message_box_width,
                    button_width = 5,
                    button_args  = {'text': 'run', "width": 4, 'font': ("", 10), 
                               '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,
                    igridrow = 1)
    tktkinter.make_path_frame(app, cparams, page1_frame, message_grid_frame, 
                    tkvars, varname = 'message', 
                    head_label_width = 10,
                    entry_width = config.message_box_width,
                    button_width = 0,
                    edit_button_width = 0,
                    shell_button_width = 0,
                    optionmenu_width = 0,
                    igridrow = 2)

    initialize_launcher_menus(app, cparams)
    set_rbutton_captions(app, cparams)

    return notebook_frame


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

    root_window = app.create_window(title = app.window_title, geometry = config.geometry, minsize = config.minsize)
    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
#==================================================================
    app = tkLauncherApp(usage_str = usage_str, globals = globals(), locals = locals(), 
                        use_user_inifile = True,
                        inifile_dirs = ['../../user', 'user'], create_inidir = False)
    print(f"Configuration file: {app.config.inifile}")
    

# 設定パラメータオブジェクト
    config  = app.config
    cparams = app.get_params()   # 計算エンジンのパラメータオブジェクト
    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)

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

    print("")
    print(f"Read configration from [{config.inifile}]")
    config.read_parameters(path = config.inifile)

    print("")
    print(f"Update parameters by command-line arguments")
    update_vars(app, cparams)

# python_pathとpython3_pathを強制的に一致させる
    config.python_path = config.python3_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"
    m = re.search(r"_(.*)$", 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"

    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("")
        print(f"Read script lists from [{path}]")

        app.extra_scripts  = []
        app.remove_scripts = []
        try:
            fp = open_chardet(path, 'r', def_encoding = 'shift_jis')
#            fp = open(path, 'r')
        except:
            print(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}]")
    config.save_parameters(path = config.inifile, section = 'Configure', sort_by_keys = True, IsPrint = False)

# 必要があれば、入力ファイルに対応するパラメータファイルを保存
    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 to [{cparams.prmpath}]", usage = usage)

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


if __name__ == "__main__":
    main()


