import sys
import os
import re
import glob
import openpyxl
import pandas as pd
import tkinter

from tklib.tkutils import pconv, pint, pfloat, terminate, del_quote
from tklib.tkutils import get_os, split_file_path, get_last_directory, get_upper_directory, split_drive, find_executable_path
from tklib.tkutils import add_path, del_quote, split_command_line
from tklib.tkutils import find_latest_file, find_files
from tklib.tkparams import tkParams
from tklib.tkfile import tkFile
from tklib.tkinifile import tkIniFile
from tklib.tkexcel_db import tkExcelDB
from tklib.tkgui.tkapplication_gui import tkApplication_GUI
import tklib.tkgui.tktkinter as tktkinter
from tklib.tkgui.tktkinter import dialog_showinfo, tkInputDialog, tkSelectDialog, tkCustomDialog_by_config
from tklib.tkscript_macro import tkScript

from tklib.tkgui.tktooltip import tkTooltip
from tklib.tkgui.tkmenu_popup import tkMenu_popup

class tkLauncherApp(tkApplication_GUI):
    def __init__(self, **args):
        self.alert_var_error = True
        self.prev_popup_menu = None

        self.dialog_config = [] #tkParams()
        
        super().__init__(**args)


    def set_var(self, var, val, set_env = False):
#        print(f"set: {var=} {val=} {set_env=}")
        var_l = var.lower()
        self.s_engine.vars[var_l] = val
        self.svars.get_param_dict()[var] = val
#        print(f"tkLauncherApp.set_var 31: {var_l=}  {self.s_engine.vars[var_l]=}")
        if set_env:
            os.environ[var] = val

    def convert_os_path_sep(self, path):
        os_name = get_os()
        if os_name == 'Windows':
            val = path.replace('/', '\\')
            val = path.replace('\\\\', '\\')
        else:
            val = path.replace('\\', '/')
            val = path.replace('//', '/')
        return val

    def use_os_path_sep(self, var):
        os_name = get_os()
        val = self.s_engine.vars.get(var.lower(), "")
        if val is None:
            val = ''
        if os_name == 'Windows':
            val = val.replace('/', '\\')
            val = val.replace('\\\\', '\\')
        else:
            val = val.replace('\\', '/')
            val = val.replace('//', '/')
        self.set_var(var, val)
        
    def echo(self, arg):
        self.print_warning(arg)

    def update_script_vars(self, cparams):
        svars = self.svars
        vars  = svars.get_param_dict()
        config  = self.config
        ctkvars = self.tkvars
        tkvars  = ctkvars.get_param_dict()
        env   = self.env
        argv  = self.argv

        vars.update(env)
#        vars.update(self.config.__dict__)

        keys = ["editor_path"]
        for key in keys:
            vars[key] =  self.config.get(key, None)

        keys = ["os_name",
                "tkProg_Root",  "tklib_Root",     "pythonlib_path", "perllib_path",
                "tkprog_path", "tkprog_X_path",   "internal_editor_path", 
                "tkapp_path",  "tkapp_open_path", "tkapp_etc_path"
               ]
#                "python_path", "perl_path"]
        for key in keys:
            vars[key] = self.get(key, None)

        keys = ["script_list_name", "script_list_path"]
        for key in keys:
            vars[key] = cparams.get(key, None)

        keys = ["lang", "python3_path", "python2_path", "python_path", "perl_path",
                "start_path", "shell_path", "filer_path", "vesta_path"]
        for key in keys:
            vars[key] = config.get(key, None)
    
        vars['n'] = '\n'
        vars['p'] = self.script_path
        vars['i'] = self.inifile
        vars['s'] = os.getcwd()
        vars['w'] = env.get("windir", "")
        if 'sel_file' in tkvars.keys():
            vars['o'] = tkvars['sel_file'].get()
        if 'argument' in tkvars.keys():
            vars['args'] = tkvars['argument'].get()
        vars['shell'] = env.get("ComSpec", "")

        s = ''
        for i in range(len(argv)):
            vars[f"{i}"] = argv[i]
            s += argv[i] + ' '

        vars['a'] = s.strip()

    def add_path(self, args):
        n = len(args)
        if n >= 2:
            varname = args[0]
            path     = args[1]
        else:
            varname = 'PATH'
            path    = args[0]

        add_path(path, varname = varname)

        self.set_var(varname, os.environ[varname], set_env = True)
