import os
#import platform
import sys
import re
import glob
import subprocess
import shutil

from tklib.tkobject import tkObject
from tklib.tkparams import tkParams
from tklib.tkutils import pint, pfloat
from tklib.tkutils import del_quote, split_two, split_command_line, quote_command_if_space
from tklib.tkfile import get_encoding, open_chardet
import tklib.tkgui.tktkinter as tktkinter


#=========================
# Application base class
#=========================


class tkScript(tkObject):
    def __init__(self, script_files = [], **args):
        super().__init__()
        
        self.script_files = script_files
        self.vars = { "check_file_mode": "auto" }
        self.script_inf = {}

        self.update(**args)


# Read continued lines with the line-end \
    def read_cont_lines(self, fp):
            s = ''
            while 1:
                line = fp.readline()
                if not line:
                    return s

                line = line.strip()
                n = len(line)
                if n == 0 or line[n-1] != '\\':
                    s += ' ' + line
                    break

                s += ' ' + line[0:n-1]

            return s.strip()

    def decompose_section(self, section):
        if section[0] != '[':
            m = re.search(r'Button(\d+)\.(\S.+)$', section)
            if m:
                return m.groups()[1], pint(m.groups()[0]), ''
            else:
                return section, '', ''

        m = re.search(r'\[\s*Button(\d+)\.(\S.+)\s*\]', section)
        if m:
            menu = m.groups()[1]
            idx  = m.groups()[0]
            m = re.search(r'\]\.(\S*)', section)
            if m:
                return menu, pint(idx), m.groups()[0]
            else:
                return menu, pint(idx), ''
        
        return section, '', ''

    def find_file_from_mainmenu(self, app, main_menu, task = None, script_files = None):
        main_menu_reg = re.sub(r'([\[\]\(\)\.\\\-])', r'\\\1', main_menu)
#        print("m=", main_menu_reg)
        for path in script_files:
#            print(f"a  Read [{path}]")
            try:
                fp = open_chardet(path, 'r', def_encoding = 'shift_jis')
            except:
                app.print_warning(f"Warning in tkscript_macto.find_file_from_mainmenu(): Can not read [{path}]")
                continue

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

                line = line.strip()
                if line == '' or line[0] != '[':
                    continue

# Check [Boot] etc
#                print(f"  line={line}")
                if line == main_menu:
#                    print(f"91 idx=None: return {path=} {line=}")
                    return path, fp, line

# Check [section]
                match = re.match(r'\[\s*(\S.*)\s*\](.*?)\s*$', line)
                if not match:
                    continue

                sec   = app.p(match.groups()[0])
                task2 = match.groups()[1]
                if task2 != "" and task2[0] == '.':
                    task2 = task2[1:]
#                print(f"   100 sec={sec}")
                if sec == main_menu and (task is None or task == ''):
                    return path, fp, line
                if sec == main_menu and task2 == task:
                    return path, fp, line

        return None, None, None

    def find_file_from_button(self, app, main_menu, idx = None, task = None, script_files = None):
#        print("")
#        print("find_file_from_button")
#        print(f"112: main_manu={main_menu} idx={idx} task={task}")
#        print("script_files=", script_files)
        main_menu_reg = re.sub(r'([\[\]\(\)\.\\\-])', r'\\\1', main_menu)
#        print(f"119: main_menu_reg={main_menu_reg}")
        for path in script_files:
#            print(f"a  Read [{path}]")
            try:
                fp = open_chardet(path, 'r', def_encoding = 'shift_jis')
            except:
                app.print_warning(f"Warning in tkscript_macto.find_file_from_button(): Can not read [{path}]")
                continue

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

                line = line.strip()
                if line == '' or line[0] != '[':
                    continue

#                print("")
#                print("138: line=", line)
#                print(f"  test main_menu={main_menu}")

# Check [ButtonXX.AAA]
                match = re.match(r'\[Button(\d+)\.(.*)\s*]', line)
                if not match:
#                    print("143: no match")
                    continue

                sec  = app.p(match.groups()[1])
                idx2 = pint(match.groups()[0])
#                print(f"149 {sec=} {idx=} {idx2=}")
                if sec != main_menu and sec != app.p(main_menu):
                    continue

#                print(f"  152: passed main_menu {main_menu}")
                if idx is None:
                    print(f"155 idx=None: return path={path} line={line}")
                    return path, fp, line
                else:
#                    print(f"157 idx!=None")
#                    print(f"  test idx idx={idx} vs idx2={idx2}")
                    if idx2 == pint(idx):
#                        print(f"  122 test task for task={task}")
                        if task is None or task == '':
#                            print(f"1 idx!=None task=None: return {path=} {line=}")
                            return path, fp, line

