import t, c
from stdint import *
import string
import memhub
import stdio
import hashtable


# ============================================================
# argparse.py — 命令行参数解析库（Python argparse 风格）
#
# 用法:
#   argparse._mbuddy = mb
#   parser = argparse.ArgumentParser("myprog", "A test", pool=mb)
#   parser.add_argument("--input", "-i", arg_type=argparse.STRING,
#                       required=True, help="input file")
#   parser.add_argument("--count", "-c", arg_type=argparse.INT,
#                       default=0, help="repeat count")
#   parser.add_argument("--verbose", "-v", action=argparse.STORE_TRUE,
#                       help="verbose mode")
#   parser.add_argument("output", arg_type=argparse.STRING,
#                       help="output path")
#   args = parser.parse_args(argc, argv)
#   if args is not None:
#       inp: str = args.get_str("input")
#       cnt: INT = args.get_int("count")
#       verb: bool = args.get_bool("verbose")
#       out: str = args.get_str("output")
#       argparse.release(args)
# ============================================================

# 类型常量
STRING: t.CDefine = 0
INT: t.CDefine = 1
BOOL: t.CDefine = 2

# 动作常量
STORE: t.CDefine = 0
STORE_TRUE: t.CDefine = 1
STORE_FALSE: t.CDefine = 2
COUNT: t.CDefine = 3

# 限制
MAX_ARGS: t.CDefine = 32

# 模块级 mbuddy 指针（由用户在 main 中赋值）
_mbuddy: memhub.MemBuddy | t.CPtr


# ============================================================
# Argument — 参数定义结构体
# ============================================================

class Argument:
    name: str               # "--input" 或 "output"（位置参数）
    short_name: str         # "-i" 或 None
    help_text: str          # 帮助文本
    arg_type: INT           # STRING, INT, BOOL
    action: INT             # STORE, STORE_TRUE, STORE_FALSE, COUNT
    default_int: INT        # INT 类型默认值
    default_str: str        # STRING 类型默认值
    required: bool          # 是否必须
    is_positional: bool     # 是否位置参数
    dest: str               # 查找键名（"--input" → "input"）


# ============================================================
# ParsedArgs — 解析结果
# ============================================================

class ParsedArgs:
    _ht: hashtable.HashTable | t.CPtr
    __mbuddy__: memhub.MemBuddy | t.CPtr

    def __new__(self, mb: memhub.MemBuddy | t.CPtr) -> t.CPtr:
        return mb.alloc(16)

    def __init__(self, mb: memhub.MemBuddy | t.CPtr):
        self.__mbuddy__ = mb
        self._ht = hashtable.HashTable(mb)

    def get_str(self, name: str) -> str:
        """获取字符串值。键不存在返回 None。"""
        if self._ht.__contains__(name) == 0: return None
        return self._ht[name]

    def get_int(self, name: str) -> INT:
        """获取整数值。键不存在返回 0。"""
        if self._ht.__contains__(name) == 0: return 0
        ptr: t.CPtr = self._ht[name]
        return c.Deref(t.CInt(t.CUInt64T(ptr), t.CPtr))

    def get_bool(self, name: str) -> INT:
        """获取布尔值（返回 1 或 0）。键不存在返回 0。"""
        if self._ht.__contains__(name) == 0: return 0
        ptr: t.CPtr = self._ht[name]
        return 1 if c.Deref(t.CInt(t.CUInt64T(ptr), t.CPtr)) != 0 else 0

    def has(self, name: str) -> bool:
        """检查键是否存在。"""
        return self._ht.__contains__(name) != 0

    def _set_str(self, name: str, val: str):
        self._ht.set_str(name, val)

    def _set_int(self, name: str, val: INT):
        self._ht.set_int(name, val)

    def _set_bool(self, name: str, val: bool):
        if val:
            self._ht.set_int(name, 1)
        else:
            self._ht.set_int(name, 0)


# ============================================================
# ArgumentParser — 命令行参数解析器
# ============================================================

