local args = {...}
local component = require("component")
local computer = require("computer")
local event = require("event")
local gpu = component.gpu
local unicode = require("unicode")
local keyboard = require("keyboard")
local text = require("text")
local os = require("os")
local pal = {}

function resetGPU(w, h)
	gpu.setResolution(w, h)
	gpu.setBackground(0, false)
	gpu.setForeground(16777215, false)
	gpu.fill(1, 1, w, h, " ")
end

local q = {}
for i=0,255 do
  local dat = (i & 0x01) << 7
  dat = dat | (i & 0x02) >> 1 << 6
  dat = dat | (i & 0x04) >> 2 << 5
  dat = dat | (i & 0x08) >> 3 << 2
  dat = dat | (i & 0x10) >> 4 << 4
  dat = dat | (i & 0x20) >> 5 << 1
  dat = dat | (i & 0x40) >> 6 << 3
  dat = dat | (i & 0x80) >> 7
  q[i + 1] = 0x2800 | dat
end

function error(str)
  print("ERROR: " .. str)
  os.exit()
end

function resetPalette(data)
 for i=0,255 do
  if (i < 16) then
    if data == nil then
      pal[i] = (i * 15) << 16 | (i * 15) << 8 | (i * 15)
    else
      pal[i] = data[3][i]
      gpu.setPaletteColor(i, data[3][i])
    end
  else
    local j = i - 16
    local b = math.floor((j % 5) * 255 / 4.0)
    local g = math.floor((math.floor(j / 5.0) % 8) * 255 / 7.0)
    local r = math.floor((math.floor(j / 40.0) % 6) * 255 / 5.0)
    pal[i] = r << 16 | g << 8 | b
  end
 end
end

resetPalette(nil)

function r8(file)
  local byte = file:read(1)
  if byte == nil then
    return 0
  else
    return string.byte(byte) & 255
  end
end

function r16(file)
  local x = r8(file)
  return x | (r8(file) << 8)
end

function loadImage(filename)
  local data = {}
  local file = io.open(filename, 'rb')
  local hdr = {67,84,73,70}

  for i=1,4 do
    if r8(file) ~= hdr[i] then
      error("Invalid header!")
    end
  end

  local hdrVersion = r8(file)
  local platformVariant = r8(file)
  local platformId = r16(file)

  if hdrVersion > 1 then
    error("Unknown header version: " .. hdrVersion)
  end

  if platformId ~= 1 or platformVariant ~= 0 then
    error("Unsupported platform ID: " .. platformId .. ":" .. platformVariant)
  end

  data[1] = {}
  data[2] = {}
  data[3] = {}
  data[2][1] = r8(file)
  data[2][1] = (data[2][1] | (r8(file) << 8))
  data[2][2] = r8(file)
  data[2][2] = (data[2][2] | (r8(file) << 8))

  local pw = r8(file)
  local ph = r8(file)
  if not (pw == 2 and ph == 4) then
    error("Unsupported character width: " .. pw .. "x" .. ph)
  end

  data[2][3] = r8(file)
  if (data[2][3] ~= 4 and data[2][3] ~= 8) or data[2][3] > gpu.getDepth() then
    error("Unsupported bit depth: " .. data[2][3])
  end

  local ccEntrySize = r8(file)
  local customColors = r16(file)
  if customColors > 0 and ccEntrySize ~= 3 then
    error("Unsupported palette entry size: " .. ccEntrySize)
  end
  if customColors > 16 then
    error("Unsupported palette entry amount: " .. customColors)
  end

  for p=0,customColors-1 do
    local w = r16(file)
    data[3][p] = w | (r8(file) << 16)
  end

  local WIDTH = data[2][1]
  local HEIGHT = data[2][2]

  for y=0,HEIGHT-1 do
    for x=0,WIDTH-1 do
      local j = (y * WIDTH) + x + 1
      local w = r16(file)
      if data[2][3] > 4 then
        data[1][j] = w | (r8(file) << 16)
      else
        data[1][j] = w
      end
    end
  end

  io.close(file)
  return data
end

function gpuBG()
  local a, al = gpu.getBackground()
  if al then
    return gpu.getPaletteColor(a)
  else
    return a
  end
