-- Simple make system for tex4ht
--kpse.set_program_name("luatex")
-- module(...,package.seeall)
local m = {}
local log = logging.new "make4ht-lib"

Make = {}
--Make.params = {}
Make.build_seq = {}
-- Patterns for matching output filenames
Make.matches = {}
Make.image_patterns = {}
Make.run_count = {}

Make.add = function(self,name,fn,par,rep)
       local par = par or {}
       self.params = self.params or {}
       Make[name] = function(self,p,typ)
               local params = {}
               for k,v in pairs(self.params) do params[k] = v end
               for k,v in pairs(par) do params[k] = v; log:info("setting param "..k) end
               local typ = typ or "make"
               local p = p or {}
               local fn = fn
               for k,v in pairs(p) do
                       params[k]=v
                       log:info("Adding: ",k,v)
               end
               -- print( fn % params)
               local command = {
                       name=name,
                       type=typ,
                       command = fn,
                       params = params,
                       repetition = rep
               }
               table.insert(self.build_seq,command)
       end
end

Make.length = function(self)
       return #self.build_seq
end

Make.match = function(self, pattern, command, params)
       local params = params or {}
       table.insert(self.matches,{pattern = pattern, command = command, params = params})
end

Make.run_command = function(self,filename,s)
       local command = s.command
       local params  = s.params
       params["filename"] = filename
       log:info("parse_lg process file: "..filename)
       --for k,v in pairs(params) do print(k..": "..v) end
       if type(command) == "function" then
               return command(filename,params)
       elseif type(command) == "string" then
               local run = command % params
               log:info("Execute: " .. run)
   return mkutils.execute(run)
       end
       return false, "parse_lg: Command is not string or function"
end

Make.image = function(self, pattern, command, params)
       local tab = {
               pattern = pattern,
               command = command,
               params  = params
       }
       table.insert(self.image_patterns, tab)
end

Make.image_convert =  function(self, images)
       local image_patterns = self.image_patterns or {}
       for i, r in pairs(image_patterns) do
               local p = self.params or {}
               local v = r.params or {}
               for k,v in pairs(v) do
                       p[k]= v
               end
               image_patterns[i].params = p
       end
       for _,i in ipairs(images) do
               local output = i.output
               for _, x in ipairs(image_patterns) do
                       local pattern = x.pattern
                       if output:match(pattern) then
                               local command = x.command
                               local p = x.params or {}
                               p.output = output
                               p.page= i.page
                               p.source = i.source
                               if type(command) == "function" then
                                       command(p)
                               elseif type(command) == "string" then
                                       local c = command % p
                                       log:info("Make4ht convert: "..c)
                                       mkutils.execute(c)
                               end
                               break
                       end
               end
       end
end

Make.file_matches = function(self, files)
       local statuses = {}
       -- First make params for all matchers
       for k,v in ipairs(self.matches) do
               local v = self.matches[k].params or {}
               local p = self.params or {}
               for i,j in pairs(p) do
                       v[i] = j
               end
               self.matches[k].params = v
       end
       -- Loop over files, run command on matched
       for _, file in ipairs(files)do
               statuses[file] = {}
               for _, s in ipairs(self.matches) do
                       local pattern= s.pattern
                       if file:match(pattern) then
                               local status, msg = self:run_command(file,s)
                               msg = msg or "No message given"
                               table.insert(statuses[file],status)
                               if status == false then
                                       log:info(msg)
                                       break
                               end
                       end
               end
       end
       return statuses
end

-- add files from the mk4 file
-- we must add them to the table generated from the lg file, so they can be processed later
--
Make.add_file = function(self, filename)
 -- self.lgfile should be present, as it is created once the lg_file was parsed for the first time
 local lg = self.lgfile or {}
 local files = lg.files or {}
 -- run filters on the file
 local filtertable = {filename}
 -- should we care about return status?
 self:file_matches(filtertable)
 -- break if the file is present already
 -- start at the end, it it was added by a build file, the file will be likely at the end
 for i = #files,1,-1  do
   if files[i] == filename then return false, "File was already added" end
 end
 -- save the added file to the lg_file
 table.insert(lg.files, filename)
 self.lg = lg
end

Make.run = function(self)
       local return_codes = {}
 local params = self.params or {}
       for _,v in ipairs(self.build_seq) do
               --print("sekvence: "..v.name)
               for p,n in pairs(v.params) do params[p] = n end
               --for c,_ in pairs(params) do print("build param: "..c) end
               if type(v.command)=="function" then
                       table.insert(return_codes,{name=v.name,status = v.command(params)})
               elseif type(v.command) =="string" then
                       local command = v.command % params
                       -- Some commands should be executed only limited times, typicaly once
                       -- tex4ht or t4ht for example
                       local run_count = self.run_count[v.command] or 0
                       run_count = run_count + 1
                       self.run_count[v.command] = run_count
                       local repetition = v.repetition
                       if repetition and run_count > repetition then
                               log:warning (command .." can be executed only "..repetition .."x")
                       else
                         log:info("executing: " .. command)
       local status = mkutils.execute(command)
                         table.insert(return_codes,{name=v.name,status=status})
                       end
               else
                       log:warning("Unknown command type, must be string or function - " ..v.name..": "..type(v.command))
               end
               local correct_exit = params.correct_exit or nil
               if correct_exit then
                       local last_return = return_codes[#return_codes] or {}
                       local current_status = last_return.status or 0
                       if current_status ~= correct_exit then
                               local last_name = last_return.name or "unknown"
                               log:fatal("Fatal error. Command "..last_name .." returned exit code "..current_status)
                               os.exit(1)
                       end
               end
       end
 local lgfile = params.input and params.input .. ".lg" or nil
 if params.builddir~="" then
   lgfile = params.builddir .. "/" .. lgfile
 end
       if lgfile then
       self.lgfile = self.lgfile or mkutils.parse_lg(lgfile, params.builddir)
   local lg = self.lgfile
               -- First convert images from lg files
               self:image_convert(lg["images"])
               -- Then run file matchers on lg files and converted images
               local files = lg["files"]
               for _,v in ipairs(lg["images"]) do
                       local v = v.output
                       -- print(v)
                       table.insert(files,v)
               end
               self:file_matches(files)
       else
               log:warning("No lg file. tex4ht run failed?")
       end
       return return_codes
end

m.Make = Make

return m

--[[Make:add("hello", "hello ${world}", {world = "world"})
Make:add("ajaj", "ajaj")
Make:hello()
Make:hello{world="světe"}
Make:hello()
Make:run()
--]]