package cjbind_cli

import std.fs.File
import std.env
import cjbind_cli.arg.{StringFlag, BoolFlag, ArgsParser}
import cjbind.build
import cjbind.clang.getClangVersion
import cjbind.options.{CjbindOptions, ObjcCodegenMode, DefaultEnumStyle}
import cjbind.utils.sprintAlign

public func processArgs(): (CjbindOptions, (String) -> Unit) {
    let noEnumPrefixFlag = BoolFlag(None, "no-enum-prefix", "no-enum-prefix",
        "生成枚举时,不使用枚举名称作为枚举值的前缀",)
    let noDetectIncludePath = BoolFlag(None, "no-detect-include-path", "no-detect-include-path",
        "禁用自动 include 路径检测")
    let noComment = BoolFlag(None, "no-comment", "no-comment", "不尝试生成代码中的注释")
    let noLayoutTests = BoolFlag(None, "no-layout-test", "no-layout-test", "不生成布局测试代码")
    let builtins = BoolFlag(None, "builtins", "builtins", "生成内置定义的 bindings,如 __builtin_va_list")
    let makeFuncWrapperFlag = BoolFlag(None, "make-func-wrapper", "make-func-wrapper", "生成 foreign 函数包装器以允许包外调用")
    let funcWrapperSuffixFlag = StringFlag(None, "func-wrapper-suffix", "func-wrapper-suffix", "生成函数包装器时使用的后缀,默认为 _cjbindwrapper", "SUFFIX", "_cjbindwrapper")
    let autoCString = BoolFlag(None, "auto-cstring", "auto-cstring", "把 char* 转换为 CString 而不是 CPointer<UInt8>")
    let arrayPointersInArgs = BoolFlag(None, "array-pointers-in-args", "array-pointers-in-args", "把数组 T arr[size] 转换为 VArray<T, $size> 而不是 CPointer<T>")
    let makeCjString = BoolFlag(None, "make-cjstring", "make-cjstring", "把 C 字符串转换为仓颉的 String 而不是 VArray<UInt8>,这可能会导致二进制表示不一致")
    let objcFlag = BoolFlag(None, "objc", "objc", "启用 Objective-C 绑定生成模式")
    let objcCodegenModeFlag = StringFlag(None, "objc-codegen-mode", "objc-codegen-mode", "ObjC 代码生成模式: runtime (默认) 或 compiler", "MODE", "runtime")
    let objcGenerateDefinitions = BoolFlag(None, "objc-generate-definitions", "objc-generate-definitions", "在 compiler 模式下生成带默认返回值的方法桩体")
    let defaultEnumStyleFlag = StringFlag(None, "default-enum-style", "default-enum-style",
        "默认枚举生成风格: consts (默认) 或 newtype", "STYLE", "consts")
    let fitMacroConstantsFlag = BoolFlag(None, "fit-macro-constants", "fit-macro-constants",
        "根据值范围选择最小适配整数类型")
    let noUnionAccessorWorkaroundFlag = BoolFlag(None, "no-union-accessor-workaround",
        "no-union-accessor-workaround", "禁用 union accessor 的编译器 verifier workaround")

    let outputFlag = StringFlag(Some("o"), "output", "output", "把生成的绑定输出到文件", "FILE", None)
    let packageFlag = StringFlag(Some("p"), "package", "package", "生成的绑定中的包名", "PACKAGE", "cjbind_ffi")

    // 处理特例
    let versionFlag = BoolFlag(Some("v"), "version", "version", "显示版本号并退出")
    let helpFlag = BoolFlag(Some("h"), "help", "help", "显示帮助信息")

    let parser = ArgsParser(
        noEnumPrefixFlag,
        noDetectIncludePath,
        noComment,
        noLayoutTests,
        builtins,
        makeFuncWrapperFlag,
        funcWrapperSuffixFlag,
        autoCString,
        arrayPointersInArgs,
        makeCjString,
        objcFlag,
        objcCodegenModeFlag,
        objcGenerateDefinitions,
        defaultEnumStyleFlag,
        fitMacroConstantsFlag,
        noUnionAccessorWorkaroundFlag,
        outputFlag,
        packageFlag,
        versionFlag,
        helpFlag
    )

    let (headers, clangArgs) = parser.parse(env.getCommandLine())

    if (versionFlag.value) {
        printVersion()
    }

    if (helpFlag.value) {
        printHelp(parser)
    }

    let opt = CjbindOptions(headers, clangArgs)
    opt.packageName = packageFlag.value.getOrThrow()
    opt.noEnumPrefix = noEnumPrefixFlag.value
    opt.noDetectIncludePath = noDetectIncludePath.value
    opt.noLayoutTests = noLayoutTests.value
    opt.builtins = builtins.value
    opt.noComment = noComment.value
    opt.autoCString = autoCString.value
    opt.makeFuncWrapper = makeFuncWrapperFlag.value
    opt.funcWrapperSuffix = funcWrapperSuffixFlag.value.getOrThrow()
    opt.arrayPointersInArgs = arrayPointersInArgs.value
    opt.makeCjString = makeCjString.value
    opt.objc = objcFlag.value
    opt.objcCodegenMode = match (objcCodegenModeFlag.value.getOrThrow()) {
        case "runtime" => ObjcCodegenMode.Runtime
        case "compiler" => ObjcCodegenMode.Compiler
        case v =>
            eprintln("Error: --objc-codegen-mode must be 'runtime' or 'compiler', got: ${v}")
            env.exit(1)
    }
    opt.objcGenerateDefinitions = objcGenerateDefinitions.value
    opt.defaultEnumStyle = match (defaultEnumStyleFlag.value.getOrThrow()) {
        case "consts" => DefaultEnumStyle.Consts
        case "newtype" => DefaultEnumStyle.NewType
        case v =>
            eprintln("Error: --default-enum-style must be 'consts' or 'newtype', got: ${v}")
            env.exit(1)
    }
    opt.fitMacroConstants = fitMacroConstantsFlag.value
    opt.useUnionAccessorWorkaround = !noUnionAccessorWorkaroundFlag.value

    let output: ?String = outputFlag.value

    let writer: (String) -> Unit = match (output) {
        case Some(v) => {gen => File.writeTo(v, gen.toArray())}
        case None => {gen => println(gen)}
    }

    return (opt, writer)
}

func printVersion(): Nothing {
    let pre = [
        "Commit Hash:",
        "Commit 时间:",
        "标签:",
        "洁净构建:"
    ]
    let post = [
        build.COMMIT_HASH,
        build.COMMIT_DATE,
        build.TAG,
        build.GIT_CLEAN_FILES.isEmpty().toString()
    ]

    let versionInfo = sprintAlign(pre, post, insert: "")

    let version = """
cjbind 版本:\t${build.VERSION}
libclang 版本:\t${getClangVersion()}

${versionInfo}"""
    println(version)

    env.exit(0)
}

func printHelp(parser: ArgsParser): Nothing {
    println(parser.getHelp())

    env.exit(0)
}