require "ui/widget/buttontable"

ReaderHighlight = InputContainer:new{}

function ReaderHighlight:init()
	if Device:hasKeyboard() then
		self.key_events = {
			ShowToc = {
				{ "." },
				doc = _("highlight text") },
		}
	end
end

function ReaderHighlight:initGesListener()
	self.ges_events = {
		Tap = {
			GestureRange:new{
				ges = "tap",
				range = Geom:new{
					x = 0, y = 0,
					w = Screen:getWidth(),
					h = Screen:getHeight()
				}
			}
		},
		Hold = {
			GestureRange:new{
				ges = "hold",
				range = Geom:new{
					x = 0, y = 0,
					w = Screen:getWidth(),
					h = Screen:getHeight()
				}
			}
		},
		HoldRelease = {
			GestureRange:new{
				ges = "hold_release",
				range = Geom:new{
					x = 0, y = 0,
					w = Screen:getWidth(),
					h = Screen:getHeight()
				}
			}
		},
		HoldPan = {
			GestureRange:new{
				ges = "hold_pan",
				range = Geom:new{
					x = 0, y = 0,
					w = Screen:getWidth(),
					h = Screen:getHeight()
				},
				rate = 2.0,
			}
		},
	}
end

function ReaderHighlight:onSetDimensions(dimen)
	-- update listening according to new screen dimen
	if Device:isTouchDevice() then
		self:initGesListener()
	end
end

function ReaderHighlight:onTap(arg, ges)
	local function inside_box(ges, box)
		local pos = self.view:screenToPageTransform(ges.pos)
		if pos then
			local x, y = pos.x, pos.y
			if box.x <= x and box.y <= y 
				and box.x + box.w >= x 
				and box.y + box.h >= y then
				return true
			end
		end
	end
	if self.hold_pos then
		self.view.highlight.temp[self.hold_pos.page] = nil
		UIManager:setDirty(self.dialog, "partial")
		self.hold_pos = nil
		return true
	end
	local pages = self.view:getCurrentPageList()
	for key, page in pairs(pages) do
		local items = self.view.highlight.saved[page]
		if not items then items = {} end
		for i = 1, #items do
			for j = 1, #items[i].boxes do
				if inside_box(ges, items[i].boxes[j]) then
					DEBUG("Tap on hightlight")
					self.edit_highlight_dialog = HighlightDialog:new{
						buttons = {
							{
								{
									text = _("Delete"),
									callback = function()
										self:deleteHighlight(page, i)
										UIManager:close(self.edit_highlight_dialog)
									end,
								},
								{
									text = _("Edit"),
									enabled = false,
									callback = function()
										self:editHighlight()
										UIManager:close(self.edit_highlight_dialog)
									end,
								},
							},
						},
					}
					UIManager:show(self.edit_highlight_dialog)
					return true
				end
			end
		end
	end
end

function ReaderHighlight:onHold(arg, ges)
	self.hold_pos = self.view:screenToPageTransform(ges.pos)
	DEBUG("hold position in page", self.hold_pos)
	if not self.hold_pos then
		DEBUG("not inside page area")
		return true
	end
	self.page_boxes = self.ui.document:getTextBoxes(self.hold_pos.page)
	--DEBUG("page text", page_boxes)
	
	if not self.page_boxes or #self.page_boxes == 0 then
		DEBUG("no page boxes detected")
		return true
	end
	
	self.selected_word = self:getWordFromPosition(self.page_boxes, self.hold_pos)
	DEBUG("selected word:", self.selected_word)
	if self.selected_word then
		local boxes = {}
		table.insert(boxes, self.selected_word.box)
		self.view.highlight.temp[self.hold_pos.page] = boxes
		UIManager:setDirty(self.dialog, "partial")
	end
	return true
end

function ReaderHighlight:onHoldPan(arg, ges)
	if not self.page_boxes or #self.page_boxes == 0 or self.hold_pos == nil then
		DEBUG("no page boxes detected")
		return true
	end
	self.holdpan_pos = self.view:screenToPageTransform(ges.pos)
	DEBUG("holdpan position in page", self.holdpan_pos)
	self.selected_text = self:getTextFromPositions(self.page_boxes, self.hold_pos, self.holdpan_pos)
	--DEBUG("selected text:", self.selected_text)
	if self.selected_text then
		self.view.highlight.temp[self.hold_pos.page] = self.selected_text.boxes
		-- remove selected word if hold moves out of word box
		if self.selected_word and
			not self.selected_word.box:contains(self.selected_text.boxes[1]) then
			self.selected_word = nil
		end
		UIManager:setDirty(self.dialog, "partial")
	end
end

