-------------------------------------------- DEPENDENCIES --------------------------------------------

if not lUtils then
	local f = http.post("https://os.leveloper.cc/sGet.php","path="..textutils.urlEncode("LevelOS/startup/lUtils.lua").."&code="..textutils.urlEncode("lSlb8kZq"),{Cookie=userID}).readAll()
	load(f,"@lUtils.lua",nil,_ENV)
end

if not blittle then
	if not fs.exists("blittle") then
		shell.run("pastebin get ujchRSnU blittle")
	end
	os.loadAPI("blittle")
end

local Snake = require("snake")
local Collectible = require("collectible")
local World = require("world")
local Graphics = require("graphics")


-------------------------------------------- DEFINITIONS ---------------------------------------------

local SKINS = {
	RED = {colors.red,colors.orange},
	BLUE = {colors.blue,colors.cyan},
	GREEN = {colors.green,colors.lime},
	PURPLE = {colors.purple,colors.magenta},
	WHITE = {colors.lightGray,colors.white},
	GRAYSCALE = {colors.lightGray,colors.gray},
	GRAYSCALE2 = {colors.gray,colors.lightGray,colors.white,colors.lightGray},
	CHRISTMAS = {colors.green, colors.red},
	
	RAINBOW = {colors.red,colors.orange,colors.yellow,colors.lime,colors.lightBlue,colors.blue,colors.purple},
	FIRE = {colors.red,colors.orange,colors.yellow,colors.orange},
}
local skins = {}
for k,v in pairs(SKINS) do
	table.insert(skins,v)
end
local SECONDS_PER_FRAME = 0.1
if jit then
	SECONDS_PER_FRAME = 0.05
end

local math_atan2 = math.atan2
local math_floor = math.floor


------------------------------------------------ GAME ------------------------------------------------

if LevelOS then
	LevelOS.maximize()
	os.sleep(1)
end

local path = fs.getDir(shell.getRunningProgram())
local font = lUtils.asset.load(fs.combine(path,"assets/font.bimg"))
local fontW = #font[1][1][1]

local oterm = term.current()
local bWin = blittle.createWindow(oterm,1,1,term.getSize())

local world = World.new(16)

for c=1,world.size*10 do
	Collectible.new(world,math.random(1,world.width),math.random(1,world.height), 1, 1, colors.lightBlue)
end

