-- Greenhouse is a simple text scrolling handler for terminal programs.
-- The idea is that it can be set a region to do its scrolling and paging
-- job and then the user can draw whatever outside it.
-- This reduces code duplication for the message viewer
-- and flowerbook.

local ansikit = require 'ansikit'
local lunacolors = require 'lunacolors'
local terminal = require 'terminal'
local Page = require 'nature.greenhouse.page'
local Object = require 'nature.object'

local Greenhouse = Object:extend()

function Greenhouse:new(sink)
	local size = terminal.size()
	self.region = size
	self.contents = nil -- or can be a table
	self.start = 1 -- where to start drawing from (should replace with self.region.y)
	self.offset = 1 -- vertical text offset
	self.horizOffset = 1
	self.sink = sink
	self.pages = {}
	self.curPage = 1
	self.step = {
		horizontal = 5,
		vertical = 1
	}
	self.separator = '─'
	self.keybinds = {
		['Up'] = function(self) self:scroll 'up' end,
		['Down'] = function(self) self:scroll 'down' end,
		['Left'] = function(self) self:scroll 'left' end,
		['Right'] = function(self) self:scroll 'right' end,
		['Ctrl-Left'] = self.previous,
		['Ctrl-Right'] = self.next,
		['Ctrl-N'] = function(self) self:toc(true) end,
		['Enter'] = function(self)
			if self.isSpecial then
				self:jump(self.specialPageIdx)
				self:special(false)
			end
		end
	}
	self.isSpecial = false
	self.specialPage = nil
	self.specialPageIdx = 1
	self.specialOffset = 1

	return self
end

function Greenhouse:addPage(page)
	table.insert(self.pages, page)
end

function Greenhouse:updateCurrentPage(text)
	local page = self.pages[self.curPage]
	page:setText(text)
end

function Greenhouse:sub(str, offset, limit)
	local overhead = 0
	local function addOverhead(s)
		overhead = overhead + string.len(s)
	end

	local s = str:gsub('\x1b%[%d+;%d+;%d+;%d+;%d+%w', addOverhead)
     :gsub('\x1b%[%d+;%d+;%d+;%d+%w', addOverhead)
     :gsub('\x1b%[%d+;%d+;%d+%w',addOverhead)
     :gsub('\x1b%[%d+;%d+%w', addOverhead)
     :gsub('\x1b%[%d+%w', addOverhead)

	return s:sub(offset, utf8.offset(str, limit + overhead) or limit + overhead)
	--return s:sub(offset, limit + overhead)
end

function Greenhouse:draw()
	local workingPage = self.pages[self.curPage]
	local offset = self.offset
	if self.isSpecial then
		offset = self.specialOffset
		workingPage = self.specialPage
	end

	if workingPage.lazy and not workingPage.loaded then
		workingPage.initialize()
	end

	local lines = workingPage.lines
	self.sink:write(ansikit.getCSI(self.start .. ';1', 'H'))
	self.sink:write(ansikit.getCSI(2, 'J'))

	local writer = self.sink.writeln
	for i = offset, offset + self.region.height - 1 do
		if i > #lines then break end

		if i == offset + self.region.height - 1 then writer = self.sink.write end

		self.sink:write(ansikit.getCSI(self.start + i - offset .. ';1', 'H'))
		local line = lines[i]:gsub('{separator}', function() return self.separator:rep(self.region.width - 1) end)
		writer(self.sink, self:sub(line:gsub('\t', '        '), self.horizOffset, self.region.width))
	end
	writer(self.sink, '\27[0m')
	self:render()
end

function Greenhouse:render()
end