function ReaderHighlight:lookup(selected_word)
	-- if we extracted text directly
	if selected_word.word then
		self.ui:handleEvent(Event:new("LookupWord", selected_word.word))
	-- or we will do OCR
	else
		local word_box = selected_word.box
		--word_box.x = word_box.x - math.floor(word_box.h * 0.1)
		--word_box.y = word_box.y - math.floor(word_box.h * 0.2)
		--word_box.w = word_box.w + math.floor(word_box.h * 0.2)
		--word_box.h = word_box.h + math.floor(word_box.h * 0.4)
		local word = self.ui.document:getOCRWord(self.hold_pos.page, word_box)
		DEBUG("OCRed word:", word)
		self.ui:handleEvent(Event:new("LookupWord", word))
	end
end

function ReaderHighlight:translate(selected_text)
	if selected_text.text ~= "" then
		self.ui:handleEvent(Event:new("LookupWord", selected_text.text))
	-- or we will do OCR
	else
		local text_box = selected_text.boxes[1]
		--text_box.x = text_box.x - math.floor(text_box.h * 0.1)
		text_box.y = text_box.y - math.floor(text_box.h * 0.2)
		--text_box.w = text_box.w + math.floor(text_box.h * 0.2)
		text_box.h = text_box.h + math.floor(text_box.h * 0.4)
		local text = self.ui.document:getOCRWord(self.hold_pos.page, text_box)
		DEBUG("OCRed text:", text)
		self.ui:handleEvent(Event:new("LookupWord", text))
	end
end

HighlightDialog = InputContainer:new{
	buttons = nil,
	tap_close_callback = nil,
}

function HighlightDialog:init()
	if Device:hasKeyboard() then
		key_events = {
			AnyKeyPressed = { { Input.group.Any },
				seqtext = "any key", doc = _("close dialog") }
		}
	else
		self.ges_events.TapClose = {
			GestureRange:new{
				ges = "tap",
				range = Geom:new{
					x = 0, y = 0,
					w = Screen:getWidth(),
					h = Screen:getHeight(),
				}
			}
		}
	end
	self[1] = CenterContainer:new{
		dimen = Screen:getSize(),
		FrameContainer:new{
			ButtonTable:new{
				width = Screen:getWidth()*0.9,
				buttons = self.buttons,
			},
			background = 0,
			bordersize = 2,
			radius = 7,
			padding = 2,
		}
	}
end

function HighlightDialog:onTapClose()
	UIManager:close(self)
	if self.tap_close_callback then
		self.tap_close_callback()
	end
	return true
end

function ReaderHighlight:onHoldRelease(arg, ges)
	if self.selected_word then
		self:lookup(self.selected_word)
		self.selected_word = nil
	elseif self.selected_text then
		DEBUG("show highlight dialog")
		self.highlight_dialog = HighlightDialog:new{
			buttons = {
				{
					{
						text = _("Highlight"),
						callback = function()
							self:saveHighlight()
							UIManager:close(self.highlight_dialog)
							self.ui:handleEvent(Event:new("Tap"))
						end,
					},
					{
						text = _("Add Note"),
						enabled = false,
						callback = function()
							self:addNote()
							UIManager:close(self.highlight_dialog)
							self.ui:handleEvent(Event:new("Tap"))
						end,
					},
				},
				{
					{
						text = _("Translate"),
						callback = function()
							self:translate(self.selected_text)
							UIManager:close(self.highlight_dialog)
							self.ui:handleEvent(Event:new("Tap"))
						end,
					},
					{
						text = _("Share"),
						enabled = false,
						callback = function()
							self:shareHighlight()
							UIManager:close(self.highlight_dialog)
							self.ui:handleEvent(Event:new("Tap"))
						end,
					},
				},
				{
					{
						text = _("More"),
						enabled = false,
						callback = function()
							self:moreAction()
							UIManager:close(self.highlight_dialog)
							self.ui:handleEvent(Event:new("Tap"))
						end,
					},
				},
			},
			tap_close_callback = function() self.ui:handleEvent(Event:new("Tap")) end,
		}
		UIManager:show(self.highlight_dialog)
	end
	return true
end

function ReaderHighlight:saveHighlight()
	DEBUG("save highlight")
	local page = self.hold_pos.page
	if self.hold_pos and self.selected_text then
		if not self.view.highlight.saved[page] then
			self.view.highlight.saved[page] = {}
		end
		local hl_item = {}
		hl_item["text"] = self.selected_text.text
		hl_item["boxes"] = self.selected_text.boxes
		hl_item["datetime"] = os.date("%Y-%m-%d %H:%M:%S"),
		table.insert(self.view.highlight.saved[page], hl_item)
		if self.selected_text.text ~= "" then
			self:exportToClippings(page, hl_item)
		end
	end
	--DEBUG("saved hightlights", self.view.highlight.saved[page])
end

