local doGlobalize = false
local lddwindow = {}

local to_colors = {
	[" "] = 0,
	["0"] = colors.white,
	["1"] = colors.orange,
	["2"] = colors.magenta,
	["3"] = colors.lightBlue,
	["4"] = colors.yellow,
	["5"] = colors.lime,
	["6"] = colors.pink,
	["7"] = colors.gray,
	["8"] = colors.lightGray,
	["9"] = colors.cyan,
	["a"] = colors.purple,
	["b"] = colors.blue,
	["c"] = colors.brown,
	["d"] = colors.green,
	["e"] = colors.red,
	["f"] = colors.black,
}
local to_blit = {}
for k,v in pairs(to_colors) do
	to_blit[v] = k
end

local stringSub = string.sub
local stringGsub = string.gsub
local tableConcat = table.concat

lddwindow.newWindow = function(nativeTerm, x, y, width, height, visible)
	local output = {
		info = {
			buffer = {{},{},{}},
			x = x,
			y = y,
			textColor = "0",
			backColor = "f",
			cursorX = 1,
			cursorY = 1,
			nativeTerm = nativeTerm or term.current(),
			width = width,
			height = height,
			blink = true,
			palette = {},
		}
	}
	if visible == nil then
		output.info.visible = true
	elseif type(visible) == "boolean" then
		output.info.visible = visible
	else
		output.info.visible = true
	end
	
	local genTblLine = function(char, length)
		local blankLine = {}
		for i = 1, length do
			blankLine[i] = char
		end
		return blankLine
	end
	
	for i = 0, 15 do
