--
-- ParticleSystemUtils
-- Utils class for particles
--
-- @author  Stefan Geiger
-- @edit Manuel Leithner
-- @date  05/07/13
--
-- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved.

ParticleSystemUtil = {};

function ParticleSystemUtil.loadParticleSystemDesc(particle, vehicle, xmlFile, key)
	local gravity = getXMLFloat(xmlFile, key.. "#particleGravity");
    if gravity == nil then
        return false;
    end;
    particle.particleEmitNode = Utils.indexToObject(vehicle.components, getXMLString(xmlFile, key.. "#emitNode"));
    particle.particleGravity = gravity/(1000*1000);
    particle.particleVelocity = Utils.getNoNil(getXMLFloat(xmlFile, key.. "#particleVelocity"), 20)*0.001;
    particle.particleGrowth = Utils.getNoNil(getXMLFloat(xmlFile, key.. "#particleGrowth"), 1)*0.001;
    particle.particleSize = Utils.getNoNil(getXMLFloat(xmlFile, key.. "#particleSize"), 4);
	particle.workArea = vehicle.workAreas[getXMLInt(xmlFile, key .. "#workArea")];
	local startIndex = {getTranslation(particle.workArea.start)};
	local widthIndex = {getTranslation(particle.workArea.width)};
	local heightIndex = {getTranslation(particle.workArea.height)};
	particle.workAreaHalfDirX = ((widthIndex[1]-startIndex[1])+(heightIndex[1]-startIndex[1])) / 2;
	particle.workAreaHalfDirZ = ((widthIndex[3]-startIndex[3])+(heightIndex[3]-startIndex[3])) / 2;

	particle.amount = 0;
    particle.amountTrailerScale = 1;
    if vehicle.isClient then
		for _, ps in pairs(particle.particleSystems) do
			if type(ps) == "table" then
				particle.originalLifespan = getParticleSystemLifespan(ps.geometry);
			end;
		end;
    end;

    return true;
end;

