-- gitano.supple
--
-- Supple interface code for Gitano
--
-- Copyright 2012-2017 Daniel Silverstone <dsilvers@digital-scurf.org>
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions
-- are met:
-- 1. Redistributions of source code must retain the above copyright
--    notice, this list of conditions and the following disclaimer.
-- 2. Redistributions in binary form must reproduce the above copyright
--    notice, this list of conditions and the following disclaimer in the
--    documentation and/or other materials provided with the distribution.
-- 3. Neither the name of the author nor the names of their contributors
--    may be used to endorse or promote products derived from this software
--    without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-- ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-- SUCH DAMAGE.
--
--
--

local supple = require 'supple'
local log = require 'gitano.log'
local config = require 'gitano.config'
local i18n = require 'gitano.i18n'
local pat = require 'gitano.patterns'

local repo_proxies = {}
local proxied_repo = {}

local proxy_repo_methods = {}
local function proxy_repo_tostring(proxy)
   return ("<ProxyRepository [%s]>"):format((proxied_repo[proxy] or {}).name or "Unknown")
end

function proxy_repo_methods:get(shaish)
   local repo = proxied_repo[self]
   return repo.git:get(shaish)
end

function proxy_repo_methods:get_config(confname)
   local repo = proxied_repo[self]
   return repo.project_config.settings[confname]
end

function proxy_repo_methods:get_config_list(confname)
   local repo = proxied_repo[self]
   return repo.project_config:get_list(confname)
end

function proxy_repo_methods:check_signature(obj, keyringname)
   local repo = proxied_repo[self]
   return repo:check_signature(obj, keyringname)
end

local proxy_repo_meta = {
   __index = proxy_repo_methods,
   __tostring = proxy_repo_tostring,
}

local function get_repo_proxy(repo)
   local proxy = repo_proxies[repo]
   if proxy then
      return proxy
   end

   proxy = setmetatable({}, proxy_repo_meta)
   proxied_repo[proxy] = repo
   repo_proxies[repo] = proxy

   proxy.name = repo.name

   return proxy
end

local function unproxy_repo(proxy)
   return proxied_repo[proxy] or proxy
end

local repo, loadedhooks

local function load_repo_module(fromrepo, prefix, module)
   local modfile = prefix .. "/" .. module:gsub("%.", "/") .. ".lua"
   local content, err
   if fromrepo then
      -- Load from the repository's admin reference
      content, err = repo:load_from_admin_ref(modfile)
      if not content and err:match("^Not found:") then
         content, err = "--nothing\n", "@empty_hook"
      end
   else
      -- Load from the config repo
      content, err = config.load_file_content(repo.config, modfile)
      if not content then
         content, err = config.get_default_hook_content(repo.config,
                                                        modfile)
      end
   end
   return content, err
end

local function load_module_src(modname)
   local pfx, mod = modname:match(pat.SUPPLE_MODULE_LOAD_MATCH)
   if not (pfx and mod) then
      error(i18n.expand("ERROR_NOT_RIGHT_NAME_FORMAT", {modname=modname}))
   end
   if pfx == "lib" then
      return load_repo_module(repo, pfx, mod)
   elseif pfx == "hooks" and not loadedhooks then
      return load_repo_module(repo, pfx, mod)
   elseif pfx == "globalhooks" and not loadedhooks then
      local mod, err = load_repo_module(nil, "global-hooks", mod)
      loadedhooks = true
      return mod, err
   elseif pfx == "global" then
      return load_repo_module(nil, "hooklib", mod)
   end
   error(i18n.expand("ERROR_UNABLE_TO_LOAD_MODULE", {modname=modname}))
end

local function load_module(modname)
   log.debug(i18n.expand("DEBUG_HOOK_LOAD_MODULE", {modname=modname}))
   local src, name = load_module_src(modname)
   if not src then
      error(name)
   end
   log.ddebug(i18n.expand("DEBUG_ATTEMPT_LOADSTRING"))
   return supple.host.loadstring(src, "@" .. name)
end

local function set_globals(globs)
   globs.load_module = load_module
   log.ddebug(i18n.expand("DEBUG_SET_GLOBALS"))
   supple.host.set_globals(globs)
end

local supple_runtime_code = [[
local loadmodule = load_module
load_module = nil
function require(modname)
   return loadmodule(modname)()
end
return (function(hookname, repo, info, ...)
    local hookf = loadmodule("hooks." .. hookname)
    local ghookf = loadmodule("globalhooks." .. hookname)
    actor = {}
    for k, v in pairs(info) do
       actor[k] = v
    end
    info = nil
    return ghookf(hookf, repo, ...)
end)(...)
]]

local function run_hook(hook, _repo, info, ...)
   if info.username == "gitano-bypass" then
      log.state(i18n.expand("SUPPLE_BYPASS_BANNER_HEADER"))
      log.state(i18n.expand("SUPPLE_BYPASS_ALERT_MESSAGE"))
      log.state(i18n.expand("SUPPLE_BYPASS_BANNER_FOOTER"))
      return true, i18n.expand("SUPPLE_BYPASSED")
   end
   local proxy = get_repo_proxy(_repo)
   repo = _repo
   log.ddebug(i18n.expand("DEBUG_SUPPLE_HOST_RUN"))
   return supple.host.run(supple_runtime_code, "@gitano.supple.runtime",
                          hook, proxy, info, ...)
end

return {
   unproxy_repo = unproxy_repo,
   set_globals = set_globals,
   run_hook = run_hook,
}
