--!A cross-platform build utility based on Lua
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- Copyright (C) 2015-present, Xmake Open Source Community.
--
-- @author      ruki
-- @file        xmake.lua
--

function _plat_values(complete, opt)
    import("core.platform.platform")
    import("core.base.hashset")
    import("core.project.project")

    if not complete or not opt.arch then
        local plats = try {function () return project.allowed_plats() end}
        if plats then
            return plats:to_array()
        end
        return platform.plats()
    end

    -- arch has given, find all supported platforms
    local plats = {}
    for _, plat in ipairs(platform.plats()) do
        local archs = hashset.from(platform.archs(plat))
        if archs:has(opt.arch) then
            table.insert(plats, plat)
        end
    end
    return plats
end

function _arch_values(complete, opt)
    opt = opt or {}
    if opt.helpmenu then
        return
    end

    -- imports
    import("core.project.project")
    import("core.platform.platform")
    import("core.base.hashset")

    -- get all platforms
    local plats = try {function () return project.allowed_plats() end}
    if plats then
        plats = plats:to_array()
    end
    plats = plats or platform.plats()

    -- get all architectures
    local archset = hashset.new()
    for _, plat in ipairs(opt.plat and { opt.plat } or plats) do
        local archs = try {function () return project.allowed_archs(plat) end}
        if archs then
            archs = archs:to_array()
        end
        if not archs then
            archs = platform.archs(plat)
        end
        if archs then
            for _, arch in ipairs(archs) do
                archset:insert(arch)
            end
        end
    end
    return archset:to_array()
end

function _arch_description()
    import("core.project.project")
    import("core.platform.platform")

    -- get all platforms
    local plats = try {function () return project.allowed_plats() end}
    if plats then
        plats = plats:to_array()
    end
    plats = plats or platform.plats()

    -- get all architectures
    local description = {}
    for i, plat in ipairs(plats) do
        local archs = try {function () return project.allowed_archs(plat) end}
        if archs then
            archs = archs:to_array()
        end
        if not archs then
            archs = platform.archs(plat)
        end
        if archs and #archs > 0 then
            local desc = "    - " .. plat .. ":"
            for _, arch in ipairs(archs) do
                desc = desc .. " " .. arch
            end
            table.insert(description, desc)
        end
    end
    return description
end

function _mode_values(complete, opt)
    import("core.project.project")
    opt = opt or {}
    local modes = try {function()
        if opt.menuconf then
            -- we cannot load target.mode in menuconf
            local allowed_modes = project.allowed_modes()
            if allowed_modes then
                return allowed_modes:to_array()
            end
        else
            return project.modes()
        end
    end}
    if not modes then
        modes = {"debug", "release"}
    end
    return modes
end

function _target_values(complete, opt)
    return import("private.utils.complete_helper.targets")(complete, opt)
end

-- the toolchains is too much, so we just show all for menuconf and auto-complete mode.
-- @see https://github.com/xmake-io/xmake/issues/3436
--
function _toolchain_values(complete, opt)
    opt = opt or {}
    if complete or opt.menuconf then
        import("core.tool.toolchain")
        return toolchain.list()
    end
end

function _project_menu_options()
    import("core.project.menu")
    return menu.options()
end

function _language_menu_options()
    import("core.language.menu")
    return menu.options("config")
end

function _platform_menu_options()
    import("core.platform.menu")
    return menu.options("config")
end