#        vars  = self.svars.get_param_dict()
#        vars['PATH'] = os.environ['PATH']

    def debug(self):
        return self.config.debug
        
    def set_debug(self, f):
        self.config.debug = f

    def confirm(self):
        return self.config.confirm

    def set_confirm(self, f):
        self.config.confirm = f

    def create_script(self):
        script_files = sorted(self.script_files.items())
        files = [d[1]["path"] for d in script_files]
        return tkScript(files)

    def set(self, argline, do_set = True):
        s = argline.strip()
        var = None
        val = None
        m = re.match(r'([\w\.\_]+)\s*=(.*)$', s, flags = re.MULTILINE | re.DOTALL)
        if not m:
            var = s
            val = ''
        else:
            g = m.groups()
            var = g[0]
            val = del_quote(g[1].strip())

        if do_set:
            self.set_var(var, val, set_env = False)

    def list_append(self, args, check_exist = False):
        svars  = self.svars
        varname = args[0]
        val = svars.get(varname, '')
        for v in args[1:]:
            if v == '':
                continue

            if not check_exist:
                if val == '':
                    val = v
                else:
                    val += '#' + v
            elif os.path.isexist(v):
                if val == '':
                    val = v
                else:
                    val += '#' + v

#        self.print_warning(f"set [{val}] to [{varname}]")
        self.set_var(varname, val, set_env = False)

    def set_path(self, argline):
        self.set(argline, do_set = False)
        a = argline.split(' ')
        self.use_os_path_sep(a[0])

    def join_path(self, args):
        varname = args[0]
        if len(args) >= 3:
            p = os.path.join(args[1], *args[2:])
        else:
            p = args[1]
#        print("tkLauncherApp.join_path 207: ", args, len(args), p)
        p = self.convert_os_path_sep(p)
        self.set_var(varname, p, set_env = False)

    def split_argline(self, argline):
        s = argline.strip()
        var = None
        command = None
        m = re.match(r'(\S+)\s+(\S.*=.*)$', s, flags = re.MULTILINE | re.DOTALL)
        if not m:
            self.print_warning(f"Error in tkLauncherApp.split_argline(): Invalid argline [{argline}]")
            return None, None, None

        g = m.groups()
        var = g[0]
        command = del_quote(g[1].strip())
        val = self.svars.get(var, None)
        return var, val, command
    
    def set_if_not_blank(self, argline):
        var, val, command = self.split_argline(argline)
        if val is not None and val != '':
            self.set(command)

    def set_if_not_null(self, argline):
        var, val, command = self.split_argline(argline)
        if val is not None:
            self.set(command)

    def set_if_blank(self, argline):
        var, val, command = self.split_argline(argline)
#        val = self.svars.get(var, None)
        if val is None or val == '':
            self.set(command)

    def set_if_not_blank(self, argline):
        var, val, command = self.split_argline(argline)
        if val is not None and val != '':
            self.set(command)

    def set_if_null(self, argline):
        var, val, command = self.split_argline(argline)
        if val is None:
            self.set(command)

    def set_if_not_null(self, argline):
        var, val, command = self.split_argline(argline)
        if val is not None:
            self.set(command)

    def get_app_path(self, args):
        vars = self.s_engine.vars

        varname = args[0]
        path    = vars.get(varname, None)
        fmask   = args[1]
        message = args[2]
        
        is_ok = True
        if path is None:
            self.print_warning(f"Warning: tkLauncherApp.get_app_path(): Variable [{varname}] is not defined.")
            is_ok = False
        elif not os.path.isfile(path):
            self.print_warning(f"Warning: tkLauncherApp.get_app_path(): [{path}] is not a file.")
            is_ok = False

        if not is_ok:
            ftype =  [("Specified", fmask), ("All", "*.*")]
            selpath = tkinter.filedialog.askopenfilename(filetypes = ftype, initialdir = '.', title = message)
            if not selpath:
                return False

            self.set_var(varname, selpath, set_env = True)
            self.print(f"config {varname} is set to {selpath}")
            config  = self.configparams
            config.set(varname, selpath)
#            self.print_warning(f"tkLauncherApp.get_app_path(): config:")
#            config.print_parameters(app = self, use_warning = True)

        return True
                    
    def save_config(self, argline):
        config = self.config
