--!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        main.lua
--

-- imports
import("core.base.option")
import("core.project.config")
import("core.project.project")
import("core.platform.platform")
import("core.package.repository")
import("devel.git")
import("net.proxy")
import("async.runjobs")
import("private.action.require.impl.environment")
import("private.service.remote_build.action", {alias = "remote_build_action"})

function _clear_quick_search_cache(is_global)
    if is_global then
        import("private.xrepo.quick_search.cache")
        cache.clear()
    end
end

-- add repository url
function _add(name, url, branch, is_global)

    -- remove previous repository if exists
    local repodir = path.join(repository.directory(is_global), name)
    if os.isdir(repodir) then
        os.rmdir(repodir)
    end

    -- enter environment
    environment.enter()

    -- clone repository
    if not os.isdir(url) then
        local remoteurl = proxy.mirror(url) or url
        git.clone(remoteurl, {verbose = option.get("verbose"), branch = branch, outputdir = repodir, autocrlf = false})
    end

    -- add url
    repository.add(name, url, branch, is_global)

    -- trace
    cprint("${color.success}add %s repository(%s): %s%s ok!", (is_global and "global" or "local"), name, url, branch and (" " .. branch) or "")

    -- leave environment
    environment.leave()

    -- clear quick search cache
    _clear_quick_search_cache(is_global)
end

-- remove repository url
function _remove(name, is_global)

    -- remove url
    repository.remove(name, is_global)

    -- remove repository
    local repodir = path.join(repository.directory(is_global), name)
    if os.isdir(repodir) then
        os.rmdir(repodir)
    end

    -- clear quick search cache
    _clear_quick_search_cache(is_global)
    cprint("${bright}remove %s repository(%s): ok!", (is_global and "global" or "local"), name)
end

-- update repositories
function _update()

    -- enter environment
    environment.enter()

    -- trace
    local name = option.get("name")
    if name then
        cprintf("updating repository ${bright}%s${clear} .. ", name)
    else
        printf("updating repositories .. ")
    end
    if option.get("verbose") then
        print("")
    end

    -- create a pull task
    local task = function ()

        -- get all repositories (local first)
        local repos = table.join(repository.repositories({global = false}), repository.repositories({global = true}))
        if name then
            for _, repo in ipairs(repos) do
                if repo:name() == name then
                    repos = {repo}
                    break
                end
            end
        end

        -- pull all repositories
        local pulled = {}
        for _, repo in ipairs(repos) do
            local repodir = repo:directory()
            if not pulled[repodir] then
                if os.isdir(repodir) then
                    -- only update the local repository with the remote url
                    if not os.isdir(repo:url()) then
                        -- check and update remote URL if it differs from repo:url()
                        local current_url = git.remote.get_url({repodir = repodir})
                        local expected_url = repo:url()
                        if current_url ~= expected_url then
                            vprint("updating remote URL for repository(%s): %s -> %s", repo:name(), current_url or "<none>", expected_url)
                            git.remote.set_url(expected_url, {repodir = repodir})
                        end
                        vprint("pulling repository(%s): %s to %s ..", repo:name(), repo:url(), repodir)
                        git.reset({verbose = option.get("verbose"), repodir = repodir, hard = true})
                        git.pull({verbose = option.get("verbose"), branch = repo:branch(), repodir = repodir, force = true})
                        io.save(path.join(repodir, "updated"), {})
                    end
                else
                    vprint("cloning repository(%s): %s to %s ..", repo:name(), repo:url(), repodir)
                    local remoteurl = proxy.mirror(repo:url()) or repo:url()
                    git.clone(remoteurl, {verbose = option.get("verbose"), branch = repo:branch(), outputdir = repodir, autocrlf = false})
                    io.save(path.join(repodir, "updated"), {})
                end
                pulled[repodir] = true
            end
        end

        -- clear quick search cache
        _clear_quick_search_cache(true)
    end

    -- pull repositories
    if option.get("verbose") then
        task()
    else
        runjobs("update repo", task, {waiting_indicator = true, isolate = true})
    end

    -- leave environment
    environment.leave()
    cprint("${green}ok")
end

-- clear all repositories
function _clear(is_global)

    -- clear all urls
    repository.clear(is_global)

    -- remove all repositories
    local repodir = repository.directory(is_global)
    if os.isdir(repodir) then
        os.rmdir(repodir)
    end

    -- clear quick search cache
    _clear_quick_search_cache(is_global)
    cprint("${color.success}clear %s repositories: ok!", (is_global and "global" or "local"))
end

-- list all repositories
function _list(is_global)
    local count = 0
    for _, position in ipairs(is_global and "global" or {"local", "global"}) do
        print("%s repositories:", position)
        for _, repo in pairs(repository.repositories({global = position == "global"})) do
            print("    %s %s%s", repo:name(), repo:url(), repo:branch() and (" " .. repo:branch()) or "")
            count = count + 1
        end
        print("")
    end
    print("%d repositories were found!", count)
end

-- get the repository directory
function _directory(is_global)
    print(repository.directory(is_global))
end

-- load project
function _load_project()

    -- enter project directory
    os.cd(project.directory())

    -- load config
    config.load()

    -- load platform
    platform.load(config.plat())
end

-- main
function main()

    -- do action for remote?
    if remote_build_action.enabled() then
        return remote_build_action()
    end

    -- load project if operate local repositories
    if not option.get("global") then
        _load_project()
    end

    -- add repository url
    if option.get("add") then

        _add(option.get("name"), option.get("url"), option.get("branch"), option.get("global"))

    -- remove repository url
    elseif option.get("remove") then

        _remove(option.get("name"), option.get("global"))

    -- update repository url
    elseif option.get("update") then

        _update()

    -- clear all repositories
    elseif option.get("clear") then

        _clear(option.get("global"))

    -- list all repositories
    elseif option.get("list") then

        _list(option.get("global"))

    -- show repo directory
    else
        _directory(option.get("global"))
    end
end
