--[[
Copyright (c) 2006-2010 LOVE Development Team

This software is provided 'as-is', without any express or implied
warranty.  In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must not
   claim that you wrote the original software. If you use this software
   in a product, an acknowledgment in the product documentation would be
   appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
   misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
--]]

-- Make sure love table exists.
if not love then love = {} end

-- Used for setup:
love.path = {}
love.arg = {}

-- Unparsed arguments:
argv = {}

-- Replace any \ with /.
function love.path.normalslashes(p)
	return string.gsub(p, "\\", "/")
end

-- Makes sure there is a slash at the end
-- of a path.
function love.path.endslash(p)
	if string.sub(p, string.len(p)-1) ~= "/" then
		return p .. "/"
	else
		return p
	end
end

-- Checks whether a path is absolute or not.
function love.path.abs(p)

	local tmp = love.path.normalslashes(p)

	-- Path is absolute if it starts with a "/".
	if string.find(tmp, "/") == 1 then
		return true
	end

	-- Path is absolute if it starts with a
	-- letter followed by a colon.
	if string.find(tmp, "%a:") == 1 then
		return true
	end

	-- Relative.
	return false

end

-- Converts any path into a full path.
function love.path.getfull(p)

	if love.path.abs(p) then
		return love.path.normalslashes(p)
	end

	local cwd = love.filesystem.getWorkingDirectory()
	cwd = love.path.normalslashes(cwd)
	cwd = love.path.endslash(cwd)

	-- Construct a full path.
	return cwd .. love.path.normalslashes(p)

end

-- Returns the leaf of a full path.
function love.path.leaf(p)
	local a = 1
	local last = p

	while a do
		a = string.find(p, "/", a+1)

		if a then
			last = string.sub(p, a+1)
		end
	end

	return last
end

-- Finds the key in the table with the lowest integral index. The lowest
-- will typically the executable, for instance "lua5.1.exe".
function love.arg.getLow(a)
	local m = math.huge
	for k,v in pairs(a) do
		if k < m then
			m = k
		end
	end
	return a[m]
end

love.arg.options = {
	console = { a = 0 },
	game = { a = 1 }
}

function love.arg.parse_option(m, i)
	m.set = true

	if m.a > 0 then
		m.arg = {}
		for j=i,i+m.a-1 do
			table.insert(m.arg, arg[j])
			i = j
		end
	end

	return i
end

function love.arg.parse_options()

	local game
	local argc = #arg

	for i=1,argc do
		-- Look for options.
		local s, e, m = string.find(arg[i], "%-%-(.+)")

		if m and love.arg.options[m] then
			i = love.arg.parse_option(love.arg.options[m], i+1)
		elseif not game then
			game = i
		end
	end

	if not love.arg.options.game.set then
		love.arg.parse_option(love.arg.options.game, game or 0)
	end
end

function love.createhandlers()

	-- Standard callback handlers.
	love.handlers = {
		kp = function (b, u)
			if love.keypressed then love.keypressed(b, u) end
		end,
		kr = function (b)
			if love.keyreleased then love.keyreleased(b) end
		end,
		mp = function (x,y,b)
			if love.mousepressed then love.mousepressed(x,y,b) end
		end,
		mr = function (x,y,b)
			if love.mousereleased then love.mousereleased(x,y,b) end
		end,
		jp = function (j,b)
			if love.joystickpressed then love.joystickpressed(j,b) end
		end,
		jr = function (j,b)
			if love.joystickreleased then love.joystickreleased(j,b) end
		end,
		q = function ()
			return
		end,
	}

end

-- This can't be overriden.
function love.boot()

	-- This is absolutely needed.
	require("love")
	require("love.filesystem")

	love.arg.parse_options()

	local o = love.arg.options

	local abs_arg0 = love.path.getfull(love.arg.getLow(arg))
	love.filesystem.init(abs_arg0)

	-- Is this one of those fancy "fused" games?
	local can_has_game = pcall(love.filesystem.setSource, abs_arg0)

	if not can_has_game and o.game.set and o.game.arg[1] then
		local full_source =  love.path.getfull(o.game.arg[1])
		local leaf = love.path.leaf(full_source)
		love.filesystem.setIdentity(leaf)
		can_has_game = pcall(love.filesystem.setSource, full_source)
	end

	if can_has_game and not (love.filesystem.exists("main.lua") or love.filesystem.exists("conf.lua")) then
		no_game_code = true
	end

	if not can_has_game then
		love.filesystem = nil
		love.nogame()
	end

end

