--
-- AnimationClips
--
-- @author:    	Xentro (www.fs-uk.com)(Marcus@Xentro.se)
-- @version:    1.1
-- @date:       2013-11-16
-- 
--[[
<clipAnimations>
	<animation node="" clipName="" clipSpeed="1" loop="true" accelerationTime="1" decelerationTime="1" inputBinding="LOWER_IMPLEMENT" l10nText="">
		<sound file="" volume="1" radius="40" innerRadius="10" />
	</animation>
</clipAnimations>
]]--

AnimationClips = {};

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

function AnimationClips:load(xmlFile)
	self.setAnimationClip = SpecializationUtil.callSpecializationsFunction("setAnimationClip");
	self.getIsClipPlaying = AnimationClips.getIsClipPlaying;
	
	self.clipAnimations = {};
	local i = 0;
	while true do
		local key = string.format("vehicle.clipAnimations.animation(%d)", i);
		if not hasXMLProperty(xmlFile, key) then break; end;
		
		local node = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#node"));
		if node ~= nil then
			local charSet = getAnimCharacterSet(node);
			if charSet ~= nil then
				local animation = {};
				animation.node = node;
				animation.animCharSet = charSet;
				animation.clip = getAnimClipIndex(animation.animCharSet, getXMLString(xmlFile, key .. "#clipName"));
				animation.clipSpeed = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#clipSpeed"), 1);
				animation.loop = Utils.getNoNil(getXMLBool(xmlFile, key .. "#loop"), false);
				animation.accelerationTime = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#accelerationTime"), 0) / 1000;
				animation.decelerationTime = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#decelerationTime"), 0) / 1000;
				animation.duration = getAnimClipDuration(animation.animCharSet, animation.clip);
				animation.activeForInput = Utils.getNoNil(getXMLBool(xmlFile, key .. "#activeForInput"), true);
				animation.canActivate = Utils.getNoNil(getXMLBool(xmlFile, key .. "#canActivate"), true);
				animation.isActivated = false;
				animation.isRunning = false;
				animation.currentAcc = 0;
				
				assignAnimTrackClip(animation.animCharSet, 0, animation.clip);
				setAnimTrackSpeedScale(animation.animCharSet, animation.clip, animation.clipSpeed);
				setAnimTrackLoopState(animation.animCharSet, 0, animation.loop);
				setAnimTrackTime(animation.animCharSet, 0, animation.currentTime);
				
				local input = getXMLString(xmlFile, key .. "#inputBinding");
				if input ~= nil then
					if InputBinding[input] ~= nil then
						animation.inputBinding = InputBinding[input];
					end;
				end;
				
				animation.text = {};
				local text = getXMLString(xmlFile, key .. "#l10nText");
				
				if text == nil then
					text = getXMLString(xmlFile, key .. "#l10nTextPositive");
					text2 = getXMLString(xmlFile, key .. "#l10nTextNegative");
					
					if text ~= nil and text2 ~= nil then
						if g_i18n:hasText(text) and g_i18n:hasText(text2) then 
							animation.text[2] = g_i18n:getText(text);
							animation.text[3] = g_i18n:getText(text2);
						end;
					end;
				else
					if g_i18n:hasText(text) then
						animation.text[1] = g_i18n:getText(text);
					end;
				end;
				
				local soundFile = getXMLString(xmlFile, key .. ".sound#file");
				if soundFile ~= nil and soundFile ~= "" then
					soundFile = Utils.getFilename(soundFile, self.baseDirectory);
					animation.soundVolume = Utils.getNoNil(getXMLFloat(xmlFile, key .. ".sound#volume"), 1.0);
					animation.soundRadius = Utils.getNoNil(getXMLFloat(xmlFile, key .. ".sound#radius"), 50);
					animation.soundInnerRadius = Utils.getNoNil(getXMLFloat(xmlFile, key .. ".sound#innerRadius"), 10);
					animation.soundFile = createAudioSource("soundFile" .. i, soundFile, animation.soundRadius, animation.soundInnerRadius, animation.soundVolume, 0);
					link(animation.node, animation.soundFile);
					setVisibility(animation.soundFile, false);
				end;
				
				table.insert(self.clipAnimations, animation);
				i = i + 1;
			else
				print("AnimationClips - Error: invalid animation node " .. node);
				break;
			end;
		else
			print("AnimationClips - Error: animation node is empty in " .. self.configFileName);
			break;
		end;
    end;
end;

function AnimationClips:delete()
end;

function AnimationClips:readStream(streamId, connection)
	for k, anim in ipairs(self.clipAnimations) do
		self:setAnimationClip(k, streamReadBool(streamId), true);
	end;
end;

function AnimationClips:writeStream(streamId, connection)
	for k, anim in ipairs(self.clipAnimations) do
		streamWriteBool(streamId, anim.isActivated);
	end;
end;

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

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

function AnimationClips:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
	if not resetVehicles then
		local i = 0;
		while true do
			local clip = key .. string.format(".clipAnimation(%d)", i);
			if not hasXMLProperty(xmlFile, clip) then break; end;
			
			local id = getXMLInt(xmlFile, clip .. "#id");
			if self.clipAnimations[id] ~= nil then
				local animTime = getXMLFloat(xmlFile, clip .. "#animationTime");
				local activated = getXMLBool(xmlFile, clip .. "#activated");
				
				if animTime > 0 then
					setAnimTrackTime(self.clipAnimations[id].animCharSet, self.clipAnimations[id].clip, animTime);
					self:setAnimationClip(id, activated);
				end;
			end;
			
			i = i + 1;
		end;
	end;
	
	return BaseMission.VEHICLE_LOAD_OK;