# Check [ButtonXX.AAA].help/dbl_click
#                        print(f"{line=}")
                        match = re.search(r'\]\.(\S+)', line)
                        if match:
                            t = match.groups()[0]
#                            print(f"    170: t={t} task={task}:")
                            if match and task == t:
#                                print(f"1 idx!=None task={t}: return {path=} {line}")
                                return path, fp, line
                        else:
#                            print(f"175: No match: return {path=} {line}")
#                            return path, fp, line
                            continue
    
        return None, None, None

    def find_file_from_menu(self, app, main_menu, idx = None, task = None, script_files = None):
#        print(f"68 main_menu={main_menu}, idx={idx} task={task}")
#        print("script_files=", script_files)
#        print("idx=", main_menu, idx, task)
        if script_files is None:
            script_files = self.script_files
#        print("script_files=", script_files)
#        print(f"tkscript_macro.find_file_from_menu(): menu={main_menu} idx=", idx, "  task=", task)
        if idx is None:
#            print("184")
            path, fp, line = self.find_file_from_mainmenu(app, main_menu, task, script_files = script_files)
        else:
#            print("187")
            path, fp, line = self.find_file_from_button(app, main_menu, idx, task, script_files = script_files)
        
#        print("p=", path, fp, line)

        return path, fp, line

    def get_main_menus(self, app):
#        print("f=", self.script_files)

        print("")
        print("Read menus")
        menus = {}
        for path in self.script_files:
            print(f"  Read [{path}]")
            try:
                fp = open_chardet(path, 'r')
#                fp = open(path, 'r')
            except:
                app.print_warning("")
                app.print_warning(f"Warning in tkscript_macto.get_main_menus(): Can not read [{path}]")
                continue

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

                if line == '':
                    continue

                if is_header:
                    if 'exit_if_not_exist' in line:
                        a = line.split()
                        a = self.convert_vars(app, a)
                        if len(a) >= 2 and not os.path.exists(a[1]):
                            print(f"    tkscript_macro.get_main_menus(): Exit by exit_if_not_exist [{a[1]}]")
                            break
                
                if line[0] == '[':
                    is_header = False
                else:
                    continue

                match = re.match(r'\[\s*Button(\d+)\.\s*(.*)\s*\]', line)
                if not match:
                    continue

                idx  = match.groups()[0]
                menu = match.groups()[1]
#                 print("    line:", line.strip())

                if menus.get(menu, None) is None:
                    menus[menu] = { "paths": [path] }
                else:
                    menus[menu]["paths"].append(path)

            fp.close()

        self.script_inf = {}
        for menu in menus:
            self.script_inf[menu] = { "paths": set(menus[menu]["paths"]) }
            m = app.p(menu)
            if m != menu:
               self.script_inf[m] = self.script_inf[menu]

#        for menu in self.script_inf.keys():
#            print(f"{menu}: ", self.script_inf[menu])
        ms = list(menus.keys())
#        print("ms=", ms)
#        exit()
        
        return ms

    def get_caption(self, app, main_menu):
#        print(f"{main_menu=}")
        inf = self.script_inf.get(main_menu, None)
        if inf:
            path, fp, line = \
                self.find_file_from_menu(app, main_menu, idx = None, task = None, script_files = inf["paths"])
        else:
            path, fp, line = self.find_file_from_menu(app, main_menu, idx = None, task = None)

        if fp is None:
            return None

        fp.close()

#        print("get_submenus; read", path)
        fp = open_chardet(path, 'r')
#        fp = open(path, 'r')
        if not fp:
            return None

        main_menu_reg = re.sub(r'([\[\]\(\)\.\-\\])', r'\\\1', main_menu)
#        print(f"{main_menu_reg=}")
        submenus = []
        caption = 'no caption'
        while 1:
            line = fp.readline()
            if not line:
                break

            line = line.strip()

#            print(f"{line=}")
#            print(f"{main_menu_reg=}", '\[RButton6\]')
#            match = re.match(r'\[RButton6\]', line)
            match = re.match(main_menu_reg, line)
            if match:
                pass
            elif not match:
                continue

                match = re.match(r'\[Button\d+\.(.*)\s*]', line)
                if not match:
                    continue

                sec = app.p(match.groups()[0])
                if sec != main_menu:
                    continue

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

                line = line.strip()
                if line == '':
                    continue

                if line[0] == '[' or (len(line) >= 3 and line[0:3].lower() == 'end'):
                    fp.seek(pos)
                    break

                match = re.match(r'\s*Caption\s*=\s*(.*)\s*$', line, re.IGNORECASE)
                if not match:
                    continue

                caption = match.groups()[0]

                break

        fp.close()
        
        return caption

    def get_submenus(self, app, main_menu):
