--
-- controlledAnimation
-- Class for controlled animations
--
-- @author  ?????
-- @date
----
-- @edited  PeterJ - euroDZN
-- @date  11/09/2013
--
-- http://eurodzn.wordpress.com/
--
-- Copyright (C) euroDZN, Confidential, All Rights Reserved.

controlledAnimation = {};

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

function controlledAnimation:load(xmlFile)

	self.setAnimationTime = SpecializationUtil.callSpecializationsFunction("setAnimationTime");
	
	self.operateWhenSelected = Utils.getNoNil(getXMLBool(xmlFile, "vehicle.animationParts#operateWhenSelected"), true);
	
	self.animationParts = {};
	local i = 0;
	while true do
		local partName = string.format("vehicle.animationParts.animationPart(%d)", i);
		local animationPart = {};
		local partStr = getXMLString(xmlFile, partName .. "#rootNode");
		if partStr == nil then
            break;
        end;
        animationPart.rootNode = Utils.indexToObject(self.components, partStr);
		local charSet = getAnimCharacterSet(animationPart.rootNode);
		if charSet == nil then
			print("Error: invalid animation rootNode " .. partStr);
			break;
		else
			animationPart.animCharSet = charSet;
			animationPart.clip = getAnimClipIndex(animationPart.animCharSet, getXMLString(xmlFile, partName.."#clipName"));
			assignAnimTrackClip(animationPart.animCharSet, 0, animationPart.clip);
			local posInputButtonStr = getXMLString(xmlFile, partName .. "#posInputButton");
			if posInputButtonStr ~= nil then
				animationPart.posInputButton = InputBinding[posInputButtonStr];
			end;
			animationPart.posInputButton = Utils.getNoNil(animationPart.posInputButton, InputBinding.IMPLEMENT_EXTRA2);
			local negInputButtonStr = getXMLString(xmlFile, partName .. "#negInputButton");
			if negInputButtonStr ~= nil then
				animationPart.negInputButton = InputBinding[negInputButtonStr];
			end;
			animationPart.negInputButton = Utils.getNoNil(animationPart.negInputButton, InputBinding.IMPLEMENT_EXTRA4);
			animationPart.clipSpeed = Utils.getNoNil(getXMLFloat(xmlFile, partName .. "#clipSpeed"), 1);
			setAnimTrackSpeedScale(animationPart.animCharSet, animationPart.clip, animationPart.clipSpeed);
			setAnimTrackLoopState(animationPart.animCharSet, 0, false);
			animationPart.startPosition =  Utils.getNoNil(getXMLInt(xmlFile, partName .. "#startPosition"), 0);
			animationPart.currentPosition = animationPart.startPosition;
			setAnimTrackTime(animationPart.animCharSet, 0, animationPart.currentPosition);
			animationPart.animDuration = getAnimClipDuration(animationPart.animCharSet, animationPart.clip);
			if animationPart.currentPosition >= animationPart.animDuration then
				print("Error: Animation Part"..i.." startPosition larger or exakt then animation clip duration!");
				break;
			end;
			animationPart.acceleration = Utils.getNoNil(getXMLFloat(xmlFile, partName .. "#acceleration"), 0);
			animationPart.deceleration = Utils.getNoNil(getXMLFloat(xmlFile, partName .. "#deceleration"), 0)*-1;
			animationPart.offSet = Utils.getNoNil(getXMLInt(xmlFile, partName .. "#offSet"), 50);
			animationPart.loadSave = Utils.getNoNil(getXMLBool(xmlFile, partName.."#loadSave"), true);
			animationPart.showHUDinfo = Utils.getNoNil(getXMLBool(xmlFile, partName.."#showHUDinfo"), false);
			if animationPart.showHUDinfo then
				animationPart.HUDtext = getXMLString(xmlFile, partName .. "#clipName");
			end;
			local numJoints = Utils.getNoNil(getXMLInt(xmlFile, partName .. "#numJoints"), 0); 
			if numJoints > 0 then
				animationPart.joints = {};
				for j=1, numJoints do
					local jointString = string.format("%s.componentJoint%d", partName, j);
					local index = Utils.getNoNil(getXMLInt(xmlFile, jointString .."#index"), 0);
					if index == 0 then 
						print("Error: Invalid ComponenteJointIndex: "..index.."");
						break;
					end;
					local jointIndex = self.componentJoints[index];
					setJointFrame(jointIndex.jointIndex, 0, jointIndex.jointNode);
					table.insert(animationPart.joints, jointIndex);
				end;
			end;
			animationPart.animationEnabled = false;
			animationPart.inputTime = animationPart.currentPosition;
			animationPart.inputDone = false;
			animationPart.clipEndTime = false;
			animationPart.clipStartTime = false;
			animationPart.isLoading = false;
			table.insert(self.animationParts, animationPart);
        end;
        i = i + 1;
    end;