# ウィジェットの設定tkvarsをcparams,confgにコピー
#        copy_tkvars(app, cparams, tkvars)
#        copy_tkvars(app, config, tkvars)
# メインウインドウサイズをconfigにコピー
        config.geometry, w, h, x0, y0 = self.get_geometry() #self.root_window_geometry
        self.print_warning("")
        self.print_warning(f"Save configuration to [{config.inifile}]")
        config.save_parameters(path = config.inifile, section = 'Configure', sort_by_keys = True, IsPrint = False)

    def print_all(self, args):
        var = args[0]
        d = self.svars.get_param_dict()
        self.print_warning("")
        self.print_warning(f"Values in [{var}]")
        for key in d.keys():
            m = re.search(rf"{var}\.(.*)$", key)
            if m:
                k = m.groups()[0]
                self.print_warning(f"  {var}.{k}: [{d[key]}]")

    def read_ini_all(self, args):
        path    = args[0]
        var     = args[1]
        ini = tkIniFile(path, 'r')
        inf = ini.read_all(path = None, AddSection = 0, IsPrint = False, encoding = None)
        if inf:
            for key in inf:
                self.print_warning(f" {key}: {inf[key]}")
                self.set_var(f"{var}.{key}", inf[key], set_env = False)

    def read_ini_to_vars(self, args):
        path = args[0]
        if len(args) > 1:
            option = args[1]
        else:
            option = ''

        fp = tkFile(path, 'r')
        if not fp.fp:
            return False

        while 1:
            line = fp.readline()
            if not line: break

            top = line[0]
            if top in "#:'": continue

            m = re.search(r'^([a-zA-Z_\-\.]+)\s*=\s*(.*?)\s*$', line)
#            m = re.search(r'^\S[a-zA-Z_\-\.]\s*=\s*(.*?)\s*$', line)
            if m:
                g = m.groups()
#                print("g=", g)
                if 'del_quote' in option:
                    val = del_quote(g[1])
                else:
                    val = g[1]
                self.set_var(g[0], val, set_env = False)
#                print(f"** read_ini: {g[0]}={g[1]}")

        return True

    def read_ini(self, args):
        n = len(args)
        def_val = ''
        if n >= 4:
            path    = args[0]
            section = args[1]
            key     = args[2]
            var     = args[3]
        if n >= 5:
            def_val = args[4]
#        print(f"read: {path=} {section=} {key=} {var=} {def_val=}")

        ini = tkIniFile(path, 'r')
        s = ini.get_string(section = section, key = key, def_val = def_val, is_print = False)
        self.set_var(var, s, set_env = False)
#        print(f"{var=}  {s=}")

    def write_ini(self, args):
        n = len(args)
        def_val = ''
        if n >= 4:
            path    = args[0]
            section = args[1]
            key     = args[2]
            val     = args[3]
#        print(f"write: {path=} {section=} {key=} {val=}")

        ini = tkIniFile(path) #, 'a')
        s = ini.write_string(section = section, key = key, value = val, is_print = False)

    def setup(self, args = None):
        setup_widgets = 'editor_path|python3_path|python2_path|perl_path|' \
                      + 'start_path|shell_path|filer_path|vesta_path|confirm_on_exit|debug_mode|show_log'
        self.dialog_setup(entry_width = 50, button_width = 10, edit_button_width = 10, 
                         shell_button_width = 0, widgets = setup_widgets, is_print = True)

        config = self.config
        if self.show_log:
            self.redirects = ["stdout", config.logfile]
        else:
            self.redirects = [config.logfile]

    def copy_config2svars(self, args):
        config = self.config
        svars  = self.svars
        for key in config.keys():
            val = config.get(key, None)
#            print("key=", key, val, svars)
            svars.set(f'config.{key}', val)

    def copy_config2scars(self, args):
        self.copy_config2svars(args)
        
    def copy_svars2config(self, args):
        config = self.config
        svars  = self.svars
        for key in svars.keys():
            if 'config.' not in key:
                continue

            cname, varname = key.split('.')
            val = svars.get(key, None)
            config.set(varname, val)
            
    def eval(self, args):
        ret = eval(args[1], self.globals, {} )
        self.set_var(args[0], "{}".format(ret), set_env = False)

    def show_font_dialog(self, args):
        var = args[0]

        font = tktkinter.font_dialog(app = self, color = self.svars.get(var, None))

        if not font:
            self.print_warning("tkLauncherApp.show_font_dialog(): Cancelled")
            return -1

        self.set_var(var, font, set_env = False)

        return 1

    def show_color_dialog(self, args):
        var = args[0]

        color = tktkinter.color_dialog(color = self.svars.get(var, None))

        if not color[0]:
            self.print_warning("tkLauncherApp.show_color_dialog(): Cancelled")
            return -1

        self.set_var(var, color[1], set_env = False)

        return 1
    
    def get_button(self, button_idx0):
        tkvars  = self.tkvars.get_param_dict()
        button_idx = pint(button_idx0, None)
        if button_idx is None:
            m = re.match(r'(\w+)(\d+)$', button_idx0)
            if m:
                g = m.groups()
                bname      = g[0]
                button_idx = pint(g[1]) - 1
                
                if bname == 'RButton' and len(tkvars["rbuttons"]) > button_idx:
                    return tkvars["rbuttons"][button_idx]
                elif bname == 'TButton' and len(tkvars["tbuttons"]) > button_idx:
                    return tkvars["tbuttons"][button_idx]
        else:
            if len(tkvars["submenu_buttons"]) >= button_idx:
                return tkvars["submenu_buttons"][button_idx-1]

        return None

    def create_menu(self, args, s_engine, cparams, fp):
        tkvars  = self.tkvars.get_param_dict()
        varname = args[0]
        title = args[1]

        def append_menu(submenus, label, cmd, fp):
            submenus.append({
                                'label': label,
                                'callback': lambda: s_engine.execute_a_command(self, cparams, cmd, fp = fp)
                            }
                           )
        
        submenus = []
        for i in range(2, len(args), 2):
            key, label = args[i].split('=')
            key, cmd   = args[i+1].split('=')