#        print("")
#        print("get_submenus: 345 in")
#        print("{main_menu=}")
        inf = self.script_inf.get(main_menu, None)
        if inf:
            path, fp, line = \
                self.find_file_from_menu(app, main_menu, idx = None, task = None, script_files = inf["paths"])
        else:
            path, fp, line = self.find_file_from_menu(app, main_menu, idx = None, task = None)
#        print(f"{path=} {line=}")
        if fp is None:
            return []

        fp.close()

#        print("get_submenus; read", path)
        fp = open_chardet(path, 'r')
#        fp = open(path, 'r')
        if not fp:
            return []

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

#            print(f"369: {line=}")
            match = re.match(r'\[Button(\d+)\.(.*)\s*]', line)
            if not match:
                continue

            sec = app.p(match.groups()[1])
            if sec != main_menu:
                continue

            idx = match.groups()[0]
            match = re.search(r'\]\.(\S+)', line)
            if match:
                task = match.groups()[0]
            else:
                task = ''

            caption = 'none'
            pos = fp.tell()
            while 1:
                line = fp.readline()
                if not line:
                    break

#                print(f"394: {line=}")
                line = line.strip()
                if line == '':
                    continue

                if line[0] == '[' or (len(line) >= 3 and line[0:3].lower() == 'end'):
                    fp.seek(pos)
                    break

                cmd, argline, args = split_command_line(line)

                cmd = cmd.lower()
                nargs = len(args)
                app.alert_var_error = False
                if cmd == 'set' and nargs >= 1:
                    argline, = self.convert_vars(app, [argline])
                    app.set(argline)
                elif cmd == 'join_path':
                    args = self.convert_vars(app, args)
                    app.join_path(args)
                elif cmd == 'use_os_path_sep' and nargs >= 1:
                    args = self.convert_vars(app, args)
                    app.use_os_path_sep(args[0])
                elif cmd == "check_exist":
                    path, = self.convert_vars(app, [args[0]])
#                    print("  416: check_exist: ", line)
#                    print("                    ", args)
#                    print("                    ", path)
                    status = self.vars.get('check_file_mode', 'auto')
#                    print(f"413: path={path}  status={status} exist=", os.path.exists(path))
                    if status == 'false':
                        caption = '!!! ' + caption
                    elif not os.path.exists(path):
#                        print(f"tkscript_macro.get_submenus(): 417 {path} does not exist")
                        caption = '!!! ' + caption
                app.alert_var_error = True

                match = re.match(r'\s*Caption\s*=\s*(.*)\s*$', line, re.IGNORECASE)
                if not match:
                    continue

                caption = match.groups()[0]
#                break

            submenus.append({ "idx": pint(idx), "task": task, "caption": caption })

        fp.close()

#        print("341: submenus=", submenus)
        return submenus

# Add variables to app.svars, and create self.vars as a dict with lower-character keys
    def update_script_vars(self, app, cparams): 
        svars = app.svars
        vars  = svars.get_param_dict()
        env   = app.env
        argv  = app.argv

        vars.update(env)

        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", 
                "script_dir", "group", "user", "user_nospace"
                ]
#                "python_path", "perl_path"]
        for key in keys:
            vars[key] =  app.get(key, None)

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

        vars['p'] = app.script_path
        vars['i'] = app.inifile
        vars['s'] = os.getcwd()
        vars['n'] = '\n'

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

        vars['a'] = s.strip()
        
        self.vars = {}
        for key in vars.keys():
            skey = key.lower()
            self.vars[skey] = vars[key]
#        print(f"{self.vars=}")

        return self.vars

    def show_message(self, app, message):
        app.show_message(message)

    def convert_a_var(self, app, var):
        idebug = 0

        vars = app.s_engine.vars

        ret = ''
        rest = var

#        print("")
#        print(f"499 var=[{var}]  rest=[{rest}]")
        while 1:
# Separate by '$'
#            print(f"509: rest=[{rest}]  ret=[{ret}]")
            m = re.match(r'(.*?)\$(.*)$', rest, flags = re.MULTILINE | re.DOTALL)
            if not m:
                ret = ret + rest
#                print(f"512 hit (.*?)$(.*)  return ret=[{ret}]")
                return ret

            g = m.groups()
            pre = g[0]
            n   = len(pre)
            rest = g[1]
            if idebug >= 2:
                print(f"  401: pre={pre} rest={rest}")

#            print(f"528 pre=[{pre}] rest=[{rest}] ret=[{ret}] n={n}")
            if n > 0 and pre[n-1] == '\\':
# for '\$' => '$'
                ret += pre[0:n-1] + '$'
                continue
            elif rest == '':
