dofile("entity.lua")
dofile("score.lua")

gameovermenu = menupage.new ({{"Congratulations"},{"Please enter your sign"},
	{""},
	{"---",function (self) 
			insertscore(table.concat(self.nickname),self.score)
			finishxmasgame() end, 
		keylistener = function (self,key) gameovermenu:keylisten(key) end},
	{""},
	{"Timebonus"},{""},{""},{"Total highscore"},{""}})

gameovermenu.keypos = 0
function gameovermenu.updatescore(self,xmas)
	local tb = xmas.level.hitzones*5000-xmas.localt
	gameovermenu.nickname = {"-","-","-"}
	self.score = xmas.player.score+tb
	self:text (7,string.format("%.2f sec x 1000",math.max(tb/1000,0)))
	self:text (10,string.format("%i",math.max(self.score,0)))
	self:text (4,"---")
end
gameovermenu:addimage("hud/wman2.tga",100,100,140,280)
gameovermenu:deactivate()

do
	local rep,lastkey = 0
	function gameovermenu.keylisten(self,k)
		local nickname,nickpos = self.nickname,self.keypos
		rep = rep + 1 -- ugly, and stupid, but it works .... use keylistener instead :/
		-- just a quickfix
		if (rep <0 and lastkey == k) then return end
		rep = -25
		lastkey = k
		if (k==295) then 
			nickpos = math.mod(nickpos+2,3)
			nickname[nickpos+1] = "-"
		elseif (k>0 and k<256) then
			nickname[nickpos+1] = string.char(k)
			nickpos = math.mod(nickpos+1,3)
		end			
		self:text(4,table.concat(nickname))
		self.keypos = nickpos
	end
end

presentshadow = {layer = l3dset.get(0):layer(2)}
setmetatable(presentshadow,{__index = entity})
function presentshadow.new (caster,level)
	local self = entity.new("presentshadow",caster.actor:pos())
	setmetatable(self,{__index = presentshadow})
	
	self.l3d = self:addl3d("present_shadow.f3d")
	self.l3d:rfBlend(true)
	self.l3d:rsBlendmode(blendmode:decal())
	self.l3d:rfNodepthmask(true)
	self.l3d:rfNovertexcolor(true)
	self.caster = caster
	self.level = level

	return self
end

function presentshadow.think (self)
	if (self._remove) then return true end
	if (not self.caster.body:state()) then return end
	local x,y,z = self.caster:pos()
	local cnt,pos = self.level:test(x,y,z,0,0,-1,400)
	if (cnt>0) then
		local h = z
		z = pos[3]
		for i=0,cnt-1 do
			if (pos[i*3+3]>z) then z = pos[i*3+3] end
		end
		h = h-z
		self.l3d:color(1,1,1,math.max(0,1-h*0.01))
		self.l3d:renderscale(h*0.01+1,h*0.01+1,1)
		self:pos(x,y,z+h*0.1+0.5)
	end
end

present = {layer = l3dset.get(0):layer(3), trail = particlesys.load("present.prt")}
present.trail:drawlayer(present.layer)

setmetatable(present,{__index = entity})
function present.new (xmasobj,linkat,bone) 
	local self = entity.new("present")
	setmetatable(self,{__index = present})
		
	self.xmas = xmasobj
	local l3d = self:addl3d("present.f3d")
	l3d:rfLitSun(true)
	l3d:projectorflag(1,true)
	l3d:parent(linkat)
	l3d:parentbone(bone)
	self.l3d = l3d
	
	return self
end

function present.delete (self) 
	if (self.shadow) then self.shadow._remove=true end
end

function present.clinkout (self)
	local x,y,z = self.l3d:worldpos()
	local rx,ry,rz = self.l3d:worldrotdeg()
	local fx,fy,fscale = 0,0,0
	
	local cnt,pos,normal,depth,geoms = self.xmas.level:test(x,y,z,0,0,-1,500)
	for i=0,cnt-1 do 
		local g = raytester==geoms[i*2+1] and geoms[i*2+2] or geoms[i*2+1]
		local obj = self.xmas.level:gethitgeomowner(g)
		if (obj) then
			local box = obj[2].aabox
			local geom = obj[1]