#            self.print_warning("401: ", label, cmd)
            append_menu(submenus, label, cmd, fp)

        tkvars[varname] = tkMenu_popup(parent = self.root_window, title = title, submenus = submenus, app = self)

    def show_context_menu(self, args, event):
        tkvars  = self.tkvars.get_param_dict()
        menu   = tkvars.get(args[0], None)
        x = event.x_root
        y = event.y_root
        menu.post(x, y)

    def add_context_menu(self, args):
        tkvars  = self.tkvars.get_param_dict()
        menu   = tkvars.get(args[0], None)
        button = self.get_button(args[1])
        if menu is None:
            self.print_warning("")
            self.print_warning(f"Warning in tkLauncherApp.add_context_menu(): Cannot find menu var [{args[0]}]")
            self.print_warning("")
            return
        if button is None:
            self.print_warning("")
            self.print_warning(f"Warning in tkLauncherApp.add_context_menu(): Invalid button [{args[0]}]")
            self.print_warning("")
            return

        menu.bind_to_widget(button, shiftx = 20, shifty = 20)

    def add_tooltip(self, args):
        if not self.use_tooltip:
            return

        tkvars  = self.tkvars.get_param_dict()
        nargs = len(args)

        button = self.get_button(args[0])
        text   = self.p(args[1])
        kwargs = {}
        if nargs >= 3:
            kwargs['bg'] = args[2]
        if nargs >= 4:
            kwargs['fg'] = args[3]

        if button:
            tkTooltip(widget = button, text = text, **kwargs)
        else:
            self.print_warning("")
            self.print_warning(f"Warning in tkLauncherApp.add_tooltip(): Invalid button name [{args[0]}]")
            self.print_warning("")

    def new_dialog(self):
        self.dialog_config = [] #tkParams()

    def add_dialog(self, args):
        p = tkParams()
        nargs = len(args)
        if nargs >= 1:
            p.widget = args[0]
        else:
            p.widget = ''
        if nargs >= 2:
            p.var = args[1]
        else:
            p.var = ''
#        print("args=", p.widget, p.var, args[2:])

        d = {}
        v = []
        for s in args[2:]:
            m = re.match(r"\s*(\S+?)\s*=\s*(.*)\s*$", s)
#            print("m=", m, s)
            if m:
                g = m.groups()
#                print("g=", g)
                key = g[0]
                val = g[1]
                if re.search(r'^(--|[A-Z])', key[0]) or re.search(r'[:/+\-#\$\\]', key[0]):
                    v.append(s)
                else:
                    d[key] = pconv(val)
#                    print("key=", key, val)
            else:
                v.append(s)
#        print("args =", d)

        p.args = d
        p.vars = v
        self.dialog_config.append(p)

    def post_custom_dialog(self, vars):
#        print("")
#        print("Return variables")
#        print("ret.ret=", vars)
        for var in vars.keys():
            val = vars[var]