# for '$ ' => '$ '
                ret += pre + '$'
            elif len(rest) == 1:
# for '$x' => '[x]' if $x exists, but => '$x' if not
                val = vars.get(rest, '$' + rest)
# rest was processed so should be replaced with ''
                rest = ''
                ret += pre + val
# Case rest = 'abc\$def', should be converted to 'abc$def'
            else:
#                print(f"ret, pre, rest=[{ret}] [{pre}] [{rest}]")
                ret += pre

# Case rest = 'abc$def', should be converted to 'abc$def'
            m = re.match(r'(\w\w+.*)$', rest)
            if m:
#                print(f"{ret=} {rest=}", m.groups())
                ret += '$'

                continue


# Case rest = 'abc$i def', should be converted to 'abc[ini_path] def'
            m = re.match(r'([a-zA-Z])([^\w_].*)?$', rest, flags = re.MULTILINE | re.DOTALL)
            if m:
                g = m.groups()
                n = len(g)
                varname = g[0].lower()
                rest = g[1]
                if rest is None:
                    rest = ''
#                print(f"545 hit abc$i def for varname={varname} rest={rest}")

                val = vars.get(varname, None)
#                print(f"   413:{var=} {val=}")
                if val is None and len(varname) > 1:
                    if app.alert_var_error:
                        app.show_error_dialog(f"tkscript_macro.convert_a_var(): Invalid varname [${varname}]")
                    val = f"??{varname}??"
#                else:
#                    val = '$' + varname

                if val is not None:
                    ret += val

                continue

# Case rest = 'abc$(tkProgRoot)def', should be converted to 'abc[tkProgRoot]def'
            m = re.match(r'\(([\w\.\:\_\-]+)\)(.*)$', rest, flags = re.MULTILINE | re.DOTALL)
            if m:
#                print("566 hit abc$(cdf)")
                g = m.groups()
                varname = g[0].lower()
                rest = g[1]

                val = vars.get(varname, None)
#                print(f"572 varname=[{varname}]  val=", val)
                if val is None and len(varname) > 1:
                    if app.alert_var_error:
                        app.show_error_dialog(f"tkscript_macro.convert_a_var(): Invalid varname [${varname}]")
                    val = f"??{varname}??"
                elif val is None:
                    val = f"??{varname}??"

                if type(val) is int or type(val) is float:
                    ret += f"{val}"
                else:
                    ret += val

                continue

        return ret
        
    def convert_vars(self, app, vars):
#        print("")
#        print("")
#        print(f"607: convertvars: vars=[{vars}]")
        for i in range(len(vars)):
#            print(f"**594 i={i}: [{vars[i]}]")
            vars[i] = self.convert_a_var(app, vars[i])
#            print("**596    ", vars[i])
        
        return vars

    def execute_external_command(self, app, cmd, args, is_print = False):
#        print(f"487 {cmd=} {args=}")
        vars = app.s_engine.vars

        python_path = quote_command_if_space(vars.get("python_path", "python"), special_chars = "|><")
        perl_path   = quote_command_if_space(vars.get("perl_path", "perl"), special_chars = "|><")
        cmd0 = python_path

#実行コマンド文字列の作成
        cmd = quote_command_if_space(cmd, special_chars = "|><")

#引数を連結
        command_line = ''
        argline = ''
        if re.search(r"\.py[\"\']?$", cmd):
#cmdがpythonスクリプトの場合、python_pathを追加
            command_line += python_path + ' ' + cmd
        elif re.search(r"\.pl[\"\']?$", cmd):
#cmdがperlスクリプトの場合、perl_pathを追加
            command_line += perl_path + ' ' + cmd
        else:
#その他
            command_line += quote_command_if_space(cmd, special_chars = "|><*?+")  # f'"{cmd}"'
#            command_line += quote_command_if_space(cmd, special_chars = "|><")  # f'"{cmd}"'
            cmd0 = quote_command_if_space(cmd, special_chars = "|><")
#        print(f"519 cmd={cmd}")
#        print(f"520 command_line={command_line}")

        for p in args:
#            print(f"525 p={p}")
            if 'start' in cmd:
                p = quote_command_if_space(p, special_chars = "|><&")
            else:
                p = quote_command_if_space(p, special_chars = "|><")
#            print(f"526 p={p}")
            command_line += ' ' + p
            argline += ' ' + p
#        print(f"526 command_line={command_line}")

        command_line = command_line.strip()
        argline = argline.strip()
        app.set_command_line(command_line)
#        print(f"529 command_line={command_line}")

        if is_print:
            print(f"524 command_line={command_line}]")