end
function gpuFG()
  local a, al = gpu.getForeground()
  if al then
    return gpu.getPaletteColor(a)
  else
    return a
  end
end

function drawImage(data, offx, offy)
  if offx == nil then offx = 0 end
  if offy == nil then offy = 0 end

  local WIDTH = data[2][1]
  local HEIGHT = data[2][2]

  resetPalette(data)

  local bg = 0
  local fg = 0
  local cw = 0

  for y=0,HEIGHT-1 do
    local str = ""
    for x=0,WIDTH-1 do
      local ind = (y * WIDTH) + x + 1
      local gBG = gpuBG()
      local gFG = gpuFG()
      if data[2][3] > 4 then
        bg = pal[data[1][ind] & 0xFF]
        fg = pal[(data[1][ind] >> 8) & 0xFF]
        cw = ((data[1][ind] >> 16) & 0xFF) + 1
      else
        fg = pal[data[1][ind] & 0x0F]
        bg = pal[(data[1][ind] >> 4) & 0x0F]
        cw = ((data[1][ind] >> 8) & 0xFF) + 1
      end
      if (gBG == fg) and (gFG == bg) then
        str = str .. unicode.char(q[cw ^ 255])
      elseif (gBG ~= bg) or (gFG ~= fg) then
        if #str > 0 then
          gpu.set(x + 1 + offx - unicode.wlen(str), y + 1 + offy, str)
        end
        if gBG ~= bg then
          gpu.setBackground(bg)
        end
        if gFG ~= fg then
          gpu.setForeground(fg)
        end
        str = unicode.char(q[cw])
      else
        str = str .. unicode.char(q[cw])
      end
    end
    if #str > 0 then
      gpu.set(WIDTH + 1 - unicode.wlen(str) + offx, y + 1 + offy, str)
    end
  end
end

resetGPU(160, 50)
local ticks = 80

local image = loadImage("zoomer.dat")

gpu.setViewport(1, 1)
drawImage(image, 80, 0)

local lastSleep = computer.uptime()
function sleep(time)
	local target = lastSleep + time
	while computer.uptime() < target do
		os.sleep(0.05)
	end
	lastSleep = target
end

local runListener
runListener = function()
  ticks = -100
  event.ignore("key_down", runListener)
end
event.listen("key_down", runListener)

gpu.setViewport(80, 25)
gpu.setBackground(0xFFFFFF, false)
local osx = 80
local osy = 25
gpu.copy(81,1,80,25,-80,0)
while ticks > -10 do
	local cx = math.sin(ticks / 91.2) * 300
	local cy = math.cos(ticks / 75.4) * 300
	local dx = 1
	local dy = 1
	local w = 45 + (math.sin(ticks / 30) * math.sin(ticks / 18.5) * 35)
	local h = w * 5 / 16
	local sx = 121 + cx - (w / 2)
	local sy = 25 + cy - (h / 2)
 sx = math.floor(sx)
 sy = math.floor(sy)
 w = math.floor(w)
 h = math.floor(h)
	while sx <= 80 do sx = sx + 80 end
	while sy <= 0 do sy = sy + 50 end
	while sx > 160 do sx = sx - 80 end
	while sy > 50 do sy = sy - 50 end
 local ow = w
 local oh = h
 w = 80
 h = 25
	gpu.copy(sx, sy, w, h, dx - sx, dy - sy)
	local wb = sx + w - 160
	local hb = sy + h - 50
	if sx + w > 160 then
		dx = 1 + w - wb
		dy = 1
		gpu.copy(81, sy, wb, h, dx - 81, dy - sy)
	end
	if sy + h > 50 then
		dx = 1
		dy = 1 + h - hb
		gpu.copy(sx, 1, w, hb, dx - sx, dy - 1)
		if sx + w > 160 then
			dx = 1 + w - wb
			gpu.copy(81, 1, wb, hb, dx - 81, dy - 1)
		end
	end
 w = ow
 h = oh
	if w ~= osx or h ~= osy then
		gpu.setViewport(w, h)
		osx = w
		osy = h
end
  	sleep(0.05)
	ticks = ticks + 1
end

gpu.setBackground(0, false)
gpu.fill(1, 1, 160, 50, " ")
resetGPU(160, 50)