--			print(box.min[1],box.min[2],box.min[3],box.max[1],box.max[2],box.max[3])
			tx,ty,tz = (box.min[1]+box.max[1])*0.5,(box.min[2]+box.max[2])*0.5,
				(box.min[3]+box.max[3])*0.5
			tx,ty,tz = g:local2world(tx,ty,0)
			fx,fy,fscale = tx-x,ty-y
			fscale = -(((fx*fx+fy*fy)^0.3)/(tz-z))*0.2
--			x,y,z = 
		end
	end
	local em = self:addemitter(self.trail,lset:layer(3))
	em:uselocal(true)
	em:rotationlock(1,true)
	em:rotationlock(2,true)
	self.actor:pos(x,y,z)
	self.actor:rotrad(rx,ry,rz)
	self.l3d:unlink()
	self.l3d:linkinterface(self.actor)
	self:addgeom(dgeombox.new(5,5,5),5)
	self:makebody()
	self.body:massbox(0.1,5,5,5)
	self.body:addforce(fx*fscale,fy*fscale,0)
	self.body:velocity(0,0,-0.1)
	--self.body:state(false)
	self.shadow = presentshadow.new(self,self.xmas.level)
end

do 
	local sndplaydelay = 0
	local drops = {wavsounds.drop01.snd, wavsounds.drop02.snd, wavsounds.drop03.snd}

	function present.collision (self,object)
		if (sndplaydelay>luxinia.time()) then return end
		local snd = math.floor((math.random()*table.getn(drops)))+1
		drops[snd]:play() 	
		sndplaydelay = luxinia.time() + 20
		
	end
end

function present.hit (self,owner)
	wavsounds.hohoho.snd:play()
	self.xmas:hit(self,owner)
	owner[3]:start(0)
	owner[4]:deactivate()
	owner.hashit = true
end

function present.think (self)
	if (not self.body) then 
		return 
	end
	if (not self.body:state()) then 
		self.deathtime = self.deathtime or self.age
		return self.age-self.deathtime > 500 -- return true in order to delete
	end
	self.deathtime = self.age
	if (self.body:jointcount()>0) then
		for i=0,self.body:jointcount()-1 do
			local j = self.body:joint(i)
			local g,g2 = j:geom1(),j:geom2()
			g = self:hasgeom(g) and g2 or g
			local owner = self.xmas.level:gethitgeomowner(g)
			if (owner and not self.hashit and not owner.hashit) then 
				self:hit (owner)
				self.hashit = true
				return true
			end
			local forces = {j:feedback()}
			local fx,fy,fz = forces[1]+forces[7],forces[2]+forces[8],forces[3]+forces[9]
			local f = fx*fx+fy*fy+fz*fz
			if (f>0.3) then 
				self:collision() 
				if (not self.hashit) then self.xmas:miss(self) self.hashit = true end
			end
		end
	end
end

followcamera = {}
setmetatable(followcamera,{__index = entity})
function followcamera.new (targetfn,targetargs,destinyfn,destinyargs,rx,ry,rz)
	local self = entity.new("followcamera")
	self.targetfn,self.targetargs = targetfn,targetargs
	self.destinyfn,self.destinyargs = destinyfn,destinyargs
	self.camera = l3dcamera.default()
	self.camera:linkinterface(self.actor)
	self.speed = 0.01
	self.rx,self.ry,self.rz = rx,ry,rz
	self.camera:fov(60)
	self.actor:pos(self.targetfn(unpack(targetargs)))
	setmetatable(self,{__index = followcamera})
	return self
end

function followcamera.think (self)
	local x,y,z = self.actor:pos()
	local tx,ty,tz = self.targetfn(unpack(self.targetargs))
	local dx,dy,dz = tx-x,ty-y,tz-z
	x,y,z = x+dx*self.speed,y+dy*self.speed,z+dz*self.speed
	self.actor:pos(x,y,z)
	
	local tx,ty,tz = self.destinyfn(unpack(self.destinyargs))
	local dx,dy,dz = -tx+x,-ty+y,-tz+z
	local ang = math.atan2(dy,dx)
	local el = math.atan2((dy*dy+dx*dx)^0.5,dz)
	self.actor:rotrad(el-math.pi*0.5+self.rx,0+self.ry,ang+math.pi*0.5+self.rz)	
end

sled = {score = 0, hits = 0, misses = 0, rowhits = 0, maxrowhits = 0, 
	ropetex = texture.load("rope.tga"), bonustime = 3000, trail = particlesys.load("trail.prt")}
	
