--
-- OnFootMovingParts
-- Specialization for moving parts which can be controlled on foot
--
-- @author  Stefan Maurus	
-- @date  06/09/14

-- Copyright © Stefan Maurus, www.stefanmaurus.de

OnFootMovingParts = {};

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

function OnFootMovingParts:load(xmlFile)
	self.setMovingPartTime = SpecializationUtil.callSpecializationsFunction("setMovingPartTime");
	self.updateOnFootMovingParts = SpecializationUtil.callSpecializationsFunction("updateOnFootMovingParts");

	self.onFootMovingParts = {};
	local i = 0;
	while true do
		local key1 = string.format("vehicle.onFootMovingParts.movingPart(%d)", i);
		if not hasXMLProperty(xmlFile, key1) then
			break;
		end;
		
		local parts = {};
		local j = 0;
		while true do
			local key2 = string.format(key1..".part(%d)", j);
			if not hasXMLProperty(xmlFile, key2) then
				break;
			end;
			local animCurve = AnimCurve:new(linearInterpolatorTransRotScale);
			local index = Utils.indexToObject(self.components, getXMLString(xmlFile, key2.."#index"));
			if index == nil then
				break;
			end;
			local stX, stY, stZ = Utils.getVectorFromString(getXMLString(xmlFile, key2.."#startTrans"));
			local etX, etY, etZ = Utils.getVectorFromString(getXMLString(xmlFile, key2.."#endTrans"));
			local xT, yT, zT = getTranslation(index);
			local srX, srY, srZ = Utils.getVectorFromString(getXMLString(xmlFile, key2.."#startRot"));
			local erX, erY, erZ = Utils.getVectorFromString(getXMLString(xmlFile, key2.."#endRot"));
			local xR, yR, zR = getTranslation(index);
			local ssX, ssY, ssZ = Utils.getVectorFromString(getXMLString(xmlFile, key2.."#startScale"));
			local esX, esY, esZ = Utils.getVectorFromString(getXMLString(xmlFile, key2.."#endScale"));
			local xS, yS, zS = getScale(index);
			animCurve:addKeyframe({x=Utils.getNoNil(stX,xT), y=Utils.getNoNil(stY,yT), z=Utils.getNoNil(stZ,zT), rx=math.rad(Utils.getNoNil(srX,xR)), ry=math.rad(Utils.getNoNil(srY,yR)), rz=math.rad(Utils.getNoNil(srZ,zR)), sx=Utils.getNoNil(ssX,xS), sy=Utils.getNoNil(ssY,yS), sz=Utils.getNoNil(ssZ,zS), time = 0});
			animCurve:addKeyframe({x=Utils.getNoNil(etX,xT), y=Utils.getNoNil(etY,yT), z=Utils.getNoNil(etZ,zT), rx=math.rad(Utils.getNoNil(erX,xR)), ry=math.rad(Utils.getNoNil(erY,yR)), rz=math.rad(Utils.getNoNil(erZ,zR)), sx=Utils.getNoNil(esX,xS), sy=Utils.getNoNil(esY,yS), sz=Utils.getNoNil(esZ,zS), time = 1});			
			table.insert(parts, {index=index, animCurve=animCurve});
			j=j+1;
		end;
	
		local changePoint = Utils.indexToObject(self.components, getXMLString(xmlFile, key1.."#changePoint"));
		local nearestDistance = Utils.getNoNil(getXMLFloat(xmlFile, key1.."#nearestDistance"), 3);
		local isInRange = false;
		local speed = Utils.getNoNil(getXMLFloat(xmlFile, key1.."#speed"), 1);
		local currentTime = 0;
		
		local upi18n = getXMLString(xmlFile, key1.."#upi18n");
		local downi18n = getXMLString(xmlFile, key1.."#downi18n");

		local upInput = getXMLString(xmlFile, key1.."#upInput");
		local downInput = getXMLString(xmlFile, key1.."#downInput");
		
		local movingTool = Utils.getVectorNFromString(getXMLString(xmlFile, key1.."#movingTool"));
		
		table.insert(self.onFootMovingParts, {parts=parts, changePoint=changePoint, nearestDistance=nearestDistance, isInRange=isInRange, speed=speed, currentTime=currentTime, upi18n=upi18n, downi18n=downi18n, upInput=upInput, downInput=downInput, movingTool=movingTool});
		i=i+1;
	end;
end;

function OnFootMovingParts:delete()
end;

function OnFootMovingParts:readStream(streamId, connection)
end;

function OnFootMovingParts:writeStream(streamId, connection) 
end;

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

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

function OnFootMovingParts:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
	for i=1, table.getn(self.onFootMovingParts), 1 do
		self:setMovingPartTime(i, Utils.getNoNil(getXMLFloat(xmlFile, string.format(key.."#movingPart%d", i)), 0));
	end;
	
	return BaseMission.VEHICLE_LOAD_OK;
end;

