-- preprocess R literate sources or Markdown files to LaTeX
local M = {}
local log = logging.new "preprocess_input"
local mkutils = require "mkutils"

local commands = {
 knitr = { command = 'Rscript -e "library(knitr); knit(\'${tex_file}\', output=\'${tmp_file}\')"'},
 pandoc = { command = 'pandoc -f ${input_format} -s -o \'${tmp_file}\' -t latex \'${tex_file}\''},
 render = { command = 'Rscript -e "library(rmarkdown); render(\'${tex_file}\', output_file=\'${tmp_file}\',output_format = \'latex_document\')"'}
}
local filetypes = {
 rnw = {sequence = {"knitr"} },
 rtex = {sequence = {"knitr"}},
 rmd = {sequence = {"render"}},
 rrst = {sequence = {"knitr", "pandoc"}, options = {input_format = "rst"}},
 md = {sequence = {"pandoc"}, options = {input_format = "markdown"}},
 rst = {sequence = {"pandoc"}, options = {input_format = "rst"}},
}

local function get_temp_name(arg,curr, length)
 -- we don't want to use the temp dir, because graphics would be then generated outside of
 -- the directory of the source document. so we will make
 local tmp_name = os.tmpname()
 if pos == sequence then
   -- base tmp_name on the input name in the last step of sequence
   -- so the generated images won't have random names
   tmp_name = arg.input .. "-preprocess_input"
 else
   tmp_name = tmp_name:match("([^/\\]+)$")
 end
 return tmp_name
end



local function execute_sequence(sequence, arg, make)
 -- keep track of all generated tmp files
 local temp_files = {}
 -- the temporary file for the current compilation step
 -- should become the tex_file for the next one. It doesn't
 -- matter that it isn't TeX file in some cases
 local previous_temp
 for pos, cmd_name in ipairs(sequence) do
   local tmp_name = get_temp_name(arg,pos, #sequence)
   temp_files[#temp_files+1] = tmp_name
   -- make the temp file name accessible to the executed commands
   arg.tmp_file = tmp_name
   -- the current temporary file should become tex_file in the next step
   -- in the first execution of the compilation sequence we will use the
   -- actual input file name
   arg.tex_file = previous_temp or arg.tex_file
   previous_temp = tmp_name
   -- get the command to execute
   local cmd  = commands[cmd_name]
   -- fill the command template with make4ht arguments and execute
   local command = cmd.command % arg
   log:info(command)
   mkutils.execute(command)
 end
 return temp_files
end

local function get_preprocessing_pipeline(input_file)
   -- detect the file extension
   local extension = input_file:match("%.(.-)$")
   if not extension then return nil, "Cannot get extension: " .. input_file end
   -- the table with file actions is case insensitive
   -- the extension is converted to lowercase in order
   -- to support both .rnw and .Rnw
   extension = string.lower(extension)
   local matched = filetypes[extension]
   if not matched then return nil, "Unsupported extension: " .. extension end
   return matched
end

-- join the make4ht params and command options tables
local function make_options(arg, command_options)
 local options = {}
 local command_options = command_options or {}
 for k,v in pairs(arg) do options[k] = v end
 for k,v in pairs(command_options) do options[k] = v end
 return options
end

M.modify_build = function(make)

 -- get access to the main arguments
 local arg = make.params
 -- get the execution sequence for the input format
 local matched, msg  = get_preprocessing_pipeline(arg.tex_file)
 if not matched then
   log:error("preprocess_input error: ".. msg)
   return
 end
 -- prepare options
 local options = make_options(arg, matched.options)
 -- run the execution sequence
 local temp_files = execute_sequence(matched.sequence or {}, options, make)
 -- the last temporary file contains the actual TeX file
 local last_temp_file = temp_files[#temp_files]
 -- remove the intermediate temp files
 if #temp_files > 2 then
   for i = 1, #temp_files - 1 do
     log:debug("Removing temporary file", temp_files[i])
     os.remove(temp_files[i])
   end
 end
 if last_temp_file then
   -- update all commands in the .mk4 file with the temp file as tex_file
   local update_params = function(cmd)
     local params = cmd.params
     params.tex_file = last_temp_file
     params.is_tmp_file = true
   end
   for _, cmd in ipairs(make.build_seq) do
     update_params(cmd)
   end
   -- also update the main params
   update_params(make)
 end
 return make
end

return M