#        print(f"{cmd0=}")
#        print(f"{argline=}")
#        print(f"528 command_line={command_line}]")

        if app.confirm():
            idlg = tktkinter.tkInputDialog(master = app.root_window, app = app, title = "Execute?", 
                    message = f"Execute this command?", width = 50, def_val = command_line, is_print = False)
            if not idlg.ret:
                print("tkLauncherApp.execute_external_command(): Cancelled")
                return -1

            command_line = idlg.ret

# 最初に os.system() をためす。同期実行
        if app.config.debug or is_print:
            print(f"** Execute os.system({command_line})")

        ret = os.system(command_line)
        self.proc = None
        if ret == 0:
            if app.config.debug or is_print:
                print(f"os.system succeeded (ret={ret}) cmd=[{command_line}]")
            return ret
        else:
            if app.config.debug or is_print:
                app.print_warning("")
                app.print_warning(f"Error in tkscript_macro.execute_external_command(): os.system faieled (ret={ret}) cmd=[{command_line}]")
                app.print_warning("")


#非同期実行は管理しない。startコマンドを使う
        """
# Popenを試す。非同期実行
        ret = 1
        try:
            cmd0 = del_quote(cmd0)
#            print(f"Popen: cmd0={cmd0} argline={argline}")
            if argline is None or argline == '':
                self.proc = subprocess.Popen([cmd0])
            else:
                self.proc = subprocess.Popen([cmd0, argline])
            ret = self.proc
        except:
# Popenのあとsystemを試したりもしていた
#            print(f"system: command_line={command_line}")
            ret = os.system(command_line) >> 8
            self.proc = None
        """


#        print("ret=", ret)
        return ret

    def execute_a_command(self, app, cparams, line, fp = None, event = None):
#        print("vars=", self.vars)
#        print(f"681: [{line}]")
        
        line = line.strip()
        if line == '':
            return 1

        if line[0] in '#:;':
            return 1

#        if app.config.debug:
#            print(f"    cmd_line_org: [{line}]")

        line_org = line
#        print(f"706 line=[{line}]")
        line, = self.convert_vars(app, [line])
#        print(f"696 line=[{line}]")

#        if app.config.debug:
#            print(f"    cmd_line_conv: [{line}]")

        cmd, argline, args = split_command_line(line)
#        print(f"701    cmd: {cmd=} {argline=} {args=}")
#        if app.config.debug:
#            print(f"586    cmd: [{cmd}] [{argline}]:", args)
#        print(f"705    cmd: [{cmd}] [{argline}]:", args)

#        cmd, argline = self.convert_vars(app, [cmd, argline])
#        print(f"720 args=", args)
        args = self.convert_vars(app, args)
#        print(f"707 args=", args)

        cmd_org = cmd
        cmd = cmd.lower()
        nargs = len(args)
#        app.set_command_line(line)
        
        def check_nargs(cmd, key, nargs, min_nargs):
            if cmd != key:
                return False
            if nargs < min_nargs:
                app.print_warning("")
                app.print_warning(f"Error: cmd={key} has {nargs} argments but {min_nargs} args are required")
                app.print_warning("")
                return False
            return True

        ret = 1
        if app.config.debug:
            print(f"    cmd_conv: cmd=[{cmd}]")
            print(f"          argline=[{argline}]:")
            for i in range(len(args)):
                print(f"      args[{i}]: [{args[i]}]")

#        print(f"730 cmd=[{cmd}]")
        if cmd == 'rem':
            return 1
        elif re.match(r'\s*caption\s*=', cmd):
            pass

        elif cmd == 'bye':
#            app.terminate("Terminated by command BYE")
            app.on_closing(do_confirm = False)

        elif cmd == 'end':
            return -1
        elif cmd == 'call' and nargs >= 1:
            try:
                fp.close()
                fp = None
            except:
                pass

            menu, idx, action = self.decompose_section(args[0])
            if idx is None or idx == "":
                idxm1 = None
            else:
                idxm1 = pint(idx) - 1
#            ret = self.execute(app, cparams, menu, idxm1, action = action, i_mouse_button = '1')
            ret = self.execute(app, cparams, menu, idxm1, action = action, i_mouse_button = '1', script_files = self.script_files)
            if ret is not None and (type(ret) is int and ret <= 0):
                return ret

        elif cmd == 'debug' and nargs >= 1:
            if args[0].lower() == 'on':
                app.set_debug(True)
            else:
                app.set_debug(False)
        elif cmd == 'confirm' and nargs >= 1:
            if args[0].lower() == 'on':
                app.set_confirm(True)
            else:
                app.set_confirm(False)

        elif cmd == 'load_menu':
            app.call('none', 'initialize_launcher_menus', app, cparams)

        elif check_nargs(cmd, 'read_ini', nargs, 4):