end;

function AnimationClips:getSaveAttributesAndNodes(nodeIdent)
	local attributes;
	local nodes = "";
	
	for k, anim in ipairs(self.clipAnimations) do
		if not anim.loop then
			if nodes ~= "" then
				nodes = nodes .. "\n";
			end;
			
			nodes = nodes .. nodeIdent .. '<clipAnimation';
			nodes = nodes .. ' id="' .. k .. '"';
			nodes = nodes .. ' animationTime="' .. string.format("%f", math.max(getAnimTrackTime(anim.animCharSet, anim.clip), 0)) .. '"';
			nodes = nodes .. ' activated="' .. tostring(anim.isActivated) .. '"';
			nodes = nodes .. " />"
		end;
	end;
	
	return attributes, nodes;
end;

function AnimationClips:update(dt)
	if self:getIsActive() and self.isClient then
		for k, anim in ipairs(self.clipAnimations) do
			if anim.inputBinding ~= nil and anim.canActivate then
				if self:getIsActiveForInput(anim.activeForInput) then
					if InputBinding.hasEvent(anim.inputBinding) then
						self:setAnimationClip(k, not anim.isActivated);
					end;
					
					if anim.text[1] ~= nil then
						g_currentMission:addHelpButtonText(anim.text[1], anim.inputBinding);
					elseif anim.text[2] ~= nil then
						if anim.isActivated then
							g_currentMission:addHelpButtonText(anim.text[3], anim.inputBinding);
						else
							g_currentMission:addHelpButtonText(anim.text[2], anim.inputBinding);
						end;
					end;
				end;
			end;
		end;
	end;
end;

function AnimationClips:updateTick(dt)
	for k, anim in ipairs(self.clipAnimations) do
		if anim.isRunning then
			local currentTime = getAnimTrackTime(anim.animCharSet, anim.clip);
			local stopClip = false;
			
			if anim.loop then
				if anim.isActivated then
					if anim.accelerationTime ~= 0 then
						if anim.currentAcc < 1 then
							anim.currentAcc = anim.currentAcc + dt * anim.accelerationTime;
							setAnimTrackSpeedScale(anim.animCharSet, anim.clip, anim.currentAcc);
						end;
					end;
				else
					if anim.decelerationTime ~= 0 then
						if anim.currentAcc > 0 then
							anim.currentAcc = anim.currentAcc - dt * anim.decelerationTime;
							setAnimTrackSpeedScale(anim.animCharSet, anim.clip, anim.currentAcc);
						else
							stopClip = true;
						end;
					else
						stopClip = true;
					end;
				end;
			else
				if currentTime > anim.duration then
					setAnimTrackTime(anim.animCharSet, anim.clip, anim.duration);
				elseif currentTime < 0 then
					setAnimTrackTime(anim.animCharSet, anim.clip, 0);
				end;
				
				if anim.isActivated then
					if currentTime > anim.duration then
						stopClip = true;
					end;
				else
					if currentTime < 0 then
						stopClip = true;
					end;
				end;
			end;
			
			if stopClip then
				disableAnimTrack(anim.animCharSet, anim.clip);
				anim.isRunning = false;
				
				if anim.soundFile ~= nil then
					setVisibility(anim.soundFile, false);
				end;
			end;
			
			if self.setMovingToolDirty ~= nil then
				self:setMovingToolDirty(anim.node);
			end;
		end;
	end;
end;

function AnimationClips:draw()
end;

function AnimationClips:setAnimationClip(id, state, noEventSend)
	local anim = self.clipAnimations[id];
	if anim ~= nil then
		anim.isActivated = state;
		
		if not anim.loop then
			local clipSpeed = anim.clipSpeed;
			if not state then
				clipSpeed = -clipSpeed;
			end;
			
			setAnimTrackSpeedScale(anim.animCharSet, anim.clip, clipSpeed);
			enableAnimTrack(anim.animCharSet, anim.clip);
			anim.isRunning = true;
		else
			if state then
				if anim.accelerationTime == 0 then
					anim.currentAcc = 1;
					setAnimTrackSpeedScale(anim.animCharSet, anim.clip, anim.currentAcc);
				end;
			else
				if anim.decelerationTime == 0 then
					anim.currentAcc = 0;
					disableAnimTrack(anim.animCharSet, anim.clip);
					anim.isRunning = false;
				end;
			end;
			
			anim.triggerTime = getAnimTrackTime(anim.animCharSet, anim.clip);
			enableAnimTrack(anim.animCharSet, anim.clip);
			anim.isRunning = true;
		end;
		
		if anim.soundFile ~= nil then
			setVisibility(anim.soundFile, anim.isRunning);
		end;
		
		SetAnimationEvent.sendEvent(self, id, state, noEventSend);
	end;
end;

function AnimationClips:getIsClipPlaying(id)
	local anim = self.clipAnimations[id];
	
	if anim ~= nil then
		return isAnimTrackEnabled(anim.animCharSet, anim.clip);
	end;
	
	return false;
end;


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, isActivated)
    local self = SetAnimationEvent:emptyNew()
    self.object = object;
	self.animationIndex = animationIndex;
	self.isActivated = isActivated;
	
    return self;
end;

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

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

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

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