--		output.info.palette[2^i] = output.info.nativeTerm.nativePaletteColor(2^i)
		output.info.palette[2^i] = term.nativePaletteColor(2^i)
	end
	
	local fixBuffer = function()
		for y = math.max(output.info.height, #output.info.buffer[1]), 1, -1 do
			if y > output.info.height then
				output.info.buffer[1][y] = nil
				output.info.buffer[2][y] = nil
				output.info.buffer[3][y] = nil
			else
				output.info.buffer[1][y] = output.info.buffer[1][y] or {}
				output.info.buffer[2][y] = output.info.buffer[2][y] or {}
				output.info.buffer[3][y] = output.info.buffer[3][y] or {}
				for x = math.max(output.info.width, #(output.info.buffer[1][1] or {})), 1, -1 do
					if x > output.info.width then
						output.info.buffer[1][y][x] = nil
						output.info.buffer[2][y][x] = nil
						output.info.buffer[3][y][x] = nil
					else
						output.info.buffer[1][y][x] = output.info.buffer[1][y][x] or " "
						output.info.buffer[2][y][x] = output.info.buffer[2][y][x] or output.info.textColor
						output.info.buffer[3][y][x] = output.info.buffer[3][y][x] or output.info.backColor
					end
				end
			end
		end
	end
	
	fixBuffer()
	
	output.reposition = function(x, y, width, height)
		output.info.x = tonumber(x) or output.info.x
		output.info.y = tonumber(y) or output.info.y
		output.info.width = tonumber(width) or output.info.width
		output.info.height = tonumber(height) or output.info.height
		fixBuffer()
	end
	
	output.setCursorPos = function(x, y)
		output.info.cursorX = x or output.info.cursorX
		output.info.cursorY = y or output.info.cursorY
	end
	
	output.getCursorPos = function()
		return output.info.cursorX, output.info.cursorY
	end
	
	output.setCursorBlink = function(blink)
		output.info.blink = blink and true or false
	end
	
	output.getCursorBlink = function(blink)
		return output.info.blink
	end
	
	output.isColor = function()
		return true
	end
	output.isColour = output.isColor
	
	output.getSize = function()
		return output.info.width, output.info.height
	end
	
	output.current = function()
		return output
	end
	
	output.clear = function()
		for y = 1, output.info.height do
			output.info.buffer[1][y] = genTblLine(" ", output.info.width)
			output.info.buffer[2][y] = genTblLine(output.info.textColor, output.info.width)
			output.info.buffer[3][y] = genTblLine(output.info.backColor, output.info.width)
		end
	end
	
	output.clearLine = function()
		if output.info.cursorY >= 1 and output.info.cursorY <= output.info.height then
			output.info.buffer[1][output.info.cursorY] = genTblLine(" ", output.info.width)
			output.info.buffer[2][output.info.cursorY] = genTblLine(output.info.textColor, output.info.width)
			output.info.buffer[3][output.info.cursorY] = genTblLine(output.info.backColor, output.info.width)
		end
	end
	
	output.setTextColor = function(color)
		output.info.textColor = to_blit[color]
	end
	output.setTextColour = output.setTextColor
	
	output.setBackgroundColor = function(color)
		output.info.backColor = to_blit[color]
	end
	output.setBackgroundColour = output.setBackgroundColor
	
	output.getTextColor = function()
		return output.info.textColor
	end
	output.getTextColour = output.getTextColor
	
	output.getBackgroundColor = function()
		return output.info.backColor
	end
	output.getBackgroundColour = output.getBackgroundColor
	
	output.write = function(char)
		local cx
		if output.info.cursorY >= 1 and output.info.cursorY <= height then
			for i = 1, #char do
				cx = -1 + i + output.info.cursorX
				if cx >= 1 and cx <= output.info.width then
					output.info.buffer[1][output.info.cursorY][cx] = stringSub(char, i, i)
					output.info.buffer[2][output.info.cursorY][cx] = output.info.textColor
					output.info.buffer[3][output.info.cursorY][cx] = output.info.backColor
				end
			end
		end
		output.info.cursorX = output.info.cursorX + #char
	end
	
	output.blit = function(char, text, back)
		local cx
		assert(#char == #text and #text == #back, "arguments must be same length")
		if output.info.cursorY >= 1 and output.info.cursorY <= height then
			for i = 1, #char do
				cx = -1 + i + output.info.cursorX
				if cx >= 1 and cx <= output.info.width then
					output.info.buffer[1][output.info.cursorY][cx] = stringSub(char, i, i)
					output.info.buffer[2][output.info.cursorY][cx] = stringSub(text, i, i)
					output.info.buffer[3][output.info.cursorY][cx] = stringSub(back, i, i)
				end
			end
		end
		output.info.cursorX = output.info.cursorX + #char
	end
	
	output.native = function()
		if true then
			return output.info.nativeTerm
		else
			return output
		end
	end
	
	output.nativePaletteColor = output.info.nativeTerm.nativePaletteColor
	output.nativePaletteColour = output.info.nativeTerm.nativePaletteColour
	
	output.setPaletteColor = function(slot, r, g, b)
		output.info.palette[slot] = {r, g, b}
	end
	output.setPaletteColour = output.setPaletteColor
	
	output.getPaletteColor = function(slot)
		return output.info.palette[slot][1], output.info.palette[slot][2], output.info.palette[slot][3]
	end
	output.getPaletteColour = output.getPaletteColor
	
	output.scroll = function(distance)
		for y = distance, output.info.height do
			for c = 1, 3 do
				output.info.buffer[c][y] = output.info.buffer[c][y + distance] or genTblLine(" ", output.info.width)
			end
		end
	end
	
	output.setVisible = function(visible)
		output.info.visible = visible and true or false
	end
	
	-- draws the window
	output.render = function(x, y, ...)
		x = x or output.info.x
		y = y or output.info.y
		-- can render to multiple terminals
		local termList = {...}
		if output.info.visible then
			if #termList == 0 then
				nTerm = nTerm or output.info.nativeTerm
				for yy = 1, output.info.height do
					nTerm.setCursorPos(x, -1 + y + yy)
					nTerm.blit(
						tableConcat(output.info.buffer[1][yy]),
						tableConcat(output.info.buffer[2][yy]),
						tableConcat(output.info.buffer[3][yy])
					)
				end
			else
				for i = 1, #termList do
					for yy = 1, output.info.height do
						termList[i].setCursorPos(x, -1 + y + yy)
						termList[i].blit(
							tableConcat(output.info.buffer[1][yy]),
							tableConcat(output.info.buffer[2][yy]),
							tableConcat(output.info.buffer[3][yy])
						)
					end
				end
			end
		end
	end
	
	-- turns window into an NFT string image
	output.screenshot = function()
		local image = ""
		local tcol, bcol
		local tchar, bchar = string.char(31), string.char(30)
		for y = 1, output.info.height do
			tcol, bcol = "", ""
			for x = 1, output.info.width do
				if tcol ~= output.info.buffer[2][y][x] then
					tcol = output.info.buffer[2][y][x]
					image = image .. tchar .. tcol
				end
				if bcol ~= output.info.buffer[3][y][x] then
					bcol = output.info.buffer[3][y][x]
					image = image .. bchar .. bcol
				end
				image = image .. output.info.buffer[1][y][x]
			end
			if y < output.info.height then
				image = image .. "\n"
			end
		end
		return image
	end
	
	return output
end

if doGlobalize then
	_G.lddwindow = lddwindow
end

return lddwindow