function ReaderHighlight:exportToClippings(page, item)
	DEBUG("export highlight to My Clippings")
	local clippings = io.open("/mnt/us/documents/My Clippings.txt", "a+")
	if clippings then
		local current_locale = os.setlocale()
		os.setlocale("C")
		clippings:write(self.document.file:gsub("(.*/)(.*)", "%2").."\n")
		clippings:write("- Koreader Highlight Page "..page.." ")
		clippings:write("| Added on "..os.date("%A, %b %d, %Y %I:%M:%S %p\n\n"))
		clippings:write(item["text"].."\n")
		clippings:write("==========\n")
		clippings:close()
		os.setlocale(current_locale)
	end
end

function ReaderHighlight:addNote()
	DEBUG("add Note")
end

function ReaderHighlight:shareHighlight()
	DEBUG("share highlight")
end

function ReaderHighlight:moreAction()
	DEBUG("more action")
end

function ReaderHighlight:deleteHighlight(page, i)
	DEBUG("delete highlight")	
	table.remove(self.view.highlight.saved[page], i)
end

function ReaderHighlight:editHighlight()
	DEBUG("edit highlight")
end

--[[
get index of nearest word box around pos
--]]
local function getWordBoxIndices(boxes, pos)
	local function inside_box(box)
		local x, y = pos.x, pos.y
		if box.x0 <= x and box.y0 <= y and box.x1 >= x and box.y1 >= y then
			return true
		end
		return false
	end
	local function box_distance(i, j)
		local wb = boxes[i][j]
		if inside_box(wb) then
			return 0
		else
			local x0, y0 = pos.x, pos.y
			local x1, y1 = (wb.x0 + wb.x1) / 2, (wb.y0 + wb.y1) / 2
			return (x0 - x1)*(x0 - x1) + (y0 - y1)*(y0 - y1)
		end
	end

	local m, n = 1, 1
	for i = 1, #boxes do
		for j = 1, #boxes[i] do
			if box_distance(i, j) < box_distance(m, n) then
				m, n = i, j
			end
		end
	end
	return m, n
end

--[[
get word and word box around pos
--]]
function ReaderHighlight:getWordFromPosition(boxes, pos)
	local i, j = getWordBoxIndices(boxes, pos)
	local lb = boxes[i]
	local wb = boxes[i][j]
	if lb and wb then
		local box = Geom:new{
			x = wb.x0, y = lb.y0, 
			w = wb.x1 - wb.x0,
			h = lb.y1 - lb.y0,
		}
		return {
			word = wb.word,
			box = box,
		}
	end
end

--[[
get text and text boxes between pos0 and pos1
--]]
function ReaderHighlight:getTextFromPositions(boxes, pos0, pos1)
    local line_text = ""
    local line_boxes = {}
    local i_start, j_start = getWordBoxIndices(boxes, pos0)
    local i_stop, j_stop = getWordBoxIndices(boxes, pos1)
    if i_start == i_stop and j_start > j_stop or i_start > i_stop then
    	i_start, i_stop = i_stop, i_start
    	j_start, j_stop = j_stop, j_start
    end
    for i = i_start, i_stop do
    	if i_start == i_stop and #boxes[i] == 0 then break end
    	-- insert line words
    	local j0 = i > i_start and 1 or j_start
    	local j1 = i < i_stop and #boxes[i] or j_stop
    	for j = j0, j1 do
    		local word = boxes[i][j].word
    		if word then
    			-- if last character of this word is an ascii char then append a space
    			local space = (word:match("[%z\194-\244][\128-\191]*$") or j == j1)
    						   and "" or " "
    			line_text = line_text..word..space
    		end
    	end
    	-- insert line box
    	local lb = boxes[i]
    	if i > i_start and i < i_stop then
    		local line_box = Geom:new{
				x = lb.x0, y = lb.y0, 
				w = lb.x1 - lb.x0,
				h = lb.y1 - lb.y0,
			}
    		table.insert(line_boxes, line_box)
    	elseif i == i_start and i < i_stop then
    		local wb = boxes[i][j_start]
    		local line_box = Geom:new{
				x = wb.x0, y = lb.y0, 
				w = lb.x1 - wb.x0,
				h = lb.y1 - lb.y0,
			}
    		table.insert(line_boxes, line_box)
    	elseif i > i_start and i == i_stop then
    		local wb = boxes[i][j_stop]
    		local line_box = Geom:new{
				x = lb.x0, y = lb.y0, 
				w = wb.x1 - lb.x0,
				h = lb.y1 - lb.y0,
			}
    		table.insert(line_boxes, line_box)
    	elseif i == i_start and i == i_stop then
    		local wb_start = boxes[i][j_start]
    		local wb_stop = boxes[i][j_stop]
    		local line_box = Geom:new{
				x = wb_start.x0, y = lb.y0, 
				w = wb_stop.x1 - wb_start.x0,
				h = lb.y1 - lb.y0,
			}
    		table.insert(line_boxes, line_box)
    	end
    end
    return {
    	text = line_text,
    	boxes = line_boxes,
    }
end

