\ProvidesPackage{odsfile}
\RequirePackage{luacode,xkeyval}

%keyval keys

\define@key{includespread}{file}{\loadodsfile{#1}}
\define@key{includespread}{sheet}{\luaexec{sheetname = "\luatexluaescapestring{#1}"}}
\define@key{includespread}{range}{\luaexec{%
local x1,y1,x2,y2 = odsreader.getRange("\luatexluaescapestring{#1}")%
range = {x1,y1,x2,y2}%
}}
\define@key{includespread}{template}{\luaexec{currenttemplate="\luatexluaescapestring{#1}"}}%
\define@key{includespread}{rowtemplate}{\luaexec{rowtemplate="\luatexluaescapestring{\unexpanded{#1}}"}}%

\define@choicekey*+{includespread}{rowseparator}[\val\nr]{tableline,hline,newline}[tableline]%
{%
\ifcase\nr\relax%
  \luaexec{rowseparator=""}%
\or%
  \luaexec{rowseparator="\\hline "}%
\or%
\luaexec{%
rowseparator=[[\\n]]
}
\fi%
}{%
	\luaexec{rowseparator="\luatexluaescapestring{#1}"}
}
\define@key{includespread}{columnbreak}{%
  \luaexec{columnbreak="\luatexluaescapestring{\unexpanded{#1}}{}"}
}
\define@key{includespread}{coltypes}{%
  \luaexec{coltypes="\luatexluaescapestring{\unexpanded{#1}}"}
}
\define@choicekey*+{includespread}{columns}[\val\nr]{head,top,none}{%
\ifcase\nr\relax%
  \luaexec{columns=1}%
\or%
  \luaexec{columns=2}%
\fi%
}{%
\luaexec{%
local function split(s,sep)
        local sep, fields = sep or ":", {}
        local pattern = string.format("([^\%s]+)", sep)
        s:gsub(pattern, function(c) fields[\#fields+1] = c end)
        return fields
end
local s="\luatexluaescapestring{#1}"
columns = split(s,",")
}%
}{}%


% Variable initialization and helper functions
\begin{luacode*}
odsreader   = require("odsfile")
odsfile     = nil
sheetname   = nil
range       = {nil,nil,nil,nil}
columns     = nil
templates   = {}
row         = {}
body        = nil
odsfilename = ""
currenttemplate = nil
rowtemplate=nil
\end{luacode*}

\newcommand\loadodsfile[2][]{%
  \setkeys{includespread}{#1}%
  \luaexec{%
    odsfilename     = "\luatexluaescapestring{#2}"%
    local ods   = odsreader.load(odsfilename)%
    odsfile, e  = ods:loadContent()%
  }%
}

\newcommand\tabletemplate[2]{%
  \luaexec{%
    templates["#1"]="\luatexluaescapestring{\unexpanded{#2}}"
  }
}

\newcommand\includespread[1][]{%
  \luaexec{%
    range = {nil,nil,nil,nil}
    rowseparator = ""
    columns=nil
    currenttemplate = nil 
    rowtemplate = nil
    columnbreak = "\\linebreak{}"
    coltypes    = nil
  }%
  \setkeys{includespread}{#1}%
  \luaexec{%
    body   = odsreader.getTable(odsfile,sheetname)
    local values = odsreader.tableValues(body,range[1],range[2],range[3],range[4])
    %-- Conversion of odsfile table values to LaTeX tabular  
    local concatParagraphs = function(column)
      % -- second returned value signalize whether cell contain paragraph, or not 
		  local getCell = odsreader.get_cell
      if type(column) =="table" then 
        return getCell(column, columnbreak), true
      end
      return getCell(column,""), false
    end
    local rowValues = function(row, headings)
      local headings = headings or {}
      local t={} 
      local i = 1
      for _,column in pairs(row) do 
        local value, br  = concatParagraphs(column.value) 
        value = value or ""
        table.insert(t,value)
        headings[i] = br
        i = i + 1
      end
      return t, headings
    end
    local makeColtypes = function(h)
      local maxsize = tex.hsize / 65536
      local h = h or {}
      local p = 0
      for _, c in pairs(h) do
        if c then 
          p = p + 1 
        end 
      end
      if p > 0 then
        local j = {}
        local size = tostring(math.floor(maxsize / \#h)) .. "pt"
        for _, c in pairs(h) do 
          local k = "l"
          if c then k = "p{"..size.."}" end
          j[\#j+1]= k
        end
        return table.concat(j)
      else 
        return string.rep("l",\#h)
      end
    end
    if rowtemplate == nil then
      local headings = {}
      local currow = {}
      currenttemplate = currenttemplate or "default"
      content = {}   
      for i,row in pairs(values) do
        currow, headings = rowValues(row, headings)
      	table.insert(content,table.concat(currow," & "))
      end
      %-- Column headings handling
      local colheading=""
      if type(columns) == "number" and columns == 1 then
      	columns = rowValues(values[1])
      	content = odsreader.table_slice(content,2,nil)
      elseif type(columns) == "number" and columns == 2 then
      	local t = odsreader.tableValues(body,range[1],1,range[3],2)
      	columns = rowValues(t[1])
      end  
      if type(columns) == "table" then colheading = table.concat(columns," & ") .. "\\\\" end
      % coltypes = ""
      if type(content)== "table" then 
        % coltypes= string.rep("l",\#content[1]) 
        if not coltypes then
          coltypes = makeColtypes(headings)
        end
      end
      content = table.concat(content, "\\\\ "..rowseparator) .. "\\\\"
      local result = odsreader.interp(templates[currenttemplate],{content=content,coltypes=coltypes,colheading=colheading,rowsep=rowseparator})
      print(result)
      tex.print(result)
    else
      local content = {}  
      currenttemplate = currenttemplate or "empty"
      for _,row in pairs(values) do
        table.insert(content,odsreader.interp(rowtemplate,rowValues(row)))
      end
      content = table.concat(content,rowseparator) 
      tex.sprint(odsreader.interp(templates[currenttemplate],{content=content}))
    end
  }
}

\tabletemplate{empty}{-{content}}

\tabletemplate{default}{-{colheading}-{rowsep}-{content}}

\tabletemplate{booktabs}{%
\begin{tabular}{-{coltypes}}
\toprule
-{colheading}
\midrule
-{content}
\\ \bottomrule
\end{tabular}
}

% Interface for adding of new rows

\newenvironment{AddRow}[1][]{%
\def\AddString##1##2{% 
\luaexec{%
local pos = "\luatexluaescapestring{##2}"%
if pos == "" then pos = nil end; row:addString("\luatexluaescapestring{\unexpanded{##1}}",nil,pos)%
}%
}%
\def\AddNumber##1##2{%
\luaexec{%
local pos = "\luatexluaescapestring{##2}"%
if pos == "" then pos = nil end; row:addFloat("\luatexluaescapestring{##1}",nil,pos)%
}%
}%
\luaexec{%
pos = "\luatexluaescapestring{#1}"%
if pos == "" then pos = nil end; row = odsreader.newRow()%
}%
}{%
\luaexec{%
body = body or odsreader.getTable(odsfile)
row:insert(body,pos)%
}%
}

% Interface for saving the spreadsheet

\newcommand\savespreadsheet{%
\luaexec{%
 local xml = require("luaxml-mod-xml")
 f = io.open("content.xml","w")%
 f:write(xml.serialize(odsfile.root))%
 f:close()%
 odsreader.updateZip(odsfilename,"content.xml")%
}%
}

% support for hyperlinks in cells

\newcommand\odslink[2]{\texttt{#2}}

\AtBeginDocument{%
	\@ifpackageloaded{hyperref}{%
		\renewcommand\odslink[2]{\href{#1}{#2}}%
	}{}
}
\endinput