--sled.trail:drawlayer(lset:layer(3))

setmetatable(sled,{__index = entity})

function sled.new (xmasobj, x,y,z)
	local self = entity.new("sled",x,y,z)
	local lring,rring = dgeombox.new(2,2,2),dgeombox.new(2,2,2)
	x,y,z = x or 0, y or 0, z or 0
	setmetatable(self,{__index = sled})	
	
	--self:addemitter(self.psys,l3dset.getnormal():layer3(),{0,0,150,-90,0,0})
	
	self.xmas = xmasobj
	self:addl3d("sled.f3d",0,0,0)
	self:addgeom(dgeombox.new(10,30,12),1)

	self:addgeom(dgeomtransform.new(),1,lring,-6.4,17,10)
	self:addgeom(dgeomtransform.new(),1,rring, 6.4,17,10)

	self:makebody()
	self.body:massbox(0.1,20,40,5)
	self.body:lock(3,200)
	
	self.body:autodisable(false)
	
	local tex = texture.load("schlitten_shadow.tga")
	self.projector = l3dprojector.new("sledshadow",layer)
	self.projector:linkinterface(self.actor)
	self.projector:uselocal(true)
	self.projector:textureid(tex)
	self.projector:activate(1)
	self.projector:fov(-100)
	self.projector:blend(blendmode:modulate())
	self.projector:frontplane(0)
	self.projector:rotationlock(0,true)
	self.projector:rotationlock(1,true)
	self.projector:backplane(300)
	self.projector:localrotdeg(-90,0,0)
	
	local em
	for i=-14,16,1.5 do
		em = self:addemitter(self.trail,lset:layer(topviewoffset),{9,i,0})
		em:uselocal(true)
		em:localrotdeg(180,0,0)
		em = self:addemitter(self.trail,lset:layer(topviewoffset),{-9,i,0})
		em:uselocal(true)
		em:localrotdeg(180,0,0)
	end
	self.wmannl3d = self:addl3d("wmannlowres.f3d",nil,nil,nil,true)
	self.anims = {
		animation.load("wmannthrow1.ma",true,false),
		animation.load("wmannthrow2.ma",true,false),
		animation.load("wmannthrow3.ma",true,false),
	}
	
	self.wmannl3d:seqAnimsource(0,0,self.anims[1])
	self.wmannl3d:seqAnimstart(0,0,0)
	self.wmannl3d:seqAnimlength(0,0,self.anims[1]:length())
	self.wmannl3d:seqOffset(0,0)
	self.wmannl3d:seqTime(0,1)
	self.wmannl3d:seqLoop(0,false)
	self.wmannl3d:seqState(0,3)
	
	self.rudianim = animation.load("reindeerrun2.ma",true,false)
	self.rudil3d = self:addl3d("reindeeranim.f3d",0,40,0,true)
	self.rudil3d:seqAnimsource(0,0,self.rudianim)
	self.rudil3d:seqAnimstart(0,0,0)
	self.rudil3d:seqAnimlength(0,0,self.rudianim:length())
	self.rudil3d:seqOffset(0,0)
	self.rudil3d:seqLoop(0,true)
	self.rudil3d:seqState(0,1)

	local ac1 = self:getnewactor() 
	ac1:linkbone(self.rudil3d,"Bip01 R Clavicle",
		matrix4x4.new({1,0,0,0,  0,1,0,0, 0,0,1,0, 0,3,0,1}))
	local ac2 = self:getnewactor() 
	ac2:linkbone(self.rudil3d,"Bip01 L Clavicle",
		matrix4x4.new({1,0,0,0,  0,1,0,0, 0,0,1,0, 0,3,0,1}))
	local acm = self:getnewactor()
	acm:linkbone(self.rudil3d,"Bip01 Neck",
		matrix4x4.new({1,0,0,0,  0,1,0,0, 0,0,1,0, 4,3,0,1}))
		
	local r1 = self:addl3d(l3dtrail.new("rope1",self.layer,5))
	r1:rfFog(true)
	r1:typeworldref(true)
	r1:facecamera(true)
	r1:matsurface(self.ropetex)
	r1:materialscale(60,1)
	local p1 = self:getnewactor() p1:link(rring)
	local p2 = self:getnewactor() p2:link(lring)
	
	r1:spawnworldref(p1,2)
	r1:spawnworldref(ac1,2)
	r1:spawnworldref(acm,2)
	r1:spawnworldref(ac2,2)
	r1:spawnworldref(p2,2)
	r1:materialnormalized(true)
	
	--[[local xyz = {
			{5,-10,10, 10,30,20},
			{5,-15,10, 34,20,50},
			{-5,-15,10, 40,20,50},
		}
	for i,v in pairs(xyz) do 
		local rx,ry,rz = v[4] or 0,v[5] or 0,v[6] or 0
		self:addl3d("present.f3d",unpack(v)):localrotdeg(rx,ry,rz)
	end]]
	
	local function r (w) return (math.random()-0.5)*w end
	
	for i=1,20 do
		local x,y,z, rx,ry,rz = r(13),r(10)-8,15+r(10), r(180),r(180),r(180)
		local l3d = self:addl3d("present.f3d",x,y,z)
		l3d:localrotdeg(rx,ry,rz)
		l3d:renderscale(1+r(0.5),1+r(0.5),1+r(0.5))
		l3d:color(0.8+r(0.2),0.8+r(0.2),0.8+r(0.2),1)
		l3d:rfNovertexcolor(true)
	end
	
	return self