end;

function controlledAnimation:delete()
end;

function controlledAnimation:readStream(streamId, connection)
	for k, animationPart in pairs(self.animationParts) do
		local timeInput = streamReadInt32(streamId);
		self:setAnimationTime(k, timeInput, true);
		animationPart.isLoading = true;
	end;
end;

function controlledAnimation:writeStream(streamId, connection)
	for k, animationPart in pairs(self.animationParts) do
		streamWriteInt32(streamId, animationPart.inputTime);
	end;
end;

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

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

function controlledAnimation:getSaveAttributesAndNodes(nodeIdent)
	local attributes = nil;
	for k, animationPart in pairs(self.animationParts) do
		if animationPart.loadSave then
			local minTime = math.max(animationPart.currentPosition, 0);
			local maxTime = math.min(minTime, animationPart.animDuration);
			local currentTime = string.format("%d", maxTime);
			local saveAttributes = "animation" .. k .. "=\"" .. currentTime .. "\""
			if k > 1 then
				attributes = attributes .. " " .. saveAttributes;
			else
				attributes = saveAttributes;
			end;
		end;
	end;
	return attributes, nil;
end;

function controlledAnimation:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
	if not resetVehicles then
		for k, animationPart in pairs(self.animationParts) do
			local keyString = string.format("#animation%d", k);
			local inputTime = Utils.getNoNil(getXMLInt(xmlFile, key .. keyString), animationPart.startPosition);
			self:setAnimationTime(k, inputTime, true);
			animationPart.isLoading = animationPart.loadSave;
		end;
	end;
	return BaseMission.VEHICLE_LOAD_OK;
end;

function controlledAnimation:update(dt)
	if self:getIsActive() then
		if self.highTrailerAttached then
			for k,implement in pairs(self.attachedImplements) do
				local jointIndex = implement.jointDescIndex;
				if jointIndex == 3 then
					if implement.object.ptoInput ~= nil then
						if implement.object.ptoInput.node ~= nil then
							self.operateHighHitch = false;
							for k, animationPart in pairs(self.animationParts) do
								local timeInput = animationPart.inputTime;
								if timeInput ~= animationPart.startPosition then
									self:setAnimationTime(k, animationPart.startPosition);
								end;
							end;
						else
							self.operateHighHitch = true;
						end;
					elseif implement.object.powerShaft ~= nil then
						if implement.object.powerShaft.node ~= nil then
							self.operateHighHitch = false;
							for k, animationPart in pairs(self.animationParts) do
								local timeInput = animationPart.inputTime;
								if timeInput ~= animationPart.startPosition then
									self:setAnimationTime(k, animationPart.startPosition);
								end;
							end;
						else
							self.operateHighHitch = true;
						end;
					end;							
				end;
			end;
		else
			self.operateHighHitch = true;
		end;
	end;
	if self.operateHighHitch then
		if self:getIsActiveForInput(self.operateWhenSelected) then
			for nr,part in ipairs(self.animationParts) do
				if InputBinding.isPressed(part.posInputButton) then
					self:setAnimationTime(nr, part.currentPosition + (part.offSet*(dt/5)));
				elseif InputBinding.isPressed(part.negInputButton) then
					self:setAnimationTime(nr, part.currentPosition - (part.offSet*(dt/5)));
				end;
			end;
		end;
	end;
end;