#        elif cmd == 'read_ini' and nargs >= 4:
            app.read_ini(args)
        elif check_nargs(cmd, 'write_ini', nargs, 4):
#        elif cmd == 'write_ini' and nargs >= 4:
            app.write_ini(args)
        elif cmd == 'read_ini_all' and nargs >= 2:
            app.read_ini_all(args)
        elif (cmd == 'read_ini_to_vars' or cmd == 'load_dotenv') and nargs >= 1:
            app.read_ini_to_vars(args)

        elif cmd == 'print_all' and nargs >= 1:
            app.print_all(args)

        elif cmd == 'get_cur_dir' and nargs >= 1:
            cwd = os.getcwd()
            app.set_var(args[0], cwd)
        elif (cmd == 'chdir' or cmd == 'cd') and nargs >= 1:
            if os.path.isdir(args[0]):
                os.chdir(args[0])
        elif (cmd == 'mkdir' or cmd == 'md') and nargs >= 1:
            for d in args:
                print(f"*** mkdir {d}")
                os.mkdir(d)
        elif (cmd == 'rmdir' or cmd == 'rd') and nargs >= 1:
            for d in args:
                print(f"*** rm {d}")
                os.rmdir(d)
        elif (cmd == 'copy' or cmd == 'cp') and nargs >= 2:
            target = args[nargs-1]
            for fm in args[:nargs-1]:
                for f in glob.glob(fm):
                    print(f"*** copy {f} to {target}")
                    shutil.copy2(f, target)
        elif (cmd == 'move' or cmd == 'mv') and nargs >= 2:
            target = args[nargs-1]
            for fm in args[:nargs-1]:
                for f in glob.glob(fm):
                    print(f"*** move {f} to {target}")
                    shutil.move(f, target)
        elif (cmd == 'delete' or cmd == 'del' or cmd == 'rm') and nargs >= 1:
            for fm in args:
                for f in glob.glob(fm):
                    print(f"*** delete {f}")
                    os.remove(f)

        elif check_nargs(cmd, 'echo', nargs, 1):
            app.echo(argline)
        elif check_nargs(cmd, 'print', nargs, 1):
            app.echo(argline)
        elif cmd == "get_cur_menu" and nargs >= 1:
            app.get_cur_menu(args[0])
        elif cmd == "get_cur_menu_file" and nargs >= 1:
            app.get_cur_menu_file(args[0])
        elif cmd == "get_cur_button_idx" and nargs >= 1:
            app.get_cur_button_idx(args[0])
        elif cmd == "get_cur_button_caption" and nargs >= 1:
            app.get_cur_button_caption(args[0])

        elif cmd == "split_str" and nargs >= 4:
            app.split_str(args)

        elif cmd == "show_window" and nargs >= 1:
            app.show_window(args[0].lower())
        elif cmd == "set_title" and nargs >= 1:
            app.set_window_title(argline)

        elif cmd == "save_config":
            app.save_config(argline)

        elif cmd == "del_quote" and nargs >= 2:
            app.del_quote(args[0])
        elif cmd == "remove_comment" and nargs >= 1:
            app.remove_comment(args[0])
        elif cmd == "get_first_word" and nargs >= 3:
            app.get_first_word(args)
        elif cmd == "get_last_word" and nargs >= 3:
            app.get_last_word(args)

        elif check_nargs(cmd, 'set', nargs, 1):
            app.set(argline)
        elif check_nargs(cmd, 'list_append', nargs, 2):
            app.list_append(args)
        elif check_nargs(cmd, 'list_append_if_exist', nargs, 2):
            app.list_append(args, check_exist = True)
        elif cmd == "set_if_blank" and nargs >= 1:
            app.set_if_blank(argline)
        elif cmd == "set_if_not_blank" and nargs >= 1:
            app.set_if_not_blank(argline)
        elif cmd == "set_if_null" and nargs >= 1:
            app.set_if_null(argline)
        elif cmd == "set_if_not_null" and nargs >= 1:
            app.set_if_not_null(argline)
        elif cmd == "set_path" and nargs >= 1:
            app.set_path(argline)
        elif cmd == "join_path" and nargs >= 3:
            app.join_path(args)

        elif cmd == 'use_os_path_sep' and nargs >= 1:
            app.use_os_path_sep(args[0])
        elif cmd == 'get_drive' and nargs >= 2:
            app.get_path_part('drive', args[0], args[1])
        elif cmd == 'get_directory' and nargs >= 2:
            app.get_path_part('dir', args[0], args[1])
        elif cmd == 'get_directory_without_drive' and nargs >= 2:
            app.get_path_part('dir_without_drive', args[0], args[1])
        elif cmd == 'get_last_directory' and nargs >= 2:
            app.get_path_part('last_dir', args[0], args[1])
        elif cmd == 'get_upper_directory' and nargs >= 2:
            app.get_path_part('upper_dir', args[0], args[1])
        elif cmd == 'get_filename' and nargs >= 2:
            app.get_path_part('filename', args[0], args[1])
        elif cmd == 'get_filebody' and nargs >= 2:
            app.get_path_part('filebody', args[0], args[1])
        elif cmd == 'get_ext' and nargs >= 2:
            app.get_path_part('ext', args[0], args[1])
        elif cmd == 'get_cur_dir' and nargs >= 1:
            app.get_cur_dir(args[0])

        elif cmd == "get_open_file_name":
            if app.get_open_file_name(args) <= 0:
                return -1
        elif cmd == "get_save_file_name":
            if app.get_save_file_name(args) <= 0:
                return -1
        elif cmd == "get_open_dir_name":
            if app.get_open_dir_name(args) <= 0:
                return -1

        elif check_nargs(cmd, 'search_files', nargs, 3):
            app.search_files(args)
        elif check_nargs(cmd, 'search_latest_file', nargs, 3):
            app.search_latest_file(args)

        elif check_nargs(cmd, 'escape_reg', nargs, 2):
            app.escape_reg(args)

        elif check_nargs(cmd, 'replace', nargs, 4):
            app.replace(args)

        elif cmd == "read_labels" and nargs >= 2:
            ret = app.read_labels(args)
            if type(ret) is int and ret <= 0:
                return -1

        elif cmd == "get_file_list" and nargs >= 1:
            app.get_file_list(args, target = 'file')

        elif cmd == "get_dir_list" and nargs >= 1:
            app.get_file_list(args, target = 'dir')

        elif cmd == "set_dialog_values" and nargs >= 2:
            ret = app.set_dialog_values(args)
            if type(ret) is int and ret <= 0:
                return -1

        elif cmd == "set_dialog_var" and nargs >= 2:
            app.set_dialog_var(args)

        elif cmd == "set_file_list" and nargs >= 1:
            app.get_file_list(args)

        elif cmd == 'setup':
            app.setup(args)
        elif cmd == 'copy_config2scars':
            app.copy_config2scars(args)
        elif cmd == 'copy_svars2config':
            app.copy_svars2config(args)
        
        elif cmd == 'color_dialog' and nargs >= 1:
            ret = app.show_color_dialog(args)
            if ret <= 0:
                return -1
        elif cmd == 'font_dialog' and nargs >= 1:
            ret = app.show_font_dialog(args)
            if ret <= 0:
                return -1

        elif cmd == "input":
            ret = app.input(args)
            if ret <= 0:
                return -1
        elif cmd == 'select_dialog' and nargs >= 1:
            ret = app.show_select_dialog(args)
            print("ret=", ret)
            if ret <= 0:
                return -1

        elif check_nargs(cmd, 'eval', nargs, 2):
            app.eval(args)

        elif check_nargs(cmd, 'add_tooltip', nargs, 2):
            app.add_tooltip(args)
        elif check_nargs(cmd, 'create_menu', nargs, 3):
            app.create_menu(args, self, cparams, fp)
        elif check_nargs(cmd, 'add_context_menu', nargs, 2):
            app.add_context_menu(args)
        elif check_nargs(cmd, 'show_context_menu', nargs, 1):
            app.show_context_menu(args, event)

        elif cmd == 'new_dialog':
            app.new_dialog()
        elif cmd == 'add_dialog' and nargs >= 1:
            app.add_dialog(args)
        elif cmd == 'custom_dialog' and nargs >= 1:
            ret = app.show_custom_dialog(args)
            if ret <= 0:
                return -1
        elif cmd == 'custom_dialog_modeless' and nargs >= 1:
            ret = app.show_custom_dialog_modeless(args)
            if ret <= 0:
                return -1

        elif cmd == 'message_dialog' and nargs >= 1:
            app.show_message_dialog(argline)
        elif cmd == 'ask_yesno_dialog' and nargs >= 1:
            ret = app.ask_yesno_dialog(argline)