#            print(f" {var}={val}")
            self.set_var(var, val, set_env = False)

    def show_custom_dialog(self, args):
        title = args[0]
        if len(args) > 1:
            buttons = args[1]
        else:
            buttons = None
        
        ret = tkCustomDialog_by_config(master = self.main_window, app = self, 
                title = self.p(title), config = self.dialog_config, buttons = buttons,
                modeless = False,
                callback = lambda vars: self.post_custom_dialog(vars))

        if ret is None or ret.ret is None:
            self.print_warning("tkLauncherApp.show_custom_dialog(): Cancelled")
            return -1

        vars = ret.ret
        self.post_custom_dialog(vars)

        return 1

    def post_custom_dialog_modeless(self, vars):
        for var in vars.keys():
            val = vars[var]
            self.set_var(var, val, set_env = False)

    def show_custom_dialog_modeless(self, args):
        title = args[0]
        if len(args) > 1:
            buttons = args[1]
        else:
            buttons = None
        
        ret = tkCustomDialog_by_config(master = self.main_window, app = self, 
                title = self.p(title), config = self.dialog_config, buttons = buttons,
                modeless = True,
                callback = lambda vars: self.post_custom_dialog_modeless(vars))

        if ret is None or ret.ret is None:
            self.print_warning("tkLauncherApp.show_custom_dialog_modeless(): Cancelled")
            return -1

        vars = ret.ret
        self.show_custom_dialog_modeless(vars)

        return 1

    def show_select_dialog(self, args):
        var     = args[0]
        title   = args[1]
        message = args[2]
        options = args[3:]
        def_val = self.svars.get(var, "")
        for idx in range(len(options)):
            s = options[idx]
            s2 = re.sub(r'\s*#.*$', '', s).strip()
            if def_val == s2:
                def_val = s
                def_index = idx
                break

        width  = self.config.get("width_selectbox",  50)
        height = self.config.get("height_selectbox", 10)

        idlg = tkSelectDialog(master = self.root_window, app = self, title = self.p(title), 
                varname = var, message = message, options = options,
                width = width, height = height,
                def_index = def_index, is_print = False)
        s = idlg.ret

        if not s:
            self.print_warning("tkLauncherApp.show_select_dialog(): Cancelled")
            return -1

#        ret = re.sub(r'\s*#.*$', '', s).strip()
#        self.set_var(var, ret, set_env = False)
        self.set_var(var, s, set_env = False)

        return 1

    def input(self, args):
        var = None
        message = 'Input'
        title   = 'Input'
        n = len(args)
        if n >= 1:
            var = args[0]
        if n >= 2:
            message = args[1]
        if n >= 3:
            title = args[2]

        initialvalue = self.s_engine.vars.get(var, "")
        idlg = tkInputDialog(master = self.root_window, app = self, title = title, message = message,
                width = self.config.width_input_editbox, height = self.config.height_input_editbox,
                def_val = initialvalue, is_print = False)

        s = idlg.ret
#        s = tktkinter.askstring(self, title = title, message = message, initialvalue = initialvalue)
        if not s:
            self.print_warning("tkLauncherApp.input(): Cancelled")
#            s = initialvalue
            return -1

        lines = s.splitlines()
        line = ''
        for s in lines:
            s = s.strip()
            if len(s) < 1:
                continue

#            print("")
#            print(f"{s=}")
            if s[0] == '@':
                m = re.match(r'\@(\w+)\s*=(.*)$', s, re.MULTILINE | re.DOTALL)
                if m:
                    g = m.groups()
                    _var = g[0]
                    val = g[1].strip()
#                    print(f"set {_var=} {val=}")
                    self.set_var(_var, val, set_env = False)
                    var_l = _var.lower()
#                    print(f"{var_l=} {self.s_engine.vars[var_l]=}")
                    continue
            else:
                line += s + ' '
        
        self.set_var(var, line, set_env = False)

        return 1

    def del_quote(self, var):
        val = self.s_engine.vars.get(var, "")
        print("del quote: ", val)
        s1 = del_quote(val)
        print(" => ", s1)
        self.set_var(var, s1, set_env = False)

    def remove_comment(self, var):
        var_l = var.lower()
#        print(f"{var_l=}")
        val = self.s_engine.vars.get(var_l, "")
#        print(f"{val=}")
        s1 = re.sub(r'\s*#.*$', '', val)
