-- Copyright 2012 Patrick Gundlach, patrick@gundla.ch
-- Public repository: https://github.com/pgundlach/lua-check-hyphen (issues/pull requests,...)
-- Version: 0.1


-- for debugging purpuse:
-- function w( ... )
--   texio.write_nl(string.format(...))
-- end

local explode = function(s,p)
   local t = { }
   for s in unicode.utf8.gmatch(s,p) do
       if s ~= "" then
           t[#t+1] = s
       end
   end
   return t
end

luachekchyphen = {}


luachekchyphen.hyphenattr = luatexbase.new_attribute("hyphenattr")
luachekchyphen.hyphenwords = {}
luachekchyphen.all_hyphenatedwords = {}
luachekchyphen.word_whitelist = {}
luachekchyphen.rectangle = node.new("whatsit","pdf_literal")
luachekchyphen.rectangle.data = string.format("q 0 0 10 10 re f S Q")

luachekchyphen.deligature = function ( glyph_node )
  local sln = unicode.utf8
  local head = glyph_node.components
  local str = ""
  while head do
    if head.id == 37 then
      if head.components then
        str = str .. luachekchyphen.deligature(head)
      else
        str = str .. sln.char(head.char)
      end
    end
    head = head.next
  end
  return str
end


luachekchyphen.collect_discs = function(head)
  local word_start
  local word
  -- this is where we store all the breakpoints
  local thisbreakpoint
  local word_with_hyphen
  local c
  local hyphencounter = #luachekchyphen.hyphenwords + 1
  local sln = unicode.utf8
  while head do
    if head.id == 0 then
    elseif head.id == 7 then --disc
      word_start = head
      word_end   = head
      while word_start.prev and word_start.prev.id ~= 10 do
        word_start = word_start.prev
      end
      word = ""
      c = 0
      while word_start and word_start.id ~= 10 do
        if word_start == head then -- disc
          -- there is a breakpoint after letter c
          node.set_attribute(head,luachekchyphen.hyphenattr,hyphencounter)
          thisbreakpoint = c
        elseif word_start.id == 37 then
          c = c + 1
          if word_start.components then
            word = word .. luachekchyphen.deligature(word_start)
          elseif sln.match(sln.char(word_start.char),"%a") then
            word = word .. sln.char(word_start.char)
          end
        end
        word_start = word_start.next
      end
      if thisbreakpoint then
        word_with_hyphen = sln.sub(word,1,thisbreakpoint) .. "-" .. sln.sub(word,thisbreakpoint+1,-1)
        luachekchyphen.hyphenwords[hyphencounter] = word_with_hyphen
        hyphencounter = #luachekchyphen.hyphenwords + 1
      end
    end
    head = head.next
  end
  return true
end

luachekchyphen.check_discs = function (head,parent)
  local c
  local word
  local tmp
  while head do
    if head.id < 2 then
      luachekchyphen.check_discs(head.list,head)
    elseif head.id == 7 and head.next and head.next.id == 10 then -- disc
      c = node.has_attribute(head,luachekchyphen.hyphenattr)
      word = luachekchyphen.hyphenwords[c]
      if luachekchyphen.word_whitelist[word] then
      	  -- word found, but OK (whitelisted)
      else
	      luachekchyphen.all_hyphenatedwords[word] = true
     	  if luachekchyphen.drawmarks then
     	  	tmp = node.copy(luachekchyphen.rectangle)
     	  	node.insert_after(parent,head,tmp)
     	  end
	  end
    end
    head = head.next
  end
  return true
end

luachekchyphen.listhyphenatedwords = function()
	if luachekchyphen.final == "true" then
		return
	end
    local unknown_hyphenation_filename = tex.jobname .. ".uhy"
    local unknown_hyphenation_file = io.open(unknown_hyphenation_filename,"w")
  
    texio.write_nl("All words with unknown hyphenation below")
    for k,v in pairs(luachekchyphen.all_hyphenatedwords) do
  	    texio.write_nl(k)
    	unknown_hyphenation_file:write(k .. "\n")
    end
    unknown_hyphenation_file:close()
    texio.write_nl("All words with unknown hyphenation above\n")
end

luachekchyphen.enable = function()
	if luachekchyphen.final == "true" then
		return
	end
  	local whitelistfile,err
  	local filecontents
  	if luachekchyphen.whitelist then
  		for i,v in ipairs(string.explode(luachekchyphen.whitelist,",")) do
  			whitelistfile,err = io.open(v)
  			if not whitelistfile then
  				texio.write_nl(err)
	  		else
  				filecontents = whitelistfile:read("*a")
  				for _,entry in ipairs(explode(filecontents,"[^%s]+")) do
  					luachekchyphen.word_whitelist[entry] = true
	  			end
  			end
  		end
 	end
  	if luachekchyphen.mark == "true" then
  		luachekchyphen.drawmarks = true
  	end
    luatexbase.add_to_callback("pre_linebreak_filter", luachekchyphen.collect_discs,"collect_discs")
    luatexbase.add_to_callback("post_linebreak_filter",luachekchyphen.check_discs,"check_discs")
end

return luachekchyphen

-- end of file