function ParticleSystemUtil.updateParticleSystem(particle, vehicle, useRaycast)
    if vehicle.isServer then
        if particle.particleEmitNode ~= nil then
			-- calculate terrain/particle collision point
			local x0,y0,z0 = getWorldTranslation(particle.particleEmitNode);
			local dx,dy,dz = localDirectionToWorld(particle.particleEmitNode, 0,-1,0);
			local vx = dx * particle.particleVelocity;
			local vy = dy * particle.particleVelocity;
			local vz = dz * particle.particleVelocity;
			local maxT = 50/particle.particleVelocity;
			local colX = x0+vx*maxT;
			local colZ = z0+vz*maxT;
			local colT = maxT;
			local hitTrailer = nil;
			local hitTerrain = false;
			if useRaycast then
				local stepT = 3/particle.particleVelocity;
				local sx = x0;
				local sy = y0;
				local sz = z0;
				for t=2*stepT, maxT, stepT do
					local x = x0+vx*t;
					local z = z0+vz*t;
					local y = y0 + vy*t + particle.particleGravity*0.5*t*t;
					local dx = x-sx;
					local dy = y-sy;
					local dz = z-sz;
					local dist = math.sqrt(dx*dx + dy*dy + dz*dz);

					ParticleSystemUtil.lastHitTerrain = false;
					ParticleSystemUtil.lastHitTrailer = nil;
					ParticleSystemUtil.lastHitDistance = 0;
					raycastAll(sx, sy, sz, dx,dy,dz, "ParticleSystemUtil.findCollisionRaycastCallback", dist, vehicle);
					hitTerrain = ParticleSystemUtil.lastHitTerrain;
					hitTrailer = ParticleSystemUtil.lastHitTrailer;

					sx = x;
					sy = y;
					sz = z;
					if hitTerrain or hitTrailer ~= nil then
						local hitDist = ParticleSystemUtil.lastHitDistance;
						colT = Utils.lerp(t-stepT, t, ParticleSystemUtil.lastHitDistance/dist);
						colX = x0+vx*colT;
						colZ = z0+vz*colT;
						break;
					end;

					if Vehicle.debugRendering then
						drawDebugPoint(x,y,z, 0,0,1,1);
					end;
				end;
			else
				local stepT = 1/particle.particleVelocity;
				for t=2*stepT, maxT, stepT do
					local x = x0+vx*t;
					local z = z0+vz*t;
					local y = y0 + vy*t + particle.particleGravity*0.5*t*t;
					local h = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z);
					if y <= h then
						hitTerrain = true;
						colX = x;
						colZ = z;
						colT = t;
						break;
					end;

					if Vehicle.debugRendering then
						drawDebugPoint(x,y,z, 0,0,1,1);
					end;
				end;
			end;

			if hitTerrain then
				local targetX, targetY, targetZ = worldToLocal(getParent(particle.workArea.start), colX, colY, colZ);

				local startX, startY, startZ = targetX-particle.workAreaHalfDirX, targetY, targetZ+particle.workAreaHalfDirZ;
				local widthX, widthY, widthZ = targetX+particle.workAreaHalfDirX, targetY, targetZ+particle.workAreaHalfDirZ;
				local heightX, heightY, heightZ = targetX-particle.workAreaHalfDirX, targetY, targetZ-particle.workAreaHalfDirZ;
				setTranslation(particle.workArea.start, startX, startY, startZ);
				setTranslation(particle.workArea.width, widthX, widthY, widthZ);
				setTranslation(particle.workArea.height, heightX, heightY, heightZ);

				if Vehicle.debugRendering then
					local x,y,z = getWorldTranslation(particle.workArea.start);
					y = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z)+0.1;
					local x1,y1,z1 = getWorldTranslation(particle.workArea.width);
					y1 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x1, 0, z1)+0.1;
					local x2,y2,z2 = getWorldTranslation(particle.workArea.height);
					y2 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x2, 0, z2)+0.1;
					drawDebugPoint(colX,getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, colX, 0, colZ)+0.1,colZ, 1,1,1,1);
					drawDebugPoint(x,y,z, 1,0,0,1);
					drawDebugPoint(x1,y1,z1, 0,1,0,1);
					drawDebugPoint(x2,y2,z2, 0,0,1,1);
					drawDebugLine(x,y,z, 1,0,0, x1, y1, z1, 0,1,0);
					drawDebugLine(x,y,z, 1,0,0, x2, y2, z2, 0,0,1);
				end;
			elseif hitTrailer ~= nil then
				local litersPerSecond = vehicle.sprayLitersPerSecond[vehicle.currentFillType];
				if litersPerSecond == nil then
					litersPerSecond = vehicle.defaultSprayLitersPerSecond;
				end
				local deltaFillLevel = litersPerSecond * dt*0.001;
				hitTrailer:setFillLevel(hitTrailer.fillLevel + deltaFillLevel, vehicle.currentFillType);
			end;

			for _, ps in pairs(particle.particleSystems) do
				if type(ps) == "table" then
					local lifespan = math.min(particle.originalLifespan, colT);
					setParticleSystemLifespan(ps.geometry, lifespan, true);
				end;
			end;
		end;
    end;
end;

function ParticleSystemUtil.findCollisionRaycastCallback(vehicle, transformId, x, y, z, distance)
    if transformId == g_currentMission.terrainRootNode then
        ParticleSystemUtil.lastHitTerrain = true;
        ParticleSystemUtil.lastHitDistance = distance;
        return false;
    else
        local trailer = g_currentMission.nodeToVehicle[transformId];
        if trailer ~= nil then
            if trailer.exactFillRootNode == transformId and trailer.allowFillType ~= nil and trailer:allowFillType(vehicle.currentFillType) and trailer.getAllowFillFromAir ~= nil and trailer:getAllowFillFromAir() and trailer.fillLevel < trailer.capacity then
                ParticleSystemUtil.lastHitTrailer = trailer;
                ParticleSystemUtil.lastHitDistance = distance;
                return false;
            end;
        end;
    end;

    return true;
end;