#            print("ret=", ret)
            if not ret:
                print("Cancelled")
                return -1
        elif cmd == 'ask_okcancel_dialog' and nargs >= 1:
            ret = app.ask_okcancel_dialog(argline)
#            print("ret=", ret)
            if not ret:
                print("Cancelled")
                return -1

        elif cmd == 'set_message' and nargs >= 1:
            app.set_message(argline)
        elif cmd == 'set_selected_file' and nargs >= 1:
            app.set_selected_file(argline)
        elif cmd == 'set_command_line' and nargs >= 1:
            app.set_command_line(argline)
        elif cmd == 'set_argument' and nargs >= 1:
            app.set_argument(argline)
        elif cmd == 'add_path' and nargs >= 1:
            app.add_path(args)

        elif check_nargs(cmd, 'get_app_path', nargs, 3):
            ret = app.get_app_path(args)
            if not ret:
                print(f"tkscript_macro.execute_a_command(): File select cancelled")
                return -1

        elif cmd == 'check_file_mode' and nargs >= 1:
            app.check_file_mode(args)
        elif cmd == 'check_exist' and nargs >= 1:
            ret = app.check_exist(args)
            if not ret:
                return -1
        elif cmd == 'exit_if_defined' and nargs >= 1:
            if not app.exit_if_defined(True, args[0]):
                return -1
        elif cmd == 'exit_if_not_defined' and nargs >= 1:
            if not app.exit_if_defined(False, args[0]):
                return -1
        elif cmd == 'exit_if_defined' and nargs >= 1:
            if not app.exit_if_defined(True, args[0]):
                return -1
        elif cmd == 'exit_if_not_exist' and nargs >= 1:
            if not app.exit_if_exist(False, args[0]):
                return -1
        elif cmd == 'exit_if_exist' and nargs >= 1:
            if not app.exit_if_exist(True, args[0]):
                return -1
        elif cmd == 'exit_if_not_exist' and nargs >= 1:
            if not app.exit_if_exist(False, args[0]):
                return -1

        elif cmd == 'wait_process' and self.proc:
            while True:
                ret = self.proc.poll()
                if ret is None:
                    pass