function Greenhouse:scroll(direction)
	if self.isSpecial then
		if direction == 'down' then
			self:next(true)
		elseif direction == 'up' then
			self:previous(true)
		end
		return
	end

	local lines = self.pages[self.curPage].lines

	local oldOffset = self.offset
	local oldHorizOffset = self.horizOffset
	if direction == 'down' then
		self.offset = math.min(self.offset + self.step.vertical, math.max(1, #lines - self.region.height))
	elseif direction == 'up' then
		self.offset = math.max(self.offset - self.step.vertical, 1)
	end

--[[
	if direction == 'left' then
		self.horizOffset = math.max(self.horizOffset - self.step.horizontal, 1)
	elseif direction == 'right' then
		self.horizOffset = self.horizOffset + self.step.horizontal
	end
]]--

	if self.offset ~= oldOffset then self:draw() end
	if self.horizOffset ~= oldHorizOffset then self:draw() end
end

function Greenhouse:update()
	self:resize()
	if self.isSpecial then
		self:updateSpecial()
	end

	self:draw()
end


function Greenhouse:special(val)
	self.isSpecial = val
	self:update()
end

function Greenhouse:toggleSpecial()
	self:special(not self.isSpecial)
end

--- This function will be called when the special page
--- is on and needs to be updated.
function Greenhouse:updateSpecial()
end

function Greenhouse:contents()
end

function Greenhouse:toc(toggle)
	if not self.isSpecial then
		self.specialPageIdx = self.curPage
	end
	if toggle then self.isSpecial = not self.isSpecial end
	-- Generate a special page for our table of contents
	local tocText = string.format([[
%s

]], lunacolors.cyan(lunacolors.bold '―― Table of Contents ――'))

	local genericPageCount = 1
	local contents = self:contents()
	if contents then
		for i, c in ipairs(contents) do
			local title = c.title
			if c.active then
				title = lunacolors.invert(title)
			end

			tocText = tocText .. title .. '\n'
		end
	else
		for i, page in ipairs(self.pages) do
			local title = page.title
			if title == 'Page' then
				title = 'Page #' .. genericPageCount
				genericPageCount = genericPageCount + 1
			end
			if i == self.specialPageIdx then
				title = lunacolors.invert(title)
			end

			tocText = tocText .. title .. '\n'
		end
	end
	self.specialPage = Page('TOC', tocText)
	function self:updateSpecial()
		self:toc()
	end
	self:draw()
end

function Greenhouse:resize()
	local size = terminal.size()
	self.region = size
end

function Greenhouse:next(special)
	local oldCurrent = special and self.specialPageIdx or self.curPage
	local pageIdx = math.min(oldCurrent + 1, #self.pages)

	if special then
		self.specialPageIdx = pageIdx
	else
		self.curPage = pageIdx
	end

	if pageIdx ~= oldCurrent then
		self.offset = 1
		self:update()
	end
end

function Greenhouse:previous(special)
	local oldCurrent = special and self.specialPageIdx or self.curPage
	local pageIdx = math.max(self.curPage - 1, 1)

	if special then
		self.specialPageIdx = pageIdx
	else
		self.curPage = pageIdx
	end

	if pageIdx ~= oldCurrent then
		self.offset = 1
		self:update()
	end
end

function Greenhouse:jump(idx)
	if idx ~= self.curPage then
		self.offset = 1
	end
	self.curPage = idx
	self:update()
end

function Greenhouse:keybind(key, callback)
	self.keybinds[key] = callback
end

function Greenhouse:input(char)
end

function Greenhouse:initUi()
	local ansikit = require 'ansikit'
	local bait = require 'bait'
	local commander = require 'commander'
	local hilbish = require 'hilbish'
	local terminal = require 'terminal'
	local Page = require 'nature.greenhouse.page'
	local done = false

	bait.catch('signal.sigint', function()
		ansikit.clear()
		done = true
	end)

	bait.catch('signal.resize', function()
		self:update()
	end)

	ansikit.screenAlt()
	ansikit.clear(true)
	self:draw()

	hilbish.goro(function()
		while not done do
			local c = read()
			self:keybind('Ctrl-D', function()
				done = true
			end)

			if self.keybinds[c] then
				self.keybinds[c](self)
			else
				self:input(c)
			end

	--[[
			if c == 27 then
				local c1 = read()
				if c1 == 91 then
					local c2 = read()
					if c2 == 66 then -- arrow down
						self:scroll 'down'
					elseif c2 == 65 then -- arrow up
						self:scroll 'up'
					end

					if c2 == 49 then
						local c3 = read()
						if c3 == 59 then
							local c4 = read()
							if c4 == 53 then
								local c5 = read()
								if c5 == 67 then
									self:next()
								elseif c5 == 68 then
									self:previous()
								end
							end
						end
					end
				end
				goto continue
			end
			]]--

			::continue::
		end
	end)

	while not done do
		--
	end
	ansikit.showCursor()
	ansikit.screenMain()
end

function read()
	terminal.saveState()
	terminal.setRaw()
	local c = hilbish.editor.readChar()

	terminal.restoreState()
	return c
end

return Greenhouse
