--!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
--

-- define rule: sign
--
-- values:
--   - wdk.sign.mode:       nosign/test/release (default: nosign)
--   - wdk.sign.store:      PrivateCertStore
--   - wdk.sign.thumbprint: 032122545DCAA6167B1ADBE5F7FDF07AE2234AAA
--   - wdk.sign.company:    xmake.io
--   - wdk.sign.certfile:   signcert.cer
--   - wdk.sign.timestamp:  http://timestamp.verisign.com/scripts/timstamp.dll
--
rule("wdk.sign")

    -- add rule: wdk environment
    add_deps("wdk.env")

    -- before load
    on_load(function (target)

        -- imports
        import("core.project.config")
        import("utils.wdk.testcert")

        -- get wdk
        local wdk = target:data("wdk")

        -- get arch
        local arch = assert(config.arch(), "arch not found!")

        -- get inf2cat
        local inf2cat = path.join(wdk.bindir, arch, "inf2cat.exe")
        if not os.isexec(inf2cat) then
            inf2cat = path.join(wdk.bindir, wdk.sdkver, arch, "inf2cat.exe")
        end
        if not os.isexec(inf2cat) then
            inf2cat = path.join(wdk.bindir, wdk.sdkver, "x86", "inf2cat.exe")
        end
        target:data_set("wdk.sign.inf2cat", inf2cat)

        -- check
        local mode = target:values("wdk.sign.mode")
        local store = target:values("wdk.sign.store")
        local certfile = target:values("wdk.sign.certfile")
        local thumbprint = target:values("wdk.sign.thumbprint")
        if mode and (not certfile and not thumbprint and not store) then
            if mode == "test" then
                -- attempt to install test certificate first
                local ok = try
                {
                    function ()
                        testcert("install")
                        return true
                    end
                }
                if not ok then
                    raise([[please first select one following step for signing:
1. run `$xmake l utils.wdk.testcert install` as admin in console (only once) or
2. add set_values(\"wdk.sign.[certfile|store|thumbprint]\", ...) in xmake.lua]], mode)
                end
            else
                raise("please add set_values(\"wdk.sign.[certfile|store|thumbprint]\", ...) for %s signing!", mode)
            end
        end
    end)

    -- after build
    after_build(function (target, opt)

        -- imports
        import("sign")
        import("core.base.option")
        import("core.theme.theme")
        import("core.project.config")
        import("core.project.depend")
        import("lib.detect.find_file")
        import("utils.progress")

        -- need build this object?
        local tempfile = os.tmpfile(target:targetfile())
        local dependfile = tempfile .. ".d"
        local dependinfo = target:is_rebuilt() and {} or (depend.load(dependfile) or {})
        if not depend.is_changed(dependinfo, {lastmtime = os.mtime(tempfile)}) then
            return
        end

        -- get sign mode
        local signmode = target:values("wdk.sign.mode")
        if not signmode then
            return
        end

        -- trace progress info
        progress.show(opt.progress, "${color.build.target}signing.%s %s", signmode, path.filename(target:targetfile()))

        -- get arch
        local arch = assert(config.arch(), "arch not found!")

        -- get inf2cat
        local inf2cat = target:data("wdk.sign.inf2cat")
        assert(inf2cat and os.isexec(inf2cat), "inf2cat not found!")

        -- sign the target file
        sign(target, target:targetfile(), signmode)

        -- get inf file
        local infile = target:data("wdk.sign.inf")
        if infile and os.isfile(infile) then

            -- do inf2cat
            local inf2cat_dir = path.directory(target:targetfile())
            local inf2cat_argv = {"/driver:" .. inf2cat_dir, "/uselocaltime"}
            local inf2cat_os = target:values("wdk.inf2cat.os") or {"XP_" .. arch, "7_" .. arch, "8_" .. arch, "10_" .. arch}
            table.insert(inf2cat_argv, "/os:" .. table.concat(table.wrap(inf2cat_os), ','))
            os.vrunv(inf2cat, inf2cat_argv)

            -- get *.cat file path from the output directory
            local catfile = find_file("*.cat", inf2cat_dir)
            assert(catfile, "*.cat not found!")

            -- sign *.cat file
            sign(target, catfile, signmode)
        else
            -- trace
            vprint("Inf2Cat task was skipped as there were no inf files to process")
        end

        -- update files and values to the dependent file
        dependinfo.files = {target:targetfile()}
        depend.save(dependinfo, dependfile)
        io.writefile(tempfile, "")
    end)

    -- after package
    after_package(function (target)

        -- imports
        import("sign")
        import("core.base.option")

        -- get signtool
        local signtool = target:data("wdk.sign.signtool")

        -- get package file
        local packagefile = target:data("wdk.sign.cab")
        assert(packagefile and os.isfile(packagefile), "the driver package file(.cab) not found!")

        -- get sign mode
        local signmode = target:values("wdk.sign.mode") or "test"

        -- trace progress info
        if option.get("verbose") then
            cprint("${dim magenta}signing.%s %s", signmode, path.filename(packagefile))
        else
            cprint("${magenta}signing.%s %s", signmode, path.filename(packagefile))
        end

        -- sign package file
        sign(target, packagefile, signmode)
    end)