#        print(f"{s1=}")
        self.set_var(var, s1, set_env = False)

    def get_first_word(self, args):
        s  = args[0]
        sep = args[1]
        var = args[2]
        m = re.match(rf'\s*(.*?)\s*{sep}', s)
        if m:
            ret = m.groups()[0]
            self.set_var(var, ret, set_env = False)

    def get_last_word(self, args):
        s  = args[0]
        sep = args[1]
        var = args[2]
        m = re.search(rf'{sep}\s*(.*?)\s*$', s)
        if m:
            ret = m.groups()[0]
            self.set_var(var, ret, set_env = False)

    def get_path_part(self, key, path, var):
        apath = os.path.abspath(path)
        if os.path.isdir(apath):
            dirname, basename, filebody, ext = apath, '', '', ''
        else:
            dirname, basename, filebody, ext = split_file_path(apath)

        last_dir = get_last_directory(apath)
        upper_dir = get_upper_directory(apath)
        drive, dirname_wo_drive = split_drive(apath)

        if key == 'drive':
            self.set_var(var, drive, set_env = False)
        elif key == 'dir':
            self.set_var(var, dirname, set_env = False)
        elif key == 'dir_without_drive':
            self.set_var(var, dirname_wo_drive, set_env = False)
        elif key == 'last_dir':
            self.set_var(var, last_dir, set_env = False)
        elif key == 'upper_dir':
            self.set_var(var, upper_dir, set_env = False)
        elif key == 'filename':
            self.set_var(var, basename, set_env = False)
        elif key == 'filebody':
            self.set_var(var, filebody, set_env = False)
        elif key == 'ext':
            self.set_var(var, ext, set_env = False)
        else:
            if self.alert_var_error:
                self.show_error(f"tkLauncherApp.get_path_part(): Invalid key [{key}]")

    def get_cur_dir(self, var):
        self.set_var(var, os.getcwd(), set_env = False)

    def get_cur_menu(self, var):
        tkvars  = self.tkvars.get_param_dict()
        sel = tkvars["menu_listbox"].curselection()
        if not sel:
            sel = self.cur_sel
        menu = tkvars["menu_listbox"].get(sel)
        self.set_var(var, menu)

    def get_cur_menu_file(self, var):
        tkvars  = self.tkvars.get_param_dict()
        sel = tkvars["menu_listbox"].curselection()
        if not sel:
            sel = self.cur_sel
        menu = tkvars["menu_listbox"].get(sel)
        path, fp, line = \
            self.s_engine.find_file_from_menu(self, menu, idx = None, task = None, script_files = None)
        self.set_var(var, path)

        return path

    def get_cur_button_idx(self, var):
        self.set_var(bst, self.last_ibutton)

        return self.last_ibutton

    def get_cur_button_caption(self, var):
        tkvars  = self.tkvars.get_param_dict()
        val = tkvars["submenu_buttons"][self.last_ibutton]["text"]
        self.set_var(var, self.last_ibutton)

        return val

    def split_str(self, args):
        str      = args[0]
        sep      = args[1]
        idx      = pint(args[2])
        var_name = args[3]
        a = str.split(sep)[idx]
        self.set_var(var_name, a)

        return a

    def check_file_mode(self, args):
        mode = args[0]
        self.set_var('check_file_mode', mode)

    def check_exist(self, args):
        path = args[0]
        if len(args) >= 2:
            message = args[1]
        else:
            message = None

        status = self.s_engine.vars.get('check_file_mode', 'auto')
        is_exist = os.path.exists(path)
        if status == 'true' or is_exist:
            return True

        tkvars  = self.tkvars.get_param_dict()
        button = tkvars["submenu_buttons"][self.last_ibutton]
        caption = button["text"]
#        print(f"346 caption={caption} ibutton={self.last_ibutton}")
#        button.configure(font = ("", 8, "normal", "roman", "normal", "overstrike"), fg = "red")
        button.set_text('!!! ' + caption)
        self.show_message_dialog(message)

        return False
                        

    def exit_if_defined(self, mode, var):
#        print("vars=", self.s_engine.vars)
        val = self.s_engine.vars.get(var, None)
        if mode is True and val is not None:
            tktkinter.dialog_showerror(self, title = 'Error', 
                    message = f"Variable [{var}] is defined. Exit.")
            return False
#            self.terminate(f"Terminated by exit_if_defined for [{var}]")
#            exit()
        elif mode is False and val is None:
            tktkinter.dialog_showerror(self, title = 'Error', 
                    message = f"Variable [{var}] is not defined. Exit.")
            return False
#            self.terminate(f"Terminated by exit_if_not_defined for [{var}]")
#            exit()
        return True

    def exit_if_exist(self, mode, path):
        f = os.path.exists(path)
        if mode is True and f:
            tktkinter.dialog_showerror(self, title = 'Error', 
                    message = f"File [{path}] exists. Exit.")
            return False
#            self.terminate(f"Terminated by exit_if_exist for [{path}]")
#            exit()
        if mode is False and not f:
            tktkinter.dialog_showerror(self, title = 'Error', 
                    message = f"File [{path}] does not exist. Exit.")
            return False
#            self.terminate(f"Terminated by exit_if_not_exist for [{path}]")
#            exit()
        return True
        
    def show_message_dialog2(app, message):
        tktkinter.dialog_showinfo(app, title = 'Information', message = message)

    def show_message_dialog(app, message):
        message = del_quote(message)
        tktkinter.dialog_showinfo(app, title = 'Information', message = message)
#        app.root_window.after(1, lambda: app.show_message_dialog2(del_quote(message)))

    def show_error_dialog2(app, message, terminate = False):
        tktkinter.dialog_showerror(app, title = 'Error', message = message)
        if terminate:
            app.terminate("Terminated by tkscript_macro.show_error()")

    def show_error_dialog(app, message, terminate = False):
        message = del_quote(message)
