--
-- MaizeFruitParticleSystem
-- Specialization for Maize Cutters
--
-- @author  	Manuel Leithner (SFM-Modding)
-- @version 	v1.0
-- @date  		28/10/11
-- @history:	v1.0 - Initial version
--
-- free for noncommerical-usage
--
MaizeFruitParticleSystem = {};

MaizeFruitParticleSystem.NEXT_PARTICLE_OFFSET = 200;

function MaizeFruitParticleSystem.prerequisitesPresent(specializations)
    return true;
end;

function MaizeFruitParticleSystem:load(xmlFile)
	
	self.canPlaceParticle = MaizeFruitParticleSystem.canPlaceParticle;
	self.getIsPSActive = MaizeFruitParticleSystem.getIsPSActive;
	self.resetFruitParticleSystem = MaizeFruitParticleSystem.resetFruitParticleSystem;
	
	self.fruitParticle = {};
	
	self.fruitParticle.cloneFactor = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.maizeFruitParticleSystem.fruit#cloneFactor"), 5);
	local maizeNode = Utils.indexToObject(self.components, getXMLString(xmlFile, "vehicle.maizeFruitParticleSystem.fruit#node"));
	setVisibility(maizeNode, false);
	
	local areas = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.maizeFruitParticleSystem.areas.area(%d)", i);
		if not hasXMLProperty(xmlFile, key) then
			break;
		end;		
		local area = {};
		area.start = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#start"));
		area.width = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#width"));
		area.height = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#height"));
		area.ps = {};
		Utils.loadParticleSystem(xmlFile, area.ps, key, self.components, false, nil, self.baseDirectory);
		area.corn = {};
		Utils.loadParticleSystem(xmlFile, area.corn, key .. ".corn(0)", self.components, false, nil, self.baseDirectory);
		link(self.components[1].node, area.corn[1].shape);
		
		local sx, sy, sz = getWorldTranslation(area.start);
		local wx, wy, wz = getWorldTranslation(area.width);		
		sx, sy, sz = worldToLocal(getParent(maizeNode), sx, sy, sz);
		wx, wy, wz = worldToLocal(getParent(maizeNode), wx, wy, wz);
		area.x = (sx + wx) / 2;
		area.y = (sy + wy) / 2;
		area.z = (sz + wz) / 2;
		
		area.freeParticle = {};	
		area.nextParticleOffset = math.random(-MaizeFruitParticleSystem.NEXT_PARTICLE_OFFSET, MaizeFruitParticleSystem.NEXT_PARTICLE_OFFSET);
		
		for j=1, self.fruitParticle.cloneFactor*2 do
			local fruitClone = clone(maizeNode, true);
			setVisibility(fruitClone, false);
			table.insert(area.freeParticle, fruitClone);
		end;	
		
		area.particleInAnim = {};
		table.insert(areas, area);
		i = i + 1;
	end;
	self.fruitParticle.areas = areas;
	
	local animation = AnimCurve:new(linearInterpolatorN);
	i = 0;
	while true do 
		local key = string.format("vehicle.maizeFruitParticleSystem.animation.key(%d)", i);
		local t = getXMLFloat(xmlFile, key.."#time");
		local x,y,z = Utils.getVectorFromString(getXMLString(xmlFile, key.."#translation"));
		if x == nil or y == nil or z == nil then
			break;
		end;
		local rx, ry, rz = Utils.getVectorFromString(getXMLString(xmlFile, key.."#rotation"));
		rx = math.rad(Utils.getNoNil(rx, 0));
		ry = math.rad(Utils.getNoNil(ry, 0));
		rz = math.rad(Utils.getNoNil(rz, 0));
		local sx, sy, sz= Utils.getVectorFromString(getXMLString(xmlFile, key.."#scale"));
		sx = Utils.getNoNil(sx, 1);
		sy = Utils.getNoNil(sy, 1);
		sz = Utils.getNoNil(sz, 1);
		local alpha = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#alpha"), 1);
		animation:addKeyframe({ v={x, y, z, rx, ry, rz, sx, sy, sz, alpha}, time = t});
		i = i +1;
	end;
	self.fruitParticle.speed = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.maizeFruitParticleSystem.animation#speed"), 5);
	self.fruitParticle.animation = animation;	
	self.fruitParticle.animationXOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.maizeFruitParticleSystem.animation#maxXOffset"), 0);	
	self.fruitParticle.animTime = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.maizeFruitParticleSystem.animation#time"), 3) * 1000;
	self.fruitParticle.hardness = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.maizeFruitParticleSystem.animation#hardness"), 0.1);
	local fruitTime = getXMLFloat(xmlFile, "vehicle.maizeFruitParticleSystem.fruit#fruitOffset");
	if fruitTime ~= nil then
		self.fruitParticle.partCount = fruitTime*1000;
	end;
	
	self.lastActiveFruitType = FruitUtil.FRUITTYPE_UNKNOWN;
	self.isFruitReset = false;
	
	
	local mapObj = g_currentMission.fruits[FruitUtil.FRUITTYPE_MAIZE];
	self.maizeViewDistance = getFoliageViewDistance(mapObj.id);
	
	self.onStopDoReset = false;