function love.init()

	-- Create default configuration settings.
	local c = {
		title = "Untitled",
		author = "Unnamed",
		version = 0,
		screen = {
			width = 800,
			height = 600,
			fullscreen = false,
			vsync = true,
			fsaa = 0,
		},
		modules = {
			event = true,
			keyboard = true,
			mouse = true,
			timer = true,
			joystick = true,
			image = true,
			graphics = true,
			audio = true,
			physics = true,
			sound = true,
			font = true,
			thread = true,
		},
		console = false, -- Only relevant for windows.
	}

	-- If config file exists, load it and allow it to update config table.
	if not love.conf and love.filesystem and love.filesystem.exists("conf.lua") then
		require("conf.lua")
	end

	-- Yes, conf.lua might not exist, but there are other ways of making
	-- love.conf appear, so we should check for it anyway.
	if love.conf then
		local ok, err = pcall(love.conf, c)
		if not ok then
			print(err)
			-- continue
		end
	end

	if love.arg.options.console.set then
		c.console = true
	end

	-- Gets desired modules.
	for k,v in pairs(c.modules) do
		if v then
			require("love." .. k)
		end
	end

	if love.event then
		love.createhandlers()
	end

	-- Setup screen here.
	if c.screen and c.modules.graphics then
		if love.graphics.checkMode(c.screen.width, c.screen.height, c.screen.fullscreen) then
			assert(love.graphics.setMode(c.screen.width, c.screen.height, c.screen.fullscreen, c.screen.vsync, c.screen.fsaa), "Could not set screen mode")
		end
		love.graphics.setCaption(c.title)
	end

	if love.filesystem and love.filesystem.exists("main.lua") then require("main.lua") end

	if no_game_code then
		error("No code to run\nYour game might be packaged incorrectly\nMake sure main.lua is at the top level of the zip")
	end

	-- Console hack
	if c.console and love._openConsole then
		love._openConsole()
	end

end

function love.run()

	if love.load then love.load(arg) end

	local dt = 0

	-- Main loop time.
	while true do
		if love.timer then
			love.timer.step()
			dt = love.timer.getDelta()
		end
		if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
		if love.graphics then
			love.graphics.clear()
			if love.draw then love.draw() end
		end

		-- Process events.
		if love.event then
			for e,a,b,c in love.event.poll() do
				if e == "q" then
					if love.audio then
						love.audio.stop()
					end
					return
				end
				love.handlers[e](a,b,c)
			end
		end

		if love.timer then love.timer.sleep(1) end
		if love.graphics then love.graphics.present() end

	end

end

-----------------------------------------------------------
-- Default screen.
-----------------------------------------------------------