function OnFootMovingParts:getSaveAttributesAndNodes(nodeIdent)
	local attributes = '';
	for i=1, table.getn(self.onFootMovingParts), 1 do
		attributes = attributes..string.format(' movingPart%d="'..self.onFootMovingParts[i].currentTime..'"', i);
	end;
	return attributes, nil;
end;

function OnFootMovingParts:setMovingPartTime(part, t, noEventSend)
	local movingPart = self.onFootMovingParts[part];
	movingPart.currentTime = t;
	if self.isServer then
		for i=1, table.getn(movingPart.movingTool) do
			Cylindered.updateComponentJoints(self, self.movingTools[movingPart.movingTool[i]+1], false);
		end;
	end;
	for _, part in pairs(movingPart.parts) do
		self:updateOnFootMovingParts(part.index, part.animCurve, t);
	end;
	SetMovingPartTimeEvent.sendEvent(self, part, t, noEventSend);
end;

function OnFootMovingParts:updateOnFootMovingParts(node, animCurve, t)
	local x,y,z, rx,ry,rz, sx,sy,sz = animCurve:get(t);
	setTranslation(node, x, y, z);
	setRotation(node, rx, ry, rz);
	setScale(node, sx, sy, sz);
end;

function OnFootMovingParts:update(dt)
	if g_currentMission.player ~= nil then
		for i, movingPart in pairs(self.onFootMovingParts) do
			if movingPart.isInRange then
				g_currentMission:addHelpButtonText(g_i18n:getText(movingPart.upi18n), InputBinding[movingPart.upInput]);		
				g_currentMission:addHelpButtonText(g_i18n:getText(movingPart.downi18n), InputBinding[movingPart.downInput]);	
				
				if InputBinding.isPressed(InputBinding[movingPart.upInput]) and not InputBinding.isPressed(InputBinding[movingPart.downInput]) then
					self:setMovingPartTime(i, math.min(movingPart.currentTime + (dt/1000*movingPart.speed), 1));
				elseif InputBinding.isPressed(InputBinding[movingPart.downInput]) and not InputBinding.isPressed(InputBinding[movingPart.upInput]) then 
					self:setMovingPartTime(i, math.max(movingPart.currentTime - (dt/1000*movingPart.speed), 0));
				end;
			end;
		end;
	end;
end;

function OnFootMovingParts:updateTick(dt)
	if g_currentMission.player ~= nil then
		local x1, y1, z1 = getWorldTranslation(g_currentMission.player.rootNode);
		for _, movingPart in pairs(self.onFootMovingParts) do
			local x2, y2, z2 = getWorldTranslation(movingPart.changePoint);
			local distance = Utils.vector3Length(x1-x2, y1-y2, z1-z2);
			if distance < movingPart.nearestDistance then
				movingPart.isInRange = true;
			else
				movingPart.isInRange = false;
			end;
		end;
	end;
end;

function OnFootMovingParts:draw()
	g_currentMission:addExtraPrintText(g_i18n:getText("FOOT_CONTROL"));
end;

function OnFootMovingParts:onAttach()
end;

function OnFootMovingParts:onDetach()	
end; 

SetMovingPartTimeEvent = {};
SetMovingPartTimeEvent_mt = Class(SetMovingPartTimeEvent, Event);

InitEventClass(SetMovingPartTimeEvent, "SetMovingPartTimeEvent");

function SetMovingPartTimeEvent:emptyNew()
    local self = Event:new(SetMovingPartTimeEvent_mt);
    self.className="SetMovingPartTimeEvent";
    return self;
end;

function SetMovingPartTimeEvent:new(vehicle, part, currentTime)
    local self = SetMovingPartTimeEvent:emptyNew()
    self.vehicle = vehicle;
	self.part = part;
	self.currentTime = currentTime;	
    return self;
end;

function SetMovingPartTimeEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId);
    self.vehicle = networkGetObject(id);

	self.part = streamReadInt16(streamId);
	self.currentTime = streamReadFloat32(streamId);
	self.vehicle:setMovingPartTime(self.part, self.currentTime, true);
	
	if not connection:getIsServer() then
        g_server:broadcastEvent(SetMovingPartTimeEvent:new(self.vehicle, self.part, self.currentTime), nil, connection, self.vehicle);
    end;
end;

function SetMovingPartTimeEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.vehicle));
	streamWriteInt16(streamId, self.part);
	streamWriteFloat32(streamId, self.currentTime);
end;

function SetMovingPartTimeEvent.sendEvent(vehicle, part, currentTime, noEventSend)
	if noEventSend == nil or noEventSend == false then
		if g_server ~= nil then
			g_server:broadcastEvent(SetMovingPartTimeEvent:new(vehicle, part, currentTime), nil, nil, vehicle);
		else
			g_client:getServerConnection():sendEvent(SetMovingPartTimeEvent:new(vehicle, part, currentTime));
		end;
	end;
end;