#        app.root_window.after(1, lambda: app.show_error_dialog2(message, terminate))

        tktkinter.dialog_showerror(app, title = 'Error', message = message)
        if terminate:
            app.terminate("Terminated by tkscript_macro.show_error()")

    def ask_okcancel_dialog2(app, message):
        return tktkinter.dialog_okcancel(app, title = 'Confirm', message = message)

    def ask_okcancel_dialog(app, message):
        message = del_quote(message)
        return tktkinter.dialog_okcancel(app, title = 'Confirm', message = message)

        """
app.print_warning("")
        app.print_warning(message)
        s = input("Enter 'OK' to continue>>")
        if s == 'OK':
            return ''
        else:
            return '2'
        """

#        return app.root_window.after(1, lambda: app.ask_okcancel_dialog2(del_quote(message)))

    def ask_yesno_dialog2(app, message):
#        return tktkinter.dialog_yesno(app, title = 'Confirm', message = message)
        return tktkinter.tkdialog_yesno(master = app.main_window, title = 'Confirm', message = message)

    def ask_yesno_dialog(app, message):
        message = del_quote(message)
        return tktkinter.dialog_yesno(app, title = 'Confirm', message = message)
#        return tktkinter.tkdialog_yesno(master = app.main_window, title = 'Confirm', message = message)

        """
        app.print_warning("")
        app.print_warning(message)
        s = input("Enter 'YES' to continue>>")
        if s == 'YES':
            return ''
        else:
            return '2'
        """

        return app.root_window.after(1, lambda: app.ask_yesno_dialog2(del_quote(message)))
        
    def read_labels(self, args):
        path = args[0]
        varname = args[1]
        xls = tkExcelDB(path, mode = 'r', table_name = None, OpenFile = True, CloseFile = True, 
                description = 'MS-Excel input', IsPrint = True)
        if xls.ws is None:
            print(f"\nError in tkLauncherApp.read_labels(): Can not read worksheet from [{path}]")
            return -1
        labels = xls.get_labels()

        """
        if '.xls' in path:
            df = pd.read_excel(path, engine = 'openpyxl')
        else:
            df = pd.read_csv(path)

        if df.empty:
            return -1

        labels = df.columns.tolist()
        """

        labels = [f"{s}" for s in labels]
        labels = "##".join(labels)
        self.set_var(varname, labels, set_env = False)

        return labels

    def get_file_list(self, args, target = 'file'):
        varname = args[0]
        dir = args[1]

        fmasks = []
        for fm in args[2:]:
            a = fm.split(';')
            fmasks.extend(a)

        files_dict = {}
        for a in fmasks:
            fmask = os.path.join(dir, a)
            files = glob.glob(fmask)
            for f in files:
                filename = None
                if 'file' in target and os.path.isfile(f):
                    filename = os.path.basename(f)
                if 'dir' in target and os.path.isdir(f):
                    filename = os.path.basename(f)
                if filename is not None:
                    files_dict[filename] = 1

        files = [f for f in sorted(files_dict.keys())]
        s = '##'.join(files)

        self.set_var(varname, s, set_env = False)

        return s

    def escape_reg(self, args):
        var = args[0]
        str = args[1]
        val = re.sub(r'([\$\\\/\(\)\[\]])', r'\\\1', str)
        self.set_var(var, val, set_env = False)

    def replace(self, args):
        var = args[0]
        str = args[1]
        _from = args[2]
        _to   = args[3]
        val = str.replace(_from, _to)
        self.set_var(var, val, set_env = False)

    def set_dialog_values(self, args):
#        print("\nset_dialog_values")
        var = args[0]
        str = args[1]

        dvars = self.__dict__.get("dialog_widget_vars", None)
#        print("dvars=", dvars)
        if dvars is None:
            return False

#        print("dvars=", dvars.__dict__)
        widget= dvars.__dict__.get(var, None)
#        print("widget=", widget)
        if widget is None:
            return False

        values = []
        for s in str.split('##'):
#            print("insert", s)
            values.append(s)
#        print("values=", values)
        try:
            widget['values'] = values
        except:
            pass
            
        return True

    def set_dialog_var(self, args):
        var = args[0]
        str = args[1]
        
        dvars  = self.__dict__.get("dialog_tkvars", None)
#        print("708")
        if dvars:
            ddict  = dvars.__dict__
            if ddict:
                try:
#                    self.print_warning("try d.tkvars")
                    ddict[var].set(str)
                    return
                except:
#                    self.print_warning("failed d.tkvars")
                    pass