function love.nogame()

	love.load = function()

		major, minor, rev = string.match(love._version_string, "(%d)%.(%d)%.(%d)")
		if not major then major = 0 end
		if not minor then minor = 0 end
		if not rev then rev = 0 end

		love.graphics.setBackgroundColor(0x84, 0xca, 0xff)

		names = {
			"wheel_0",
			"wheel_1",
			"wheel_2",
			"wheel_3",
			"wheel_4",
			"wheel_5",
			"wheel_6",
			"wheel_7",
			"wheel_8",
			"wheel_9",
			"belt_tooth",
			"belt_track",
			"turret_body",
			"turret_cannon",
			"star",
			"knoll01",
			"knoll02",
			"knoll03",
			"knoll04",
			"tree01",
			"bubble",
			"love",
		}

		local decode = function(file)
			return love.graphics.newImage(love.image.newImageData(file))
		end

		images = {}

		for i,v in pairs(names) do
			images[v] = decode(love["_"..v.."_png"])
		end

		pools = {
			{
				images.knoll01,
				images.knoll02,
			},
			{
				images.knoll03,
				images.knoll04,
			},
		}

		List = {}
		List.__index = List

		List.new = function(self)
			local o = {
				head = nil,
			}
			setmetatable(o, List)
			return o
		end

		List.update = function(self, dt)
			local n = self.head
			while n do
				n:update(dt)
				n = n.next
			end
		end

		List.draw = function(self)
			local n = self.head
			while n do
				n:draw()
				n = n.next
			end
		end

		Node = {}
		Node.__index = Node

		Node.new = function(self, object)
			local o = {
				next = nil,
			}
			setmetatable(o, List)
			return o
		end

		Node.insert = function(self, list)
			local h = list.head
			list.head = self
			self.next = h
		end

		Node.remove = function(self)
			parent.next = self.next
		end

		Object = Node:new()
		Object.__index = Object
		setmetatable(Object, Node)

		Object.new = function(self)
			local o = {
				image = nil,
				x = 0,
				y = 0,
				dx = -400,
				dy = 0,
				scale = 1,
				r = 0,
				duration = 30,
				passed = 0,
				t = 0,
				alpha = 255
			}
			setmetatable(o, Object)
			return o
		end

		Object.update = function(self, dt)
			self.passed = self.passed + dt
			while self.passed > self.duration do
				self.passed = self.passed - self.duration
			end
			self.t = self.passed/self.duration
		end

		Object.draw = function(self)
			if self.image then
				local x = self.x + self.dx*self.t
				local y = self.y + self.dy*self.t
				local r = self.r*self.t
				love.graphics.setColor(255, 255, 255, self.alpha)
				love.graphics.draw(self.image, x, y, r, self.scale)
				love.graphics.setColor(255, 255, 255, 255)
			end
		end

		Tree = Object:new()
		Tree.__index = Tree
		setmetatable(Tree, Object)

		Tree.new = function(self)
			local o = {}
			o.image = images.tree01
			o.x = 800 + math.random(0, 800)
			o.y = 300 + math.random(0, 40)
			o.xt = -200;
			o.dx = o.xt - o.x
			o.speed = 100
			o.duration = -o.dx/o.speed
			setmetatable(o, Tree)
			return o
		end

		Star = Object:new()
		Star.__index = Star
		setmetatable(Star, Object)

		Star.new = function(self, speed, scale)
			local o = {}
			o.image = images.star
			o.x = 800 + math.random(0, 800)
			o.y = -200 + math.random(0, 300)
			o.xt = -50;
			o.dy = 400
			o.dx = o.xt - o.x
			o.speed = speed
			o.scale = scale
			o.duration = -o.dx/o.speed
			o.r = math.pi * 5
			o.alpha = 100 + math.random(155)
			setmetatable(o, Star)
			return o
		end

		Knoll = Object:new()
		Knoll.__index = Knoll
		setmetatable(Knoll, Object)

		Knoll.new = function(self, pool, var, speed)
			local o = {}
			o.image = pools[pool][math.random(1, #pools[pool])]
			o.x = 800 + math.random(0, 800)
			o.y = 300 + var - math.random(0, var*2)
			o.xt = -200;
			o.dx = o.xt - o.x
			o.speed = speed
			o.duration = -o.dx/o.speed
			setmetatable(o, Star)
			return o
		end

		Belt = Object:new()
		Belt.__index = Belt
		setmetatable(Belt, Object)

		Belt.new = function(self, n)

			local o = {}

			o.r = 30
			o.d = o.r*2
			o.half_c = math.pi*o.r
			o.c = 2*o.half_c
			o.x = 200
			o.y = 300
			o.th = 1
			o.ta = 1
			o.w = o.th*o.half_c
			o.total = o.th*2+o.ta*2
			o.teeth = {}

			for i=0,n-1 do
				local b = { x = 0, y = 0, t = (o.total/n)*i }
				table.insert(o.teeth, b)
			end

			setmetatable(o, Belt)
			return o
		end

		Belt.update = function(self, dt)
			for i,b in ipairs(self.teeth) do
				b.t = b.t + dt

				if b.t < self.th then
					local t = b.t
					b.x = self.x + self.w * (t/self.th)
					b.y = self.y
				elseif b.t < self.th + self.ta then
					local t = (self.th + self.ta - b.t)
					b.x = self.x + self.w + math.cos(-math.pi*t + math.pi/2)*self.r
					b.y = self.y + self.r + math.sin(-math.pi*t + math.pi/2)*self.r
				elseif b.t < self.th*2 + self.ta then
					local t = (b.t - self.th*2 + self.ta)/self.th
					b.x = self.x + self.w * (2-t)
					b.y = self.y + self.d
				elseif b.t < self.total then
					local t = (self.th*2 + self.ta - b.t)
					b.x = self.x + math.cos(-math.pi*t + math.pi/2)*self.r
					b.y = self.y + self.r + math.sin(-math.pi*t + math.pi/2)*self.r
				else
					b.t = b.t - self.total
				end
			end
		end

		Belt.draw = function(self)
			for i,b in ipairs(self.teeth) do
				love.graphics.draw(images.belt_tooth, b.x, b.y)
			end
		end

		Tank = Object:new()
		Tank.__index = Tank
		setmetatable(Tank, Object)

		Tank.new = function(self)
			local o = {}
			o.x = 200
			o.y = 490
			o.i = 49
			o.belt = Belt:new(30)
			o.belt.x = o.x-7
			o.belt.y = o.y-37
			o.angle = 0
			setmetatable(o, Tank)
			return o
		end

		Tank.update = function(self, dt)
			self.angle = self.angle + dt * math.pi/2
			self.belt:update(dt)
		end


		Tank.draw = function(self)
			love.graphics.draw(images.turret_cannon, self.x+30, self.y-80)
			love.graphics.draw(images.turret_body, self.x-12, self.y-110)
			love.graphics.draw(images.belt_track, self.belt.x-74, self.belt.y-28)
			love.graphics.draw(images["wheel_"..tostring(major)], self.x, self.y, self.angle, 1, 1, 32, 32)
			love.graphics.draw(images["wheel_"..tostring(minor)], self.x+self.i, self.y, self.angle, 1, 1, 32, 32)
			love.graphics.draw(images["wheel_"..tostring(rev)], self.x+self.i*2, self.y, self.angle, 1, 1, 32, 32)
			self.belt:draw()
		end

		Bubble = Object:new()
		Bubble.__index = Bubble
		setmetatable(Bubble, Object)

		 Bubble.new = function(self)
			local o = {}
			o.x = 240
			o.y = 190
			o.angle = 0
			setmetatable(o, Bubble)
			return o
		end

		Bubble.update = function(self, dt)
			self.angle = self.angle + dt*5
		end

		Bubble.draw = function(self)
			local yv = math.sin(self.angle)*5
			love.graphics.draw(images.bubble, self.x, self.y+yv)
			love.graphics.draw(images.love, self.x+8, self.y+yv+95)
		end

		timers = {}

		Timer = {}
		Timer.__index = Timer

		Timer.spawn = function(self, tick, f)
			local o = {
				passed = 0,
				tick = tick,
				f = f
			}
			setmetatable(o, Timer)
			table.insert(timers, o)
		end

		Timer.update = function(self, dt)
			self.passed = self.passed + dt
			while self.passed > self.tick do
				self.passed = self.passed - self.tick
				self.f()
			end
		end

		lists = {
			b = List:new(),
			f = List:new()
		}

		do
			local t = Bubble:new()
			t:insert(lists.f)
		end

		do
			local t = Tank:new()
			t:insert(lists.f)
		end

		for i=1,3 do
			local t = Tree:new(50, 300)
			t:insert(lists.b)
		end


		for i=1,2 do
			local t = Knoll:new(1, 50, 100)
			t:insert(lists.b)
		end

		for i=1,40 do
			local t = Star:new(100, 1)
			t:insert(lists.b)
		end

		for i=1,5 do
			local t = Knoll:new(2, 100, 50)
			t:insert(lists.b)
		end

		for i,v in pairs(lists) do
			v:update(10)
		end

	end



	love.update = function(dt)

		for i,v in ipairs(timers) do v:update(dt) end


		for i,v in pairs(lists) do
			v:update(dt)
		end

		love.timer.sleep(10)
	end


	love.draw = function()

		lists.b:draw()

		-- Ground
		love.graphics.setColor(146, 201, 87)
		love.graphics.rectangle("fill", 0, 530, 800, 70)
		love.graphics.setColor(205, 227, 161)
		love.graphics.rectangle("fill", 0, 520, 800, 10)
		love.graphics.setColor(255, 255, 255)

		lists.f:draw()

	end

	love.conf = function(t)
		t.title = "*Tank* you for using LOVE " .. love._version_string .. " (" .. love._version_codename .. ")"
		t.modules.audio = false
		t.modules.sound = false
		t.modules.physics = false
		t.modules.joystick = false
		t.modules.native = false
		t.modules.font = false
	end

end

-----------------------------------------------------------
-- Error screen.
-----------------------------------------------------------

local debug = debug

local function error_printer(msg, layer)
	print((debug.traceback("Error: " .. msg, 1+(layer or 1)):gsub("\n[^\n]+$", "")))
end

function love.errhand(msg)

	error_printer(msg, 2)

	if not love.graphics or not love.event or not love.graphics.isCreated() then
		return
	end

	-- Load.
	love.graphics.setScissor()
	love.graphics.setBackgroundColor(89, 157, 220)
	local font = love.graphics.newFont(love._vera_ttf, 14)
	love.graphics.setFont(font)

	love.graphics.setColor(255, 255, 255, 255)

	local trace = debug.traceback()

	love.graphics.clear()

	local err = {}

	table.insert(err, "Error\n")
	table.insert(err, msg.."\n\n")

	for l in string.gmatch(trace, "(.-)\n") do
		if not string.match(l, "boot.lua") then
			l = string.gsub(l, "stack traceback:", "Traceback\n")
			table.insert(err, l)
		end
	end

	local p = table.concat(err, "\n")

	p = string.gsub(p, "\t", "")
	p = string.gsub(p, "%[string \"(.-)\"%]", "%1")

	local function draw()
		love.graphics.clear()
		love.graphics.printf(p, 70, 70, love.graphics.getWidth() - 70)
		love.graphics.present()
	end

	draw()

	while true do
		e, a, b, c = love.event.wait()

		if e == "q" then
			return
		end
		if e == "kp" and a == "escape" then
			return
		end

		draw()

	end

end


-----------------------------------------------------------
-- The root of all calls.
-----------------------------------------------------------

result = xpcall(love.boot, error_printer)
if not result then return end
result = xpcall(love.init, love.errhand)
if not result then return end
result = xpcall(love.run, love.errhand)
if not result then return end