end

function sled.hit (self,present,object)
	self.nexthittime = self.nexthittime or 0
	self.score = self.score + 50 + 50*self.rowhits + math.floor(self.rowhits/9) * 150 + math.max(0,(self.nexthittime-self.xmas.localt)*0.1)
	self.hits = self.hits + 1
	self.nexthittime = self.xmas.localt+self.bonustime
	self.rowhits = self.rowhits + 1
	self.maxrowhits = math.max(self.maxrowhits,self.rowhits)
end

function sled.miss (self,present)
	self.misses = self.misses + 1
	self.rowhits = 0
	self.lasthit = 0
end

function sled.randanim (self) 
	local r = math.floor(math.random()*table.getn(self.anims)*0.99999)+1
	self.wmannl3d:seqAnimsource(0,0,self.anims[r])
	self.wmannl3d:seqAnimstart(0,0,0)
	self.wmannl3d:seqAnimlength(0,0,self.anims[r]:length())
	self.wmannl3d:seqOffset(0,0)
	self.wmannl3d:seqTime(0,0)
	self.wmannl3d:seqState(0,1)
end

function sled.think (self)
	local fx,fy,fz =(Keyboard.isKeyDown(Keyboard.keycode.LEFT) and -1 or 0) + (Keyboard.isKeyDown(Keyboard.keycode.RIGHT) and 1 or 0),
		 			(Keyboard.isKeyDown(Keyboard.keycode.UP)	and 0.5 or 0) + (Keyboard.isKeyDown(Keyboard.keycode.DOWN) and -0.5 or 0)*0.75+0.5,
		 			0
	local drop = Keyboard.isKeyDown(Keyboard.keycode.LCTRL) or Keyboard.isKeyDown(Keyboard.keycode.RCTRL) 
	if (self.finish) then
		fx = 0.7
		fy = 2
		fz =-4.5
		drop = false
		self.body:lock(0,0)
	end
	local ax,ay,az = self.body:rotrad()
	
	self.body:addforce(0,0,10+fz,0,0,10,true) -- stabilizing	
	self.body:addreltorque(0,0,-fx*20)
	self.body:addrelforce(0,fy*2,0,0,10,0,true)
	local vx,vy,vz = self.body:velocity()
	self.body:velocity(vx*0.994,vy*0.994,vz*0.994)
	local v = (vx*vx+vy*vy+vz*vz)^0.5
	
	vx,vy,vz = self.body:angvel()
	self.body:addreltorque(0,-vz*2000,0)
	self.body:angvel(vx*0.94,vy*0.94,vz*0.94)
	
	self.rudil3d:localrotdeg(math.abs(vz*2000),-vz*1000,vz*3000)
	self.rudil3d:localpos(-vz*800,40,0)
	self.rudil3d:seqSpeed(0,0.4+v)
		
	if (drop and not self.present and (self.wmannl3d:seqState(0)==2 or
		self.wmannl3d:seqState(0)==3)) then
		self.present = present.new(self.xmas,self.wmannl3d,"giftBone")
		self:randanim()
	end

	if (self.present and self.wmannl3d:seqTime(0)>900) then
		self.present:clinkout()
		self.present = nil
	end
end