task("config")
    set_category("action")
    on_run("main")
    set_menu {
        usage = "xmake config|f [options]",
        description = "Configure the project.",
        shortname = 'f',
        options = {
            {'c', "clean",          "k",  nil,       "Clean the cached user configs and detection cache."},
            {nil, "check",          "k",  nil,       "Just ignore detection cache and force to check all, it will reserve the cached user configs."},
            {nil, "export",         "kv", nil,       "Export the current configuration to the given file.",
                                                     "    e.g.",
                                                     "    - xmake f -m debug -xxx=y --export=build/config.txt"},
            {nil, "import",         "kv", nil,       "Import configs from the given file.",
                                                     "    e.g.",
                                                     "    - xmake f --import=build/config.txt"},
            {nil, "menu",           "k",  nil,       "Configure project with a menu-driven user interface."},
            {category = "."},
            {'p', "plat",           "kv", "auto",    "Compile for the given platform.", values = _plat_values},
            {'a', "arch",           "kv", "auto",    "Compile for the given architecture.", _arch_description, values = _arch_values},
            {'m', "mode",           "kv", "auto",    "Compile for the given mode.", values = _mode_values},
            {'k', "kind",           "kv", "static",  "Compile for the given target kind.", values = {"static", "shared", "binary"}},
            {nil, "host",           "kv", "$(host)", "Set the current host environment."},
            {nil, "policies",       "kv", nil,       "Set the project policies.",
                                                     "    e.g.",
                                                     "    - xmake f --policies=package.fetch_only",
                                                     "    - xmake f --policies=package.precompiled:n,package.install_only"},
            {category = "Package Configuration"},
            {nil, "require",        "kv", nil,       "Require all dependent packages?", values = {"yes", "no"}},
            {nil, "pkg_searchdirs", "kv", nil,       "The search directories of the remote package.",
                                                     "    e.g.",
                                                     "    - xmake f --pkg_searchdirs=/dir1" .. path.envsep() .. "/dir2"},
            {category = "Cross Compilation Configuration"},
            {nil, "cross",          "kv", nil,       "Set cross toolchains prefix",
                                                     "e.g.",
                                                     "    - i386-mingw32-",
                                                     "    - arm-linux-androideabi-"},
            {nil, "target_os",      "kv", nil,       "Set target os only for cross-compilation"},
            {nil, "bin",            "kv", nil,       "Set cross toolchains bin directory",
                                                     "e.g.",
                                                     "    - sdk/bin (/arm-linux-gcc ..)"},
            {nil, "sdk",            "kv", nil,       "Set cross SDK directory",
                                                     "e.g.",
                                                     "    - sdk/bin",
                                                     "    - sdk/lib",
                                                     "    - sdk/include"},
            {nil, "toolchain",      "kv", nil,       "Set toolchain name",
                                                     "e.g. ",
                                                     "    - xmake f --toolchain=clang",
                                                     "    - xmake f --toolchain=[cross|llvm|sdcc ..] --sdk=/xxx",
                                                     "    - run `xmake show -l toolchains` to get all toolchains",
                                                     values = _toolchain_values},
            {nil, "toolchain_host", "kv", nil,       "Set host toolchain name, it's only for building packages on host machine.",
                                                     "e.g. ",
                                                     "    - xmake f --toolchain_host=clang",
                                                     "    - xmake f --toolchain_host=[cross|llvm|sdcc ..] --sdk=/xxx",
                                                     "    - run `xmake show -l toolchains` to get all toolchains",
                                                     values = _toolchain_values},
            {nil, "runtimes",       "kv", nil,       "Set the compiler runtime library.",
                                                     "e.g. ",
                                                     "    - xmake f --runtimes=MTd",
                                                     "    - xmake f --runtimes=MT,c++_static",
                                                     values = {"MT", "MTd", "MD", "MDd",             -- only for msvc
                                                               "c++_static", "c++_shared",           -- gcc/clang/android ndk
                                                               "stdc++_static", "stdc++_shared",     -- gcc/clang
                                                               "gnustl_static", "gnustl_shared",     -- only for old android ndk
                                                               "stlport_static", "stlport_shared"}}, -- only for old android ndk
            _language_menu_options,
            _platform_menu_options,
            {category = "Other Configuration"},
            {nil, "debugger",       "kv", "auto",    "Set debugger"},
            {nil, "ccache",         "kv", true,      "Enable or disable the c/c++ compiler cache."},
            {nil, "ccachedir",      "kv", nil,       "Set the ccache directory."},
            {nil, "trybuild",       "kv", nil,       "Enable try-build mode and set the third-party buildsystem tool.",
                                                     "e.g.",
                                                     "    - xmake f --trybuild=auto; xmake",
                                                     "    - xmake f --trybuild=autoconf -p android --ndk=xxx; xmake",
                                                     "",
                                                     "the third-party buildsystems:",
                                                     values = {"auto", "make", "autoconf", "cmake", "scons", "meson", "bazel", "ninja", "msbuild", "xcodebuild", "ndkbuild", "xrepo"}},
            {nil, "tryconfigs",     "kv", nil,       "Set the extra configurations of the third-party buildsystem for the try-build mode.",
                                                     "e.g.",
                                                     "    - xmake f --trybuild=autoconf --tryconfigs='--enable-shared=no'"},
            {'o', "builddir",       "kv", "build",   "Set build directory."},
            {nil, "buildir",        "kv", nil,       "Set build directory. (deprecated)"},
            {},
            {category = "Project Configuration"},
            _project_menu_options,
        }
    }
