--[[
    This file is part of Ice Lua Components.

    Ice Lua Components is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Ice Lua Components is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Ice Lua Components.  If not, see <http://www.gnu.org/licenses/>.
]]

if client then
	mdl_nade = model_load({
		kv6={bdir=DIR_PKG_KV6, name="nade.kv6", scale=6.0/128.0},
		pmf={bdir=DIR_PKG_PMF, name="nade.pmf"},
	}, {"kv6","pmf"})
	mdl_nade_inst = mdl_nade {}
end

function nade_add(nade)
	nades.tail = nades.tail + 1
	nades[nades.tail] = nade
end

function nade_prune(sec_current)
	local i
	for i=nades.head,nades.tail do
		if nades[i] and nades[i].dead then
			nades[i] = nil
			if i == nades.head then
				nades.head = nades.head + 1
			end
		end
	end
	if nades.head > nades.tail then
		nades.head = 1
		nades.tail = 0
	end
end

function new_nade(settings)
	local this = {
		x = settings.x,
		y = settings.y,
		z = settings.z,
		x0 = settings.x,
		y0 = settings.y,
		z0 = settings.z,
		x1 = settings.x,
		y1 = settings.y,
		z1 = settings.z,

		vx = settings.vx,
		vy = settings.vy,
		vz = settings.vz,

		pid = settings.pid,

		trem = 0.0,
		fuse = settings.fuse,
		dead = false
	} this.this = this

	local function prv_advance()
		local d,x1,y1,z1,x2,y2,z2,_
		this.x0 = this.x1
		this.y0 = this.y1
		this.z0 = this.z1
		local db = math.sqrt(this.vx*this.vx+this.vy*this.vy+this.vz*this.vz)
		--print("a",this.x0,this.y0,this.z0,this.vx,this.vy,this.vz)
		--print("db",db)
		d,x1,y1,z1,x2,y2,z2 = trace_map_ray_dist(
			this.x0,this.y0,this.z0,
			this.vx/db,this.vy/db,this.vz/db,
			db)

		local df = 1.0
		if d then
			df = math.max(0,d/db-0.001)
			--print("df",df,d,db)
		end

		this.x1 = this.x0 + this.vx*df
		this.y1 = this.y0 + this.vy*df
		this.z1 = this.z0 + this.vz*df

		if x1 then
			local bounce_vel = 0
			if x1 ~= x2 then
				this.vx = -this.vx*MODE_NADE_BDAMP
				bounce_vel = math.max(bounce_vel, math.abs(this.vx))
			end
			if y1 ~= y2 then
				this.vy = -this.vy*MODE_NADE_BDAMP
				bounce_vel = math.max(bounce_vel, math.abs(this.vy))
			end
			if z1 ~= z2 then
				this.vz = -this.vz*MODE_NADE_BDAMP
				bounce_vel = math.max(bounce_vel, math.abs(this.vz))
			end
			this.vx = this.vx * MODE_NADE_ADAMP
			this.vy = this.vy * MODE_NADE_ADAMP
			this.vz = this.vz * MODE_NADE_ADAMP

			if client and bounce_vel >= MODE_NADE_BOUNCE_SOUND_MIN then
				local vol = 1.0
				if bounce_vel < MODE_NADE_BOUNCE_SOUND_FADE then
					vol = rescale_value(MODE_NADE_BOUNCE_SOUND_MIN, MODE_NADE_BOUNCE_SOUND_FADE, 0, 1, bounce_vel)
				end
				client.wav_play_global(wav_nade_bounce, this.x, this.y, this.z, vol)
			end
		end

		this.vy = this.vy + 5*MODE_GRAVITY*MODE_NADE_STEP*MODE_NADE_STEP
	end

	function this.explode_dmg()
		local hplr = this.pid and players[this.pid]
		if hplr and not hplr.has_permission("kill") then return end

		local x,y,z
		local x0,y0,z0
		x0,y0,z0 = math.floor(this.x)
			, math.floor(this.y)
			, math.floor(this.z)

		local xlen,ylen,zlen
		xlen,ylen,zlen = common.map_get_dims()

		local vpls = nil
		if MODE_NADE_VPL_ENABLE then
			vpls = vpl_gen_from_sphere(this.x, this.y, this.z,
				MODE_NADE_VPL_MAX_COUNT, MODE_NADE_VPL_MAX_RANGE, MODE_NADE_VPL_MAX_TRIES)
		end

		local i
		for i=1,players.max do
			local plr = players[i]
			if plr and ((not hplr) or plr == hplr or plr.team ~= hplr.team) then
				if MODE_NADE_VPL_ENABLE then
					local dmg_acc = 0
					local j
					vpls[#vpls+1] = {x = this.x, y = this.y, z = this.z,
						s = MODE_NADE_VPL_DIRECT_STRENGTH, d = 0.0, special = true}
					--print(#vpls)
					for j=1,#vpls do
						-- Get the VPL.
						local v = vpls[j]

						-- Get the delta vector to the player's head.
						local dx,dy,dz
						dx = plr.x-v.x
						dy = (plr.y+((plr.crouching and 0.6) or 0.9))-v.y
						dz = plr.z-v.z

						-- Get the distance.
						local dd = dx*dx+dy*dy+dz*dz
						dd = math.sqrt(dd)

						-- Compare it against the maximum distance.
						-- Incorporate the already-travelled distance of the VPL.
						if dd + v.d < MODE_NADE_VPL_MAX_RANGE then
							-- Normalise the delta vector.
							dx = dx/dd
							dy = dy/dd
							dz = dz/dd

							-- See if there's anything between you and the game^H^H^H^H VPL.
							local nd
							nd = trace_map_ray_dist(v.x,v.y,v.z, dx,dy,dz, dd)
							if not nd then
								-- Didn't hit anything. Calculate damage.
								dd = dd + v.d
								local dmg = MODE_NADE_VPL_DAMAGE_1*v.s/(dd*dd)
								dmg_acc = math.max(dmg_acc, dmg)
							end
						end
					end
					--print(dmg_acc)
					dmg_acc = math.floor(dmg_acc + 0.8)
					if dmg_acc >= 1 then
						plr.explosive_damage(dmg_acc, hplr)
					end
				else
					local dx,dy,dz
					dx = plr.x-this.x
					dy = (plr.y+0.9)-this.y
					dz = plr.z-this.z

					local dd = dx*dx+dy*dy+dz*dz
					if dd < MODE_NADE_RANGE*MODE_NADE_RANGE then
						dd = math.sqrt(dd)
						dx = dx/dd
						dy = dy/dd
						dz = dz/dd
						local nd
						nd = trace_map_ray_dist(this.x,this.y,this.z, dx,dy,dz, dd)
						if not nd then
							local dmg = (-(math.pow(dd / MODE_NADE_RANGE, 4)) + 1) * MODE_NADE_DAMAGE

							plr.explosive_damage(dmg, hplr)
						end
					end
				end
			end
		end

		if hplr and not hplr.has_permission("build") then return end

		if map_block_get(x0,y0,z0) ~= nil then
			if y0 < ylen-2 then
				map_block_break(x0,y0,z0)
				net_broadcast(nil, common.net_pack("BHHH"
					, PKT_BLK_RM1, x0,y0,z0))
			end
		else
			for z=z0-1,z0+1 do
			for x=x0-1,x0+1 do
			for y=y0-1,y0+1 do
				if y < ylen-2 and map_block_break(x,y,z) then
					net_broadcast(nil, common.net_pack("BHHH"
						, PKT_BLK_RM1, x,y,z))
				end
			end
			end
			end
			if MODE_NADE_VPL_ENABLE then
				local dmg_blks = {}
				for j=1,#vpls do
					local v = vpls[j]
					local x = v.cx --math.floor(v.x+0.5)
					local y = v.cy --math.floor(v.y+0.5)
					local z = v.cz --math.floor(v.z+0.5)
					local blk = {x,y,z}
					if y and y < ylen-2 then
						if not dmg_blks[blk] then
							dmg_blks[blk] = 0
						end
						dmg_blks[blk] = dmg_blks[blk] + MODE_NADE_VPL_BLK_DAMAGE
					end
				end
				for blk,dmg in pairs(dmg_blks) do
					bhealth_damage(blk[1],blk[2],blk[3], dmg)
					net_broadcast(nil, common.net_pack("BHHHH", PKT_BLK_DAMAGE, blk[1],blk[2],blk[3], dmg))
				end
			end
		end


	end

	function this.tick(sec_current, sec_delta)
		if this.dead then return end

		this.trem = this.trem - sec_delta
		local i = 10
		while this.trem < 0 do
			prv_advance()
			this.trem = this.trem + MODE_NADE_STEP
			i = i - 1
			if i <= 0 then break end
		end

		local lerp = 1-this.trem/MODE_NADE_STEP
		this.x = this.x1*lerp+this.x0*(1-lerp)
		this.y = this.y1*lerp+this.y0*(1-lerp)
		this.z = this.z1*lerp+this.z0*(1-lerp)

		this.fuse = this.fuse - sec_delta
		if this.fuse <= 0 then
			if client then
				client.wav_play_global(wav_nade_boom, this.x, this.y, this.z)
				if MODE_DEBUG_VPLTEST then
					VPLPOINT = {x = this.x, y = this.y, z = this.z}
					v(true)
				end
				local i
				local nade_particlecount = (
					MODE_NADE_VPL_ENABLE and 1
					) or (math.random() * 10 + 20)
				local pvel = 2
				nade_part_mdl = nade_part_mdl or new_particle_model(70, 70, 70)
				local mdl = nade_part_mdl
				--[[
				local mdl = new_particle_model(
					60 + math.random() * 20,
					60 + math.random() * 20,
					60 + math.random() * 20)
				]]

				for i=1,nade_particlecount do
					particles_add(new_particle{
						x = this.x,
						y = this.y-0.1,
						z = this.z,
						vx = pvel*(2*math.random()-1),
						vy = pvel*(2*math.random()-1.8),
						vz = pvel*(2*math.random()-1),
						size = 16 + math.random() * 32,
						model = mdl,
					})
				end
			end
			if server then
				this.explode_dmg()
			end
			this.dead = true
		end
	end

	function this.render()
		if this.dead then return end

		mdl_nade_inst.render_global(
			this.x, this.y, this.z,
			0.0, 0.0, 0.0, 1.0)
	end

	return this
end