#        print("735")
        tkvars = self.tkvars.get_param_dict()
        tkdict = tkvars.get(var, None)
        if tkdict:
            tkdict[var].set(str)

    
    def search_files(self, args):
        tkvars  = self.get_tkvars_dict()
        varname     = args[0]
        target_file = args[1]
        start_path  = args[2]

        path_list = find_files(target_pfile, start_dir = start_path)
        self.set_var(varname, '#'.join(path_list), set_env = False)

    def search_latest_file(self, args):
        tkvars  = self.get_tkvars_dict()
        varname     = args[0]
        target_file = args[1]
        start_path  = args[2]

        path = find_latest_file(target_file, start_dir = start_path, defval = None)
        print("set", varname, path)
        self.set_var(varname, path, set_env = False)

    def get_open_dir_name(self, args):
        nargs = len(args)
        fmask = '*.*'
        ini_dir = '.'
        caption = 'Select file to open'
        if nargs >= 1:
            ini_dir = args[0]
        if nargs >= 2:
            caption = " ".join(args[1:])

#        ftype = [("All", "*.*")]
        ftype =  [("Specified", fmask)]
#        print("args=", fmask, ini_dir, caption, ftype)
        selpath = tkinter.filedialog.askdirectory(initialdir = ini_dir, title = caption)
        if not selpath:
            return -1

        self.set_var('o', selpath, set_env = False)
#        self.s_engine.vars['o'] = selpath
#        self.svars.get_param_dict()['o'] = selpath
        tkvars  = self.get_tkvars_dict()
        tkvars['sel_file'].set(selpath)

        return 1

    def get_open_file_name(self, args):
        nargs = len(args)
        fmask = '*.*'
        ini_dir = '.'
        caption = 'Select file to open'
        if nargs >= 1:
            fmask = args[0]
        if nargs >= 2:
            ini_dir = args[1]
        if nargs >= 3:
            caption = " ".join(args[2:])

#        ftype = [("All", "*.*")]
        ftype =  [("Specified", fmask), ("All", "*.*")]
#        print("args=", fmask, ini_dir, caption, ftype)
        selpath = tkinter.filedialog.askopenfilename(filetypes = ftype, initialdir = ini_dir, title = caption)
        if not selpath:
            return -1

        self.set_var('o', selpath, set_env = False)
#        self.s_engine.vars['o'] = selpath
#        self.svars.get_param_dict()['o'] = selpath
        tkvars  = self.get_tkvars_dict()
        tkvars['sel_file'].set(selpath)

        return 1

    def get_save_file_name(self, args):
        nargs = len(args)
        fmask = '*.*'
        ini_dir = '.'
        caption = 'Select file to save'
        if nargs >= 1:
            fmask = args[0]
        if nargs >= 2:
            ini_dir = args[1]
        if nargs >= 3:
            caption = " ".join(args[2:])

#        ftype = [("All", "*.*")]
        ftype =  [("Specified", fmask)]
        selpath = tkinter.filedialog.asksaveasfilename(filetypes = ftype, initialdir = ini_dir, title = caption)
        if not selpath:
            return -1

        self.set_var('o', selpath, set_env = False)
#        self.s_engine.vars['o'] = selpath
#        self.svars.get_param_dict()['o'] = selpath
        tkvars  = self.get_tkvars_dict()
        tkvars['sel_file'].set(selpath)

        return 1

    def move_top(self, f = True):
        self.root_window.attributes("-topmost", f)

    def show_window(self, mode):
#Minimize/Maximize/Normal/Hide/TopMost/NoTopMost
        if mode == 'topmost':
            self.move_top(True)
        elif mode == 'notopmost':
            self.move_top(False)
        elif mode == 'minimize':
            self.root_window.iconify()
        elif mode == 'maximize':
#            self.root_window.maxsize()
            self.root_window.state('zoomed')
        elif mode == 'normal' or mode == 'show':
            self.root_window.deiconify()
        elif mode == 'hide':
            self.root_window.withdraw()

    def set_window_title(self, title):
#        print("self.set_window_title", title)
        self.window_title = title
        self.root_window.title(title)

    def set_message(self, message):
        tkvars  = self.get_tkvars_dict()
        tkvars['message'].set(message)

    def set_selected_file(self, message):
        tkvars  = self.get_tkvars_dict()
        tkvars['sel_file'].set(message)

    def set_command_line(self, message):
        tkvars  = self.get_tkvars_dict()
        tkvars['command_line'].set(message)

    def set_command_line_org(self, message):
        tkvars  = self.get_tkvars_dict()
        tkvars['command_line_org'].set(message)

    def set_argument(self, message):
        tkvars  = self.get_tkvars_dict()
        tkvars['argument'].set(message)

    def show_message(self, message):
        tkvars  = self.get_tkvars_dict()
        tkvars['message'].set(message)