#                    print("waiting for finish")
                else:
                    break
        else:
#            print(f"981: [{cmd_org}] ", args)
            app.set_command_line_org(line_org)
            ret = self.execute_external_command(app, cmd_org, args)
#            print("ret=", ret)
            if type(ret) is int and ret < 0:
                app.print_warning("")
                app.print_warning(f"Error in tkscript_macro.execute_external_command(): Could not execute {cmd_org}")
                return -1

        return 1

# idx: Button number in this program starting from 0
# idx1: Button number in the script file starting from 1
    def execute(self, app, cparams, menu, idx, action, i_mouse_button, script_files = None, event = None, max_num = None):
        if script_files is None:
            inf = self.script_inf.get(menu, None)
            if inf:
                script_files = inf["paths"]
#                print("path=", path)
            else:
                script_files = self.script_files
#                print("null path")
        
        if idx is not None:
            idx1 = idx + 1
        else:
            idx1 = None

        if idx is None:
            section = f"[{menu}]"
        elif action == '':
            section = f"[Button{idx1}.{menu}]"
        else:
            section = f"[Button{idx1}.{menu}].{action}"

        print("")
        if i_mouse_button is None:
            app.print_warning(f"Execute '{section}'")
#            app.print_warning(f"Execute '{section}' in {script_files}")
        else:
            if action is None:
                app.print_warning(f"Execute '{section}' by click mouse button #{i_mouse_button}")
#                app.print_warning(f"Execute '{section}' in {script_files} by click mouse button #{i_mouse_button}")
            else:
                app.print_warning(f"Execute '{section}.{action}' by click mouse button #{i_mouse_button}")
#                app.print_warning(f"Execute '{section}.{action}' in {script_files} by click mouse button #{i_mouse_button}")

        self.OnErrorAction = 'stop'
        app.update_script_vars(cparams)
        self.update_script_vars(app, cparams)
#        ENV{'ExecutingSection'} = $section;

#        print("menu=", menu, idx1, action)
#        print("script_files=", script_files)
        path, fp, line = self.find_file_from_menu(app, menu, idx = idx1, task = action, script_files = script_files)
        if fp is None:
            if action == '':
                action = 'default'
            app.print_warning(f"Warning in tkscript_macro.execute(): Can not find file for {menu} Button#{idx1} for {action}")
            return -1

        app.print_warning(f"   in file [{path}]")

        match = re.search(r'\[Button\d+\.(.*)\]', line)
        if match:
            section = match.groups()[0]
        print(f"  '{section}' is found in [{path}]")

        ret = 1
        while 1:
            line = self.read_cont_lines(fp)

            if line == '' or line[0] in '#:;':
                continue

            if line.lower() == 'bye' or re.match(r'Bye[\s\r]', line, re.IGNORECASE):
                app.terminate()

#            print(f"1052: execute [{line}]")
            ret = self.execute_a_command(app, cparams, line, fp, event = event)

            if ret <= 0:   # END command
                break
            if ret < 0:
                if self.OnErrorAction == 'continue':
                    continue
                break

#            print("    cmd:", line)

        fp.close()

        return ret