class ArgumentParser:
    _prog: str
    _description: str
    _args: Argument | t.CPtr
    _arg_count: INT
    __mbuddy__: memhub.MemBuddy | t.CPtr

    def __new__(self, prog: str = "", description: str = "",
                pool: memhub.MemBuddy | t.CPtr = None) -> t.CPtr:
        mb: memhub.MemBuddy | t.CPtr = pool
        if mb is None: mb = _mbuddy
        return mb.alloc(40)

    def __init__(self, prog: str = "", description: str = "",
                 pool: memhub.MemBuddy | t.CPtr = None):
        mb: memhub.MemBuddy | t.CPtr = pool
        if mb is None: mb = _mbuddy
        self.__mbuddy__ = mb
        self._prog = prog
        self._description = description
        self._arg_count = 0
        self._args = mb.alloc(MAX_ARGS * Argument.__sizeof__())

    def add_argument(self, name: str = "", short: str = None,
                     arg_type: INT = 0, default: INT = 0,
                     default_str: str = None, required: bool = False,
                     action: INT = 0, help: str = None):
        """注册一个命令行参数。

        Args:
            name: 参数名（"--input" 或 "output"）
            short: 短选项（"-i"），可选
            arg_type: 类型（STRING/INT/BOOL）
            default: INT 默认值
            default_str: STRING 默认值
            required: 是否必须
            action: 动作（STORE/STORE_TRUE/STORE_FALSE/COUNT）
            help: 帮助文本
        """
        if self._arg_count >= MAX_ARGS: return
        idx: INT = self._arg_count
        arg: Argument | t.CPtr = t.CPtr(t.CUInt64T(self._args) + idx * Argument.__sizeof__())
        arg.name = name
        arg.short_name = short
        arg.help_text = help
        arg.arg_type = arg_type
        arg.action = action
        arg.default_int = default
        arg.default_str = default_str
        arg.required = required
        # 判断是否位置参数
        if name is not None and name[0] != '-':
            arg.is_positional = True
            arg.dest = name
        else:
            arg.is_positional = False
            if name is not None and name[0] == '-' and name[1] == '-':
                arg.dest = name + 2
            else:
                arg.dest = name + 1
        self._arg_count = idx + 1

    def _get_arg(self, idx: INT) -> Argument | t.CPtr:
        return t.CPtr(t.CUInt64T(self._args) + idx * Argument.__sizeof__())

    def _find_long(self, token: str) -> INT:
        """精确匹配长选项名"""
        i: INT = 0
        while i < self._arg_count:
            arg: Argument | t.CPtr = self._get_arg(i)
            if string.strcmp(arg.name, token) == 0:
                return i
            i += 1
        return -1

    def _find_long_prefix(self, token: str) -> INT:
        """匹配 --name=value 中的 name 前缀"""
        i: INT = 0
        while i < self._arg_count:
            arg: Argument | t.CPtr = self._get_arg(i)
            name_len: INT = string.strlen(arg.name)
            if string.strncmp(token, arg.name, name_len) == 0:
                if token[name_len] == '=':
                    return i
            i += 1
        return -1

    def _find_short(self, token: str) -> INT:
        i: INT = 0
        while i < self._arg_count:
            arg: Argument | t.CPtr = self._get_arg(i)
            if arg.short_name is not None:
                if string.strcmp(arg.short_name, token) == 0:
                    return i
            i += 1
        return -1

    def _find_positional(self, pos_idx: INT) -> INT:
        count: INT = 0
        i: INT = 0
        while i < self._arg_count:
            arg: Argument | t.CPtr = self._get_arg(i)
            if arg.is_positional:
                if count == pos_idx:
                    return i
                count += 1
            i += 1
        return -1

    def _store_value(self, args: ParsedArgs | t.CPtr,
                     arg: Argument | t.CPtr, value: str):
        if arg.arg_type == INT:
            args._set_int(arg.dest, string.atoi(value))
        elif arg.arg_type == STRING:
            args._set_str(arg.dest, value)
        elif arg.arg_type == BOOL:
            v: INT = string.atoi(value)
            if v != 0:
                args._set_bool(arg.dest, True)
            else:
                args._set_bool(arg.dest, False)

    def _apply_action(self, args: ParsedArgs | t.CPtr, arg: Argument | t.CPtr):
        if arg.action == STORE_TRUE:
            args._set_bool(arg.dest, True)
        elif arg.action == STORE_FALSE:
            args._set_bool(arg.dest, False)
        elif arg.action == COUNT:
            cur: INT = args.get_int(arg.dest)
            args._set_int(arg.dest, cur + 1)

    def parse_args(self, argc: INT, argv: str | t.CPtr) -> ParsedArgs | t.CPtr:
        """解析命令行参数。

        Args:
            argc: 参数个数
            argv: 参数数组（char**，argv[0] 为程序名）
        Returns:
            ParsedArgs 指针，失败返回 None
        """
        if self.__mbuddy__ is None: return None

        args: ParsedArgs | t.CPtr = ParsedArgs(self.__mbuddy__)
        if args is None: return None

        # 初始化默认值
        i: INT = 0
        while i < self._arg_count:
            arg: Argument | t.CPtr = self._get_arg(i)
            if arg.action == STORE:
                if arg.arg_type == INT:
                    args._set_int(arg.dest, arg.default_int)
                elif arg.arg_type == STRING:
                    args._set_str(arg.dest, arg.default_str)
            elif arg.action == STORE_TRUE:
                args._set_bool(arg.dest, False)
            elif arg.action == STORE_FALSE:
                args._set_bool(arg.dest, True)
            elif arg.action == COUNT:
                args._set_int(arg.dest, 0)
            i += 1

        # 解析 argv
        idx: INT = 1
        pos_idx: INT = 0
        token: str = None
        found: INT = 0
        arg: Argument | t.CPtr = None
        value: str = None
        entry: str | t.CPtr = None
        name_len: INT = 0

        while idx < argc:
            entry = t.CPtr(t.CUInt64T(argv) + idx * 8)
            token = entry[0]
            if token is None: break

            # "--" 结束符：后续全部为位置参数
            if token[0] == '-' and token[1] == '-' and token[2] == 0:
                idx += 1
                while idx < argc:
                    entry = t.CPtr(t.CUInt64T(argv) + idx * 8)
                    token = entry[0]
                    found = self._find_positional(pos_idx)
                    if found < 0:
                        stdio.printf("error: unexpected argument '%s'\n", token)
                        release(args)
                        return None
                    arg = self._get_arg(found)
                    self._store_value(args, arg, token)
                    pos_idx += 1
                    idx += 1
                break

            # 长选项 --name 或 --name=value
            if token[0] == '-' and token[1] == '-':
                # 检查是否含 =
                has_eq: bool = False
                p: str = token
                while p[0] != 0:
                    if p[0] == '=':
                        has_eq = True
                        break
                    p += 1

                if has_eq:
                    # --name=value
                    found = self._find_long_prefix(token)
                    if found < 0:
                        stdio.printf(
                            "error: unrecognized argument '%s'\n", token)
                        release(args)
                        return None
                    arg = self._get_arg(found)
                    # 提取 = 后的值
                    arg_name_len: INT = string.strlen(arg.name)
                    value = token + arg_name_len + 1
                    self._store_value(args, arg, value)
                    idx += 1
                else:
                    # --name（值在下一个 argv 或标志）
                    found = self._find_long(token)
                    if found < 0:
                        stdio.printf(
                            "error: unrecognized argument '%s'\n", token)
                        release(args)
                        return None
                    arg = self._get_arg(found)
                    if arg.action == STORE:
                        if idx + 1 >= argc:
                            stdio.printf(
                                "error: %s requires a value\n", arg.name)
                            release(args)
                            return None
                        entry = t.CPtr(t.CUInt64T(argv) + (idx + 1) * 8)
                        value = entry[0]
                        self._store_value(args, arg, value)
                        idx += 2
                    else:
                        self._apply_action(args, arg)
                        idx += 1

            # 短选项 -x
            elif token[0] == '-':
                found = self._find_short(token)
                if found < 0:
                    stdio.printf(
                        "error: unrecognized argument '%s'\n", token)
                    release(args)
                    return None
                arg = self._get_arg(found)
                if arg.action == STORE:
                    if idx + 1 >= argc:
                        stdio.printf(
                            "error: %s requires a value\n", arg.name)
                        release(args)
                        return None
                    entry = t.CPtr(t.CUInt64T(argv) + (idx + 1) * 8)
                    value = entry[0]
                    self._store_value(args, arg, value)
                    idx += 2
                else:
                    self._apply_action(args, arg)
                    idx += 1

            # 位置参数
            else:
                found = self._find_positional(pos_idx)
                if found < 0:
                    stdio.printf(
                        "error: unexpected argument '%s'\n", token)
                    release(args)
                    return None
                arg = self._get_arg(found)
                self._store_value(args, arg, token)
                pos_idx += 1
                idx += 1

        # 检查必须参数
        i = 0
        while i < self._arg_count:
            arg = self._get_arg(i)
            if arg.required:
                if not args.has(arg.dest):
                    stdio.printf(
                        "error: missing required argument '%s'\n",
                        arg.name)
                    release(args)
                    return None
            i += 1

        return args

    def print_help(self):
        """打印帮助信息"""
        stdio.printf("usage: %s [options]\n", self._prog)
        if self._description is not None and self._description[0] != 0:
            stdio.printf("\n%s\n", self._description)
        stdio.printf("\npositional arguments:\n")
        i: INT = 0
        while i < self._arg_count:
            arg: Argument | t.CPtr = self._get_arg(i)
            if arg.is_positional:
                ht: str = arg.help_text
                if ht is None: ht = ""
                stdio.printf("  %-20s %s\n", arg.name, ht)
            i += 1
        stdio.printf("\noptional arguments:\n")
        i = 0
        while i < self._arg_count:
            arg = self._get_arg(i)
            if not arg.is_positional:
                ht: str = arg.help_text
                if ht is None: ht = ""
                if arg.short_name is not None:
                    stdio.printf("  %s, %-18s %s\n",
                        arg.short_name, arg.name, ht)
                else:
                    stdio.printf("  %-22s %s\n", arg.name, ht)
            i += 1

    def print_usage(self):
        """打印用法简述"""
        stdio.printf("usage: %s [options]\n", self._prog)


# ============================================================
# release — 释放 ParsedArgs 及其内部资源
# ============================================================

def release(args: ParsedArgs | t.CPtr):
    """释放 ParsedArgs 对象及其内部 HashTable。"""
    if args is None: return
    if args.__mbuddy__ is None: return
    if args._ht is not None:
        if args._ht.__slots__ is not None:
            args.__mbuddy__.free(args._ht.__slots__)
        args.__mbuddy__.free(t.CVoid(t.CUInt64T(args._ht), t.CPtr))
    args.__mbuddy__.free(t.CVoid(t.CUInt64T(args), t.CPtr))