function controlledAnimation:updateTick(dt)
	for k, animationPart in pairs(self.animationParts) do
		if animationPart.animationEnabled then
			local currentTime = animationPart.currentPosition;
			local timeInput = animationPart.inputTime;
			local clipSpeed = animationPart.clipSpeed;
			local duration = animationPart.animDuration;
			if animationPart.isLoading then
				clipSpeed = animationPart.clipSpeed*animationPart.offSet;
				animationPart.isLoading = false;
			end;
			if animationPart.clip ~= nil then
				if currentTime < timeInput and animationPart.inputDone == false then
					setAnimTrackSpeedScale(animationPart.animCharSet, animationPart.clip, clipSpeed);
					if currentTime+animationPart.offSet >= timeInput then
						animationPart.inputDone = true;
						setAnimTrackTime(animationPart.animCharSet, animationPart.clip, timeInput);
						setAnimTrackSpeedScale(animationPart.animCharSet, animationPart.clip, 0);
					end;
				elseif currentTime > timeInput and animationPart.inputDone == false then
					setAnimTrackSpeedScale(animationPart.animCharSet, animationPart.clip, -clipSpeed);
					if currentTime-animationPart.offSet <= timeInput then
						animationPart.inputDone = true;
						setAnimTrackTime(animationPart.animCharSet, animationPart.clip, timeInput);
						setAnimTrackSpeedScale(animationPart.animCharSet, animationPart.clip, 0);
					end;
				end;
				if currentTime >= duration-animationPart.offSet then
					animationPart.clipEndTime = true;
				elseif currentTime <= animationPart.offSet then
					animationPart.clipStartTime = true;
				elseif currentTime == animationPart.startPosition then
					animationPart.clipStartTime = true;
				else
					animationPart.clipStartTime = false;
					animationPart.clipEndTime = false;
				end;
			end;
			if animationPart.joints ~= nil then
				for k, joint in pairs(animationPart.joints) do
					setJointFrame(joint.jointIndex, 0, joint.jointNode);
				end;
			end;
			animationPart.currentPosition = getAnimTrackTime(animationPart.animCharSet, animationPart.clip);
		else
			enableAnimTrack(animationPart.animCharSet, animationPart.clip);
		end;
		animationPart.animationEnabled = isAnimTrackEnabled(animationPart.animCharSet, animationPart.clip);
	end;
	for i, jointDesc in pairs(self.componentJoints) do
		setJointFrame(self.componentJoints[i].jointIndex, 0, self.componentJoints[i].jointNode);
	end;
end;

function controlledAnimation:draw()
	if self.operateHighHitch then
		if table.getn(self.animationParts) > 0 and self:getIsActiveForInput() then
			for _,part in ipairs(self.animationParts) do
				if part.showHUDinfo then
					g_currentMission:addExtraPrintText(string.format(g_i18n:getText(part.HUDtext), self.typeDesc) .. " " .. InputBinding.getKeyNamesOfDigitalAction(part.posInputButton) .. "/" .. InputBinding.getKeyNamesOfDigitalAction(part.negInputButton));
				end;
			end;
		end;
	end;
end;

function controlledAnimation:setAnimationTime(animationPart, timeInput, noEventSend)
	local minTime = math.max(timeInput, 0);
	local maxTime = math.min(minTime, self.animationParts[animationPart].animDuration);
	SetAnimationEvent.sendEvent(self, animationPart, maxTime, noEventSend);
	if maxTime ~= self.animationParts[animationPart].currentPosition then
		self.animationParts[animationPart].inputDone = false;
		self.animationParts[animationPart].inputTime = maxTime;
	end;
end;

function controlledAnimation:validateAttacherJoint(implement, jointDesc, dt)
    return true;
end;


---- EVENT ----

SetAnimationEvent = {};
SetAnimationEvent_mt = Class(SetAnimationEvent, Event);

InitEventClass(SetAnimationEvent, "SetAnimationEvent");

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

function SetAnimationEvent:new(object, animationIndex, inputTime)
    local self = SetAnimationEvent:emptyNew()
    self.object = object;
	self.animationIndex = animationIndex;
	self.inputTime = inputTime;
    return self;
end;

function SetAnimationEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId);
	self.object = networkGetObject(id);
	self.animationIndex = streamReadInt32(streamId);
	self.inputTime = streamReadInt32(streamId);
    self:run(connection);
end;

function SetAnimationEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.object));
	streamWriteInt32(streamId, self.animationIndex);
	streamWriteInt32(streamId, self.inputTime);
end;

function SetAnimationEvent:run(connection)
	self.object:setAnimationTime(self.animationIndex, self.inputTime, true)
	if not connection:getIsServer() then
		g_server:broadcastEvent(SetAnimationEvent:new(self.object, self.animationIndex, self.inputTime), nil, connection, self.object);
	end;
end;

function SetAnimationEvent.sendEvent(vehicle, animationIndex, inputTime, noEventSend)
	local animationPart = vehicle.animationParts[animationIndex];
	if animationPart ~= nil then
		if inputTime ~= vehicle.animationParts[animationIndex].inputTime then
			if noEventSend == nil or noEventSend == false then
				if g_server ~= nil then
					g_server:broadcastEvent(SetAnimationEvent:new(vehicle, animationIndex, inputTime), nil, nil, vehicle);
				else
					g_client:getServerConnection():sendEvent(SetAnimationEvent:new(vehicle, animationIndex, inputTime));
				end;
			end;
		end;
	end;
end;