end;

function MaizeFruitParticleSystem:delete()
    for _, area in ipairs(self.fruitParticle.areas) do
        Utils.deleteParticleSystem(area.ps);
		Utils.deleteParticleSystem(area.corn);
    end;
end;

function MaizeFruitParticleSystem:readStream(streamId, connection)
end;

function MaizeFruitParticleSystem:writeStream(streamId, connection)
end;

function MaizeFruitParticleSystem:mouseEvent(posX, posY, isDown, isUp, button)
end;

function MaizeFruitParticleSystem:keyEvent(unicode, sym, modifier, isDown)
end;

function MaizeFruitParticleSystem:update(dt)
end;

function MaizeFruitParticleSystem:updateTick(dt)

	local isPsVisible = true;

	if self:getIsActive() or self.rollNodes[1].speed ~= 0 then	
		isPsVisible = true;
		local camera = getCamera();
		local distance = 0;
		if camera ~= 0 then
			local x,y,z = getWorldTranslation(camera);
			local x1,y1,z1 = getWorldTranslation(self.rootNode);
			distance = Utils.vector3Length(x-x1,y-y1,z-z1);
			isPsVisible = (distance <= self.maizeViewDistance);
		end;
	end;
	
	if self.rollNodes[1].speed ~= 0 then
		-- play particle animation
		local animTime = self.fruitParticle.animTime;
		for _, area in pairs(self.fruitParticle.areas) do
			local isParticleActive = false;
			for k, particle in pairs(area.particleInAnim) do
				if particle.time > animTime then
					table.remove(area.particleInAnim, k);
					table.insert(area.freeParticle, particle.node);
					setVisibility(particle.node, false);
					setShaderParameter(getChildAt(particle.node, 0), "FruitSettings", 1, 0, 0, 0, false);
				else 
					particle.time = particle.time + dt * math.max(0.85, math.min((self.lastSpeed*3600) / self.fruitParticle.speed, 1.5));			
					local v = self.fruitParticle.animation:get(particle.time / animTime);
					local offset = particle.offset * (particle.time / animTime);
					setTranslation(particle.node, area.x + v[1] + offset, area.y + v[2], area.z + v[3]);
					setRotation(particle.node, v[4],v[5],v[6]);
					setShaderParameter(getChildAt(particle.node, 0), "FruitSettings", v[8], 0, 0, v[10], false);
					
					particle.lastUpdate = particle.lastUpdate + dt;
					if particle.dir == nil then						
						particle.dir = 1;
						if math.random(-1, 1) < 0 then
							particle.dir = -1;
						end;	
					end;
					
					local movement = dt * self.fruitParticle.hardness;
					local x,y,z = getRotation(getChildAt(particle.node, 0));
					local newRotX = math.rad(x + math.random(0, movement) * particle.dir);
					local newRotZ = math.rad(z + math.random(0, movement) * particle.dir);
					
					newRotX = Utils.clamp(newRotX, math.rad(-70), math.rad(70));
					newRotZ = Utils.clamp(newRotZ, math.rad(-70), math.rad(70));
					setRotation(getChildAt(particle.node, 0), newRotX, y, newRotZ);	
					
					if particle.lastUpdate > 300 then
						particle.dir = particle.dir * -1;
						particle.lastUpdate = 0;
					end;
				end;
				isParticleActive = true;
			end;
			
			if self.isClient then
				Utils.setEmittingState(area.ps, isParticleActive and isPsVisible);
				Utils.setEmittingState(area.corn, isParticleActive and isPsVisible);
			end;
		end;
		self.onStopDoReset = true;
	else
		if self.onStopDoReset then
			self:resetFruitParticleSystem();
			self.onStopDoReset = false;
		end;
	end;
	
	if self:getIsActive() then		
		local allowsThreshing = true;
		if self.attacherVehicle ~= nil then
			allowsThreshing = self.attacherVehicle.allowsThreshing and self.attacherVehicle:getIsThreshingAllowed(false);
		end;
		
		if self.reelStarted and self:getIsPSActive() and self.lastSpeed*3600 > 0.5 and self.speedViolationTimer > 0 and allowsThreshing and isPsVisible then
			if self.attacherVehicle ~= nil and self.attacherVehicle.allowsThreshing then
				if self.attacherVehicle.lastValidGrainTankFruitType ~= FruitUtil.FRUITTYPE_UNKNOWN then
					self.lastActiveFruitType = self.attacherVehicle.lastValidGrainTankFruitType;
				end;
				
				if self.lastActiveFruitType == FruitUtil.FRUITTYPE_MAIZE then
					for k, area in pairs(self.fruitParticle.areas) do
						local x,_,z = getWorldTranslation(area.start);
						local x1,_,z1 = getWorldTranslation(area.width);
						local x2,_,z2 = getWorldTranslation(area.height);  		
						fruitValue = Utils.getFruitArea(self.lastActiveFruitType, x, z, x1, z1, x2, z2);
						
						if fruitValue > 0 then
							if self:canPlaceParticle(area, 0) then
								area.nextParticleOffset = math.random(-MaizeFruitParticleSystem.NEXT_PARTICLE_OFFSET, MaizeFruitParticleSystem.NEXT_PARTICLE_OFFSET);
								local newParticle = {};
								newParticle.time = 0;
								newParticle.lastUpdate = 0;
								newParticle.node = area.freeParticle[1];
								newParticle.offset = math.random(-self.fruitParticle.animationXOffset, self.fruitParticle.animationXOffset);
								table.remove(area.freeParticle, 1);
								-- randomize particle rotation
								setRotation(getChildAt(newParticle.node, 0), math.rad(math.random(-10, 10)), math.rad(math.random(0, 45)), math.rad(math.random(-10, 10)));
								-- set correct particle position
								local v = self.fruitParticle.animation:get(0);
								setTranslation(newParticle.node, area.x + v[1] + newParticle.offset, area.y + v[2], area.z + v[3]);
								setRotation(newParticle.node, v[4],v[5],v[6]);
								setShaderParameter(getChildAt(newParticle.node, 0), "FruitSettings", 1, 0, 0, v[10], false);
								setVisibility(newParticle.node, true);				
								table.insert(area.particleInAnim, newParticle);
							end;
						end;
					end;
				end;
			end;
		end;
	end;
