--!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-2020, TBOOX Open Source Group.
--
-- @author      ruki
-- @file        depend.lua
--

-- imports
import("private.tools.cl.parse_deps", {alias = "parse_deps_cl"})
import("private.tools.gcc.parse_deps", {alias = "parse_deps_gcc"})

-- load depfiles for gcc
function _load_depfiles_gcc(dependinfo)

    local depfiles = dependinfo.depfiles_gcc
    if depfiles then
        depfiles = parse_deps_gcc(depfiles)
        if depfiles then
            if dependinfo.files then
                table.join2(dependinfo.files, depfiles)
            else
                dependinfo.files = depfiles
            end
        end
        dependinfo.depfiles_gcc = nil
    end
end

-- load depfiles for cl
function _load_depfiles_cl(dependinfo)

    local depfiles = dependinfo.depfiles_cl
    if depfiles then
        depfiles = parse_deps_cl(depfiles)
        if depfiles then
            if dependinfo.files then
                table.join2(dependinfo.files, depfiles)
            else
                dependinfo.files = depfiles
            end
        end
        dependinfo.depfiles_cl = nil
    end
end

-- load dependent info from the given file (.d) 
function load(dependfile)

    if os.isfile(dependfile) then
        -- may be the depend file has been incomplete when if the compilation process is abnormally interrupted
        local dependinfo = try { function() return io.load(dependfile) end }
        if dependinfo then
            -- attempt to load depfiles from the compilers
            if is_plat("windows") then
                _load_depfiles_cl(dependinfo)
            else
                _load_depfiles_gcc(dependinfo)
            end
            return dependinfo
        end
    end
end

-- save dependent info to file
function save(dependinfo, dependfile)
    io.save(dependfile, dependinfo)
end

-- the dependent info is changed?
--
-- if not depend.is_changed(dependinfo, {filemtime = os.mtime(objectfile), values = {...}}) then
--      return 
-- end
--
function is_changed(dependinfo, opt)

    -- empty depend info? always be changed
    local files = dependinfo.files or {}
    local values = dependinfo.values or {}
    if #files == 0 and #values == 0 then
        return true
    end

    -- check the dependent files are changed?
    local lastmtime = opt.lastmtime or 0
    _g.files_mtime = _g.files_mtime or {}
    local files_mtime = _g.files_mtime
    for _, file in ipairs(files) do

        -- get and cache the file mtime
        local mtime = files_mtime[file] or (os.isfile(file) and os.mtime(file) or nil)
        files_mtime[file] = mtime

        -- source and header files have been changed?
        if mtime == nil or mtime > lastmtime then
            return true
        end
    end

    -- check the dependent values are changed?
    local depvalues = values
    local optvalues = opt.values or {}
    if #depvalues ~= #optvalues then
        return true
    end
    for idx, depvalue in ipairs(depvalues) do
        local optvalue = optvalues[idx]
        local deptype = type(depvalue) 
        local opttype = type(optvalue)
        if deptype ~= opttype then
            return true
        elseif deptype == "string" and depvalue ~= optvalue then
            return true
        elseif deptype == "table" then
            for subidx, subvalue in ipairs(depvalue) do
                if subvalue ~= optvalue[subidx] then
                    return true
                end
            end
        end
    end

    -- check the dependent files list are changed?
    local optfiles = opt.files
    if optfiles then
        for idx, file in ipairs(files) do
            if file ~= optfiles[idx] then
                return true
            end
        end
    end
end
