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

-- imports
import("core.base.option")
import("lib.detect.find_tool")

-- the options
local options = {
    {'g', "good",     "kv",  nil, "Set the good commit."},
    {'b', "bad",      "kv",  nil, "Set the bad commit."},
    {nil, "gitdir",   "kv",  nil, "Set the git root directory."},
    {'c', "commands", "kv" , nil, "Run the multiple commands instead of the default build command.",
                                  "e.g.",
                                  "    $ xmake l cli.bisect -c 'xmake -rv' -g good -b bad",
                                  "    $ xmake l cli.bisect -c 'xmake -vD; xmake run hello' -g good -b bad"},
    {'s', "script"  , "kv" , nil, "Run the given lua script file.",
                                  "e.g.",
                                  "    $ xmake l cli.bisect -s /tmp/test.lua -g good -b bad"},
    {'-', "arbitrary", "vs", nil, "Run an arbitrary command.",
                                  "e.g.",
                                  "    $ xmake l cli.bisect -g 90846dd -b ddb86e4 -- xmake -rv"}
}

-- run command
function _run_command(opt)
    opt = opt or {}
    local ok = try
    {
        function ()
            local commands = opt.commands
            local scriptfile = opt.script
            local arbitrary = opt.arbitrary
            if commands then
                for _, command in ipairs(commands:split(";")) do
                    os.exec(command:trim())
                end
            elseif arbitrary then
                local program = arbitrary[1]
                local argv = #arbitrary > 1 and table.slice(arbitrary, 2) or {}
                os.execv(program, argv)
            elseif scriptfile and os.isfile(scriptfile) and path.extension(scriptfile) == ".lua" then
                local script = import(path.basename(scriptfile),
                    {rootdir = path.directory(scriptfile), anonymous = true})
                script(events)
            end
            return true
        end,
        catch
        {
            function (errors)
                cprint(tostring(errors))
            end
        }
    }
    return ok
end


-- use `git bisect` to analyze problem
function main(...)
    local argv = table.pack(...)
    local args = option.parse(argv, options, "Analyze problem using `git bisect`.",
                                             "",
                                             "Usage: xmake l cli.bisect [options]")

    local git = assert(find_tool("git"), "git not found!")
    local good = assert(args.good, "please set `--good commit`")
    local bad = assert(args.bad, "please set `--bad commit`")
    local gitdir = args.gitdir or os.curdir()
    os.execv(git.program, {"bisect", "start"}, {curdir = gitdir})
    os.execv(git.program, {"bisect", "good", good}, {curdir = gitdir})
    os.execv(git.program, {"bisect", "bad", bad}, {curdir = gitdir})
    while true do
        local output
        if _run_command(args) then
            output = os.iorunv(git.program, {"bisect", "good"}, {curdir = gitdir})
        else
            output = os.iorunv(git.program, {"bisect", "bad"}, {curdir = gitdir})
        end
        if output then
            print(output)
            if output:find("is the first bad commit", 1, true) then
                break
            end
        end
    end
    os.execv(git.program, {"bisect", "reset"}, {curdir = gitdir})
end