local pSkin = math.random(1,#skins)
local playerData = {skin=skins[pSkin]}

local w,h = bWin.getSize()
local player = Snake.new(playerData, world, 0, math.floor(world.width/2)+1, math.floor(world.height/2)+1)

local bots = {}

_G.debugPlayer = player
_G.debugBots = bots

local camX,camY = 0,0
local tW,tH = bWin.getSize()
local oTime = os.epoch("utc")
local frameUpdate = os.startTimer(SECONDS_PER_FRAME)
local isCCPC = false

while true do
	local e = {os.pullEvent()}
	if e[1] == "timer" and e[2] == frameUpdate then
		if #bots < 10 then
			local sX,sY
			while true do
				sX,sY = math.random(1,world.width), math.random(1,world.height)
				-- check collision
				local continue = true
				for obj,_ in pairs(world.mobileObjects) do
					if obj:isInside(sX,sY,10) then
						continue = false
						break
					end
				end
				if continue then
					break
				end
			end
			table.insert(bots, Snake.new(nil, world, 0, sX, sY))
			bots[#bots]:update(0)
			bots[#bots].skin = skins[math.random(1,#skins)]
		end
		if not player.dead then
			world.zoom = (0.996^player.size*3.5+0.5)/318 * math.max(tW, tH)
		end
		local zoom = world.zoom or 1
		if lUtils.isHolding(keys.leftShift) then
			player.boosting = true
		elseif not isCCPC then
			player.boosting = false
		end
		local dt = os.epoch("utc")-oTime
		oTime = os.epoch("utc")
		frameUpdate = os.startTimer(SECONDS_PER_FRAME)
		if not player.dead then
			player:update(dt/1000)
		end
		for b=#bots,1,-1 do
			if bots[b].dead then
				table.remove(bots,b)
			else
				bots[b]:update(dt/1000)
			end
		end
		term.redirect(bWin)
		tW,tH = term.getSize()
		local pX,pY = player.segments[1].x, player.segments[1].y
		if player.dead then
			pX,pY = math_floor(world.width/2)+1, math_floor(world.height/2)+1
		end
		camX,camY = math_floor(math_floor(tW/2)+1-pX*zoom), math_floor(math_floor(tH/2)+1-pY*zoom)
		term.setBackgroundColor(colors.black)
		term.clear()
		term.setBackgroundColor(colors.gray)
		
		local reg,regX,regY = world:getRegion(pX,pY)
		
		local regX1,regY1, regX2,regY2 = math.max(regX-10,1), math.max(regY-10,1), math.min(regX+10,world.size), math.min(regY+10,world.size)
		
		local drawn = {}
		
		for regionX=regX1,regX2 do
			for regionY=regY1,regY2 do
				local v = world:indexRegion(regionX,regionY)
				if v then
					if regionY == 1 then
						term.setCursorPos(camX+((v.x+v.size)*zoom)-2, camY+(v.y*zoom))
						if regionX == world.size then
							term.write("   ")
						else
							term.write(string.rep(" ",5))
						end
						for y=(v.y*zoom)+1, (v.y*zoom)+2 do
							term.setCursorPos(camX+((v.x+v.size)*zoom), camY+y)
							term.write(" ")
						end
						if regionX == 1 then
							term.setCursorPos(camX+v.x*zoom, camY+v.y*zoom)
							term.write("   ")
							for y=(v.y*zoom)+1, (v.y*zoom)+2 do
								term.setCursorPos(camX+(v.x*zoom), camY+y)
								term.write(" ")
							end
						end
					end
					local yOff = 2
					if regionY == world.size then
						yOff = 0
					end
					for y=((v.y+v.size)*zoom)-2,((v.y+v.size)*zoom)+yOff do
						if y == (v.y+v.size)*zoom then
							term.setCursorPos(camX+((v.x+v.size)*zoom)-2,camY+y)
							local a = 5
							if regionX == world.size then
								a = 3
							end
							term.write(string.rep(" ",a))
							if regionX == 1 then
								term.setCursorPos(camX+v.x*zoom,camY+y)
								term.write("   ")
							end
						else
							term.setCursorPos(camX+(v.x+v.size)*zoom,camY+y)
							term.write(" ")
							if regionX == 1 then
								term.setCursorPos(camX+v.x*zoom,camY+y)
								term.write(" ")
							end
						end
					end
				end
			end
		end
		
		for regionX=regX1,regX2 do
			for regionY=regY1,regY2 do
				local v = world:indexRegion(regionX,regionY)
				if v then
					for obj,_ in pairs(v.objects) do
						if not drawn[obj] then
							obj:render(camX, camY)
							drawn[obj] = true
						end
					end
				end
			end
		end
		term.redirect(oterm)
		--[[if tW > 300 then
			term.setCursorPos(1,1)
			term.setBackgroundColor(colors.black)
			term.setTextColor(colors.white)
			print(dt.."ms")
			print("Zoom: "..zoom)
			print("Size: "..player.size)
			print(player.x..", "..player.y)
			print(player.segments[1].x..", "..player.segments[1].y)
			print("Rotation:         "..player.direction)
			print("Desired Rotation: "..player.desiredDirection)
			print("Screen size: "..tW..","..tH)
		end]]
		
		if player.dying or player.dead then
			local w,h = term.getSize()
			local txt = "YOU DIED"
			local x,y = math.floor(w/2)+1 - math.floor((#txt*(fontW+1))/2) + 1, math.floor(h/2) - 2
			term.setCursorPos(x,y)
			Graphics.fontWrite(font, txt)
			term.setCursorPos(1,y+3)
			term.setTextColor(colors.white)
			lUtils.centerText("Score: "..math.floor(player.size*2-20))
			term.setCursorPos(1,y+5)
			if player.dead then
				term.setTextColor(colors.white)
				lUtils.centerText("Press space to respawn.")
			else
				term.setTextColor(colors.lightGray)
				lUtils.centerText("Press space to respawn. ("..math.ceil((player.dying+4700)/1000)..")")
			end
		else
			local w,h = term.getSize()
			if w > 70 then
				local txt = "SCORE: "..math.floor(player.size*2-20)
				local x,y = math.floor(w/2)+1 - math.floor((#txt*(fontW+1))/2) + 1, 2
				term.setCursorPos(x,y)
				Graphics.fontWrite(font, txt)
			else
				term.setTextColor(colors.white)
				term.setCursorPos(1,2)
				lUtils.centerText("Score: "..math.floor(player.size*2-20))
			end
		end
		
	elseif e[1] == "key" then
		if e[2] == keys.d then
			player.desiredDirection = 0
		elseif e[2] == keys.a then
			player.desiredDirection = 180
		elseif e[2] == keys.w then
			player.desiredDirection = -90
		elseif e[2] == keys.s then
			player.desiredDirection = 90
		elseif e[2] == keys.rightBracket then
			pSkin = pSkin+1
			if pSkin > #skins then
				pSkin = 1
			end
			player.skin = skins[pSkin]
		elseif e[2] == keys.leftBracket then
			pSkin = pSkin-1
			if pSkin < 1 then
				pSkin = #skins
			end
			player.skin = skins[pSkin]
		elseif e[2] == keys.space and player.dead then
			playerData.skin = skins[pSkin]
			player = Snake.new(playerData, world, 0, math.floor(world.width/2)+1, math.floor(world.height/2)+1)
		end
		--player.direction = player.desiredDirection
	elseif e[1] == "mouse_drag" or e[1] == "mouse_click" or e[1] == "mouse_move" then
		local zoom = world.zoom or 1
		if e[1] == "mouse_move" then
			isCCPC = true
		end
		if e[1] == "mouse_drag" and isCCPC then
			player.boosting = true
		end
		--[[local mX,mY = math_floor((e[3]*2)/zoom+0.5),math_floor((e[4]*3)/zoom+0.5)
		player.desiredDirection = 360-math.deg(math.atan2(mX-player.x, mY-player.y))+90]]
		local mX,mY = e[3],e[4]
		local sW,sH = term.getSize()
		local cX,cY = math_floor(sW/2)+1, math_floor(sH/2)+1
		if e[2] == 1 then
			
			player.desiredDirection = 360-math.deg(math.atan2(mX-cX, mY-cY))+90
			--player.direction = player.desiredDirection
		elseif e[2] == 3 then
			mX, mY = mX/zoom-camX,mY/zoom-camY
			--table.insert(bots, Snake.new(nil, world, 0, math.floor(world.width/2)+1, math.floor(world.height/2)+1))
			local sX,sY
			while true do
				sX,sY = math.random(1,world.width), math.random(1,world.height)
				-- check collision
				if true then
					break
				end
			end
			table.insert(bots, Snake.new(nil, world, 0, sX, sY))
			bots[#bots]:update(0)
			bots[#bots].skin = skins[math.random(1,#skins)]
		end
	elseif e[1] == "mouse_up" and isCCPC then
		player.boosting = false
	elseif e[1] == "mouse_scroll" then
		if not world.zoom then world.zoom = 1 end
		world.zoom = math.max(0.5,world.zoom-e[2]*0.25)
	elseif e[1] == "term_resize" then
		local w,h = term.getSize()
		bWin.reposition(1,1,w, h)
		tW, tH = bWin.getSize()
	end
end