xmas = { space = dspacehash.new() }	
function xmas.new (lvl)
	local self = {t = 0,localt = 0}
	setmetatable(self,{__index=xmas})
	
	self.level = lvl or levelmap.load("test.lvl.lua")
	xmas.space:add(self.level.space)
	xmas.space:add(entity.space)
	self.player = sled.new(self, 0,0,100)
	self.camera = followcamera.new(
		function (a,b) 
			local x,y,z = self.player.body:local2world(0,-180,300)
			local a,b,c = self.player.body:pos()
			return x,y,c+300
		end,{self.camera,self.player},
		self.player.body.local2world,{self.player.body,0,0,0},0.1,0,0)

	Keyboard.setKeyBinding (string.byte('P'),function () self:pause(not self._pause)  end)
	--Keyboard.setKeyBinding  (string.byte('0'),function () self:step()  end)

	gamehud.setcurposfn(function () 
		local x,y = self.player.actor:pos()
		local _,_,rot = self.player.actor:rotrad()
		return x,y,-rot end)
	gamehud.settimer(function()
		return math.max(0,self.level.hitzones*5000-self.localt),
			math.max(0,(self.player.nexthittime or 0)-self.localt),self.player.bonustime
	end)
	gamehud.setscorefn(function()
		return self.player.score
	end)
	
	return self
end

function xmas:delete()
	if (self._del) then return end
	self._del = true
	self.level:cleanup()
	self.camera:delete()
	self.player:delete()
	self._pause = true
	xmas.space:delete()
	xmas.space = dspacehash.new()
	gamehud.setcurposfn()
	gamehud.settimer()
	gamehud.setscorefn()
end

function xmas.pause(self,p)
	self._pause = p
	render.pauseparticles(p)
	entity.pauseall(p)
end

function xmas.step(self)
	self._step = true
	self:pause(false) 
end

function xmas.updatescore (self)
--	gamehud.updatescore(self)
end

function xmas.hit (self,present,object)
	self.player:hit(self,present,object)
	self:updatescore()
	gamehud.hit(self)
end

function xmas.miss (self,present)
	self.player:miss(self,present)
	self:updatescore()
	gamehud.miss(self)
end

function xmas.think(self)
	if (self._pause) then
		self.t = luxinia.time()
		return
	end
	local cnt = 0
	while (self.t<luxinia.time() and cnt<10) do
		entity.thinkall()

		dworld.collidetest(xmas.space)
		dworld.makecontacts()	
		dworld.quickstep(1)

		self.t = self.t + 5
		self.localt = self.localt + 5
		cnt = cnt +1
		if (self._step) then break end
	end
	if (cnt==10) then self.t = luxinia.time() end
	if (self._step) then
		self_step = false
		self:pause(true)
	end
	
end

function createxmasgame (lvl) 
--	_print("creating xmasgame")
	local x = xmas.new()
--	_print("created")
	xgame = x
	normalview.rClear:color(false)
	gamehud.visible(true)
	finishgame = nil
--	x.player.hits = x.level.hitzones-1
	particlesys.load("chimney.prt"):clearparticles()
	particlesys.load("chimneyhappy.prt"):clearparticles()
	particlesys.load("laternenfall.prt"):clearparticles()
	function xmasthink () 
		x:think() 

		if ((finishgame or x.player.hits == x.level.hitzones) and not x.dietime) then
			finishgame = nil
			x.dietime = x.t + 15000
			x.player.finish = true
			x.camera.targetfn = function(self) return x.camera.actor:pos() end
			gamehud.clear()
			gamehud.visible(false)
			if (forcescore or	x.player.hits == x.level.hitzones) then
				gameovermenu:updatescore(x)
				gameovermenu:activate()
				gameovermenu:up()
			else
				finishxmasgame()
			end
		end
		if (x.dietime and x.dietime<x.t) then

		end
	end
	
	Timer.set("xmasthink",xmasthink)
end

function finishxmasgame ()
	if (not xgame) then return end

	xgame:delete()
	xgame = nil

	Timer.remove("xmas")
	normalview.rClear:color(true)
	
	particlesys.load("chimney.prt"):clearparticles()
	particlesys.load("chimneyhappy.prt"):clearparticles()
	particlesys.load("laternenfall.prt"):clearparticles()

	entity.removeall()
	initmenubackground ()
	scoremenu:activate()
	render.pauseparticles(false)
end
