--
-- ltj-debug.lua
--
local ltjdbg = {}
luatexja.debug = ltjdbg
local table, string = table, string

-------------------- pretty-print

local function get_serialize_param()
  return table.serialize_functions,
         table.serialize_compact,
         table.serialize_inline
end
local function set_serialize_param(s_f, s_c, s_i)
  table.serialize_functions = s_f
  table.serialize_compact = s_c
  table.serialize_inline = s_i
end

local function normal_serialize(t)
  local s_f, s_c, s_i = get_serialize_param()
  set_serialize_param(true, true, true)
  local ret = table.serialize(t, false, false, true)
  set_serialize_param(s_f, s_c, s_i)
  return ret
end

local function table_tosource(t)
  if not next(t) then return "{}" end
  local res_n = "\127"..normal_serialize({t}).."\127"
  local s, e, cap = res_n:find("\127{\n ({ .* }),\n}\127")
  if s == 1 and e == res_n:len() then return cap
  else return normal_serialize(t)
  end
end
ltjdbg.table_tosource = table_tosource

local function function_tosource(f)
  local res = normal_serialize({f})
  return res:sub(4, res:len() - 3)
end
ltjdbg.function_tosource = function_tosource

--! 値 v をそれを表すソース文字列に変換する.
--! lualibs の table.serialize() の処理を利用している.
local function tosource(v)
  local tv = type(v)
  if tv == "function" then return function_tosource(v)
  elseif tv == "table" then return table_tosource(v)
  elseif tv == "string" then return string.format('%q', v)
  else return tostring(v)
  end
end
ltjdbg.tosource = tosource

local function coerce(f, v)
  if f == "q" then return "s", tosource(v)
  elseif f == "s" then return f, tostring(v)
  else return f, tonumber(v) or 0
  end
end

local function do_pformat(fmt, ...)
  fmt = fmt:gsub("``", "\127"):gsub("`", "%%"):gsub("\127", "`")
  local i, na, a = 0, {}, {...}
  local function proc(p, f)
    i = i + 1; f, na[i] = coerce(f, a[i])
    return p..f
  end
  fmt = fmt:gsub("(%%[-+#]?[%d%.]*)([a-zA-Z])", proc)
  return fmt:format(unpack(na))
end

--! string.format() の拡張版. 以下の点が異なる.
--!  - %q は全ての型について tosource() に変換
--!  - <%> の代わりに <`> も使える (TeX での使用のため)
--!  - %d, %s 等でキャストを行う
local function pformat(fmt, ...)
  if type(fmt) == "string" then
    return do_pformat(fmt, ...)
  else
    return tosource(fmt)
  end
end
ltjdbg.pformat = pformat

-------------------- 所要時間合計
do
   local max = math.max
   local gettime = os.socketgettime or (socket and socket.gettime)
   local time_stat = {}
   local function start_time_measure(n)
      if not time_stat[n] then
	 time_stat[n] = {1, -gettime()}
      else
	 local t = time_stat[n]
	 t[1], t[2] = t[1]+1, t[2]-gettime()
      end
   end
   local function stop_time_measure(n)
      local t = time_stat[n]
      t[2] = t[2] + gettime()
   end

   local function print_measure()
      stop_time_measure 'RUN'
      local temp = {}
      for i,v in pairs(time_stat) do
	 temp[#temp+1] = { i, v[1], v[2], v[2]/v[1] }
      end
      table.sort(temp, function (a,b) return (a[4]>b[4]) end)
      print()
      print('desc', 'ave. (us)', 'times', 'total (ms)')
      for _,v in ipairs(temp) do
	 print ((v[1] .. '                '):sub(1,16), 1000000*v[4], v[2], 1000*v[3])
      end
   end
   if luatexja.base then
      luatexja.base.start_time_measure = start_time_measure
      luatexja.base.stop_time_measure = stop_time_measure
      luatexbase.add_to_callback('stop_run', print_measure, 'luatexja.time_measure', 1)
      luatexbase.add_to_callback('pre_linebreak_filter',
				 function(p)
				    start_time_measure 'tex_linebreak'; return p
				 end,
				 'measure_tex_linebreak', 20000)
   end
end

-------------------- debug logging
do
local debug_show_term = true
local debug_show_log = true
--! デバッグログを端末に出力するか
local function show_term(v)
  debug_show_term = v
end
ltjdbg.show_term = show_term
--! デバッグログをログファイルに出力するか
function show_log(v)
  debug_show_log = v
end
ltjdbg.show_log = show_log

local function write_debug_log(s)
  local target
  if debug_show_term and debug_show_log then
    texio.write_nl("term and log", s)
  elseif debug_show_term and not debug_show_log then
    texio.write_nl("term", s)
  elseif not debug_show_term and debug_show_log then
    texio.write_nl("log", s)
  end
end

--! デバッグログ出力. 引数は pformat() と同じ.
local function out_debug(...)
  if debug_show_term or debug_show_log then
    write_debug_log("%DEBUG:"..pformat(...))
  end
end

--! デバッグログ出力, パッケージ名付き.
local function package_debug(pkg, ...)
  if debug_show_term or debug_show_log then
    write_debug_log("%DEBUG("..pkg.."):"..pformat(...))
  end
end

--! パッケージ名付きデバッグログ出力器を得る.
local function debug_logger(pkg)
  return function(...) package_debug(pkg, ...) end
end

if luatexja.base then
  luatexja.base.out_debug = out_debug
  luatexja.base.package_debug = package_debug
  luatexja.base.debug_logger = debug_logger
  luatexja.base.show_term = show_term
  luatexja.base.show_log = show_log
end
end

-------------------- all done
-- EOF