end;

function MaizeFruitParticleSystem:draw()	
end;

function MaizeFruitParticleSystem:resetFruitType()
	self.isFruitReset = true;
end;

function MaizeFruitParticleSystem:getIsPSActive()
	local isActive = self.lastCutterAreaBiggerZero or self.isFruitReset;
	self.isFruitReset = false;	
	return isActive;
end;

function MaizeFruitParticleSystem:canPlaceParticle(area, time)
	local animatedParticle = area.particleInAnim;	
	local percent = math.max(0.85, math.min((self.lastSpeed*3600) / self.fruitParticle.speed, 1.5));
	local halfTime = (self.fruitParticle.partCount / percent) / 2 + area.nextParticleOffset;
	
	for _, particle in pairs(animatedParticle) do
		if particle.time + halfTime > time and  particle.time - halfTime < time then
			return false;
		end;
	end;
	
	return true;
end;

function MaizeFruitParticleSystem:onLeave()
    if self.deactivateOnLeave then
        self:resetFruitParticleSystem();
    end;
end;

function MaizeFruitParticleSystem:onDetach()
	self:resetFruitParticleSystem();
end;

function MaizeFruitParticleSystem:resetFruitParticleSystem()
	for _, area in pairs(self.fruitParticle.areas) do
		for k, particle in pairs(area.particleInAnim) do
			table.insert(area.freeParticle, particle.node);
			setVisibility(particle.node, false);
		end;
		area.particleInAnim = {};
	end;
end;