--
-- InteractiveComponent Interface
-- Specifies an interactive component
--
-- @author  	Manuel Leithner (SFM-Modding)
-- @version 	v4.0
-- @date  		15/10/10
-- @history:	v1.0 - Initial version
--				v2.0 - converted to ls2011
--				v4.0 - by Mogli: several improvements
--
-- free for noncommerical-usage
--

InteractiveControlBase = {}

function InteractiveControlBase.newSpecialization()
	local specialization = {}
	setmetatable(specialization, { __index = InteractiveControlBase })
	return specialization
end

function InteractiveControlBase.prerequisitesPresent(specializations)
	if InteractiveControl ~= nil and SpecializationUtil.hasSpecialization(InteractiveControl, specializations) then
		return true
	end
	if InteractiveControlPanel ~= nil and SpecializationUtil.hasSpecialization(InteractiveControlPanel, specializations) then
		return true
	end
	return false
end;

function InteractiveControlBase:load(xmlFile)
end;

function InteractiveControlBase:delete()
end;

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

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

function InteractiveControlBase:update(dt)	
end;

function InteractiveControlBase:updateTick(dt)
end;

function InteractiveControlBase:draw()
end;

function InteractiveControlBase.checkForKeyModifiers( keys )
	local modifiers = {}
	for keyId,bool in pairs( Input.keyIdIsModifier ) do
		modifiers[keyId] = true
	end
	for _, keyId in pairs(keys) do
		modifiers[keyId] = false
	end
	for keyId,bool in pairs(modifiers) do
		if bool and Input.isKeyPressed( keyId ) then
			return false
		end
	end
	return true
end

function InteractiveControlBase.hasInputEvent( name )
	if InputBinding[name] == nil then
		return false
	end
	if InputBinding.hasEvent(InputBinding[name]) then
		if InputBinding.areKeysPressed( InputBinding.actions[InputBinding[name]].keys1 ) then
		  return InteractiveControlBase.checkForKeyModifiers( InputBinding.actions[InputBinding[name]].keys1 )
		end
		if InputBinding.areKeysPressed( InputBinding.actions[InputBinding[name]].keys2 ) then
		  return InteractiveControlBase.checkForKeyModifiers( InputBinding.actions[InputBinding[name]].keys2 )
		end
		return true
	end
	return false
end


InteractiveComponentInterface = InteractiveControlBase.newSpecialization();

function InteractiveComponentInterface:new(vehicle, xmlFile, xmlName, mt, event, setter, getter)

	if vehicle == nil then
		print("ERROR in InteractiveComponentInterface:new ("..tostring(vehicle)..")")
		InteractiveComponentInterface.printCallstack()
	end
	
	local mTable;
	if mt == nil then
		mTable = Class(InteractiveComponentInterface);
	else
		mTable = mt;
	end;
  local instance = {};
  setmetatable(instance, mTable);
	
	instance.vehicle = vehicle;
	instance.id = Utils.getNoNil(getXMLString(xmlFile, xmlName .. "#name"),"");
	instance.name = Utils.getNoNil(g_i18n:getText( instance.id ), "ERROR");
	instance.mark = Utils.indexToObject(vehicle.components, getXMLString(xmlFile, xmlName .. "#mark"));
	setVisibility(instance.mark,false);
	instance.highlight = getChildAt(instance.mark, 0);
	instance.scaleX, instance.scaleY, instance.scaleZ = getScale(instance.highlight);
	instance.scale = 0.01;
	instance.size = Utils.getNoNil(getXMLFloat(xmlFile, xmlName .. "#size"), 0.1);
	instance.isActive = true;
	instance.isMouseOver = false;
	instance.isOpen = false;
	instance.onMessage  = g_i18n:getText( Utils.getNoNil(getXMLString(xmlFile, xmlName .. "#onMessage"),  "ic_component_open"));
	instance.offMessage = g_i18n:getText( Utils.getNoNil(getXMLString(xmlFile, xmlName .. "#offMessage"), "ic_component_close"));
	instance.synch = Utils.getNoNil(getXMLBool(xmlFile, xmlName .. "#synch"), true);
	instance.saveAttributes = Utils.getNoNil(getXMLBool(xmlFile, xmlName .. "#saveAttributes"), true);
	instance.showHelp = Utils.getNoNil(getXMLBool(xmlFile, xmlName .. "#showHelpText"), false);
	instance.playSound = Utils.getNoNil(getXMLBool(xmlFile, xmlName .. "#playSound"), true);
	instance.playSoundLoop = Utils.getNoNil(getXMLBool(xmlFile, xmlName .. "#playSoundLoop"), false);

	local xmlNameSound = xmlName .. ".interactiveControlSound"
	if vehicle.isClient and hasXMLProperty(xmlFile, xmlNameSound) then
		instance.interactiveControlSample = Utils.loadSample(xmlFile, {}, xmlNameSound, nil, vehicle.baseDirectory)
	end
	
	InteractiveComponentInterface.parseEventSetterGetter( instance, event, setter, getter );
	instance.setStateFunction = InteractiveComponentInterface.getSetter( instance )
	instance.getStateFunction = InteractiveComponentInterface.getGetter( instance )
	
	return instance;	
end;

function InteractiveComponentInterface:update(dt)	
	if self.isActive then
		if self.highlight ~= nil then
			if self.isMouseOver then	
				self.scale = self.scale - 0.0002 * dt;
				setScale(self.highlight, self.scaleX + self.scale, self.scaleY, self.scaleZ + self.scale);				
				if self.scaleX + self.scale <= 0.95 then
					self.scale = 0.05;
				end;				
			end;
		end;
	end;
end;

function InteractiveComponentInterface:draw()
	if self.isMouseOver and self.isActive and self.vehicle ~= nil and self.vehicle.infoBar ~= nil then
		self.vehicle.infoBar.hud:render();
		setTextBold(true);
		setTextColor(1,1,1,1);
		setTextAlignment(RenderText.ALIGN_LEFT);
		if self.isOpen then
			renderText(self.vehicle.infoBar.posX + 0.016, self.vehicle.infoBar.posY + 0.017, 0.018, string.format(self.offMessage, self.name));
		else
			renderText(self.vehicle.infoBar.posX + 0.016, self.vehicle.infoBar.posY + 0.017, 0.018, string.format(self.onMessage, self.name));
		end;
		setTextAlignment(RenderText.ALIGN_LEFT);
		setTextBold(false);
	end;
end;

function InteractiveComponentInterface:doAction(noEventSend, forceValue)
	local playSound = false
	if forceValue ~= nil then
		if self.isOpen ~= forceValue and self.playSound then
			playSound = true
		end
		self.isOpen = forceValue;
	else
		self.isOpen = not self.isOpen;
		if self.playSound then
			playSound = true
		end
	end;

	if playSound then
		local interactiveControlSample = Utils.getNoNil( self.interactiveControlSample, self.vehicle.interactiveControlSample )
		if interactiveControlSample ~= nil then
			if self.playSoundLoop then
				if self.isOpen then
					Utils.playSample( interactiveControlSample, 0, 0, nil )
				else
					Utils.stopSample( interactiveControlSample )
				end
			else
				Utils.playSample( interactiveControlSample, 1, 0, nil )
			end
		end
	end
end;

function InteractiveComponentInterface:onEnter(dt)
	self.isMouseOver = true;
end;

function InteractiveComponentInterface:onExit(dt)
	self.isMouseOver = false;
end;

function InteractiveComponentInterface:setActive(isActive)
	self.isActive = isActive;
	if self.isMouseOver then
		self:onExit(1);
	end
end;

function InteractiveComponentInterface:setVisible(isVisible)
	if self.mark ~= nil then
		setVisibility(self.mark, isVisible);
	end;
end;

function InteractiveComponentInterface:printCallstack()
	local i = 2 
	local info 
	print("------------------------------------------------------------------------") 
	while i <= 10 do
		info = debug.getinfo(i) 
		if info == nil then break end
		print(string.format("%i: %s (%i): %s", i, info.short_src, Utils.getNoNil(info.currentline,0), Utils.getNoNil(info.name,"<???>"))) 
		i = i + 1 
	end
	if info ~= nil and info.name ~= nil and info.currentline ~= nil then
		print("...") 
	end
	print("------------------------------------------------------------------------") 
end;

function InteractiveComponentInterface:getSaveAttributes()
	return ""
end

function InteractiveComponentInterface:setSaveAttributes(xmlFile, xmlName)
end

function InteractiveComponentInterface:parseEventSetterGetter( event, setter, getter )
	local dummy = {}
	dummy.event  = event 
	dummy.setter = setter 
	dummy.getter = getter 
	for _,n in pairs({"event","setter","getter"}) do
		local n2 = n.."Tab";
		if dummy[n] == nil then
			self[n]  = nil
			self[n2] = nil			
		else			
			self[n2] = Utils.splitString(".",dummy[n]);
			if table.getn( self[n2] ) == 1 then
				self[n] = self[n2][1]
				self[n2] = nil			
			else
				self[n] = nil
				if table.getn( self[n2] ) < 1 then
					self[n2] = nil			
				end
			end
		end
	end
end	

local function getHelper( object, tab, index )
	if object == nil then
		return
	elseif tab[index] == nil then
		return object
	else		
		return getHelper( object[tab[index]], tab, index+1 )
	end
end

local function setHelper( object, tab, index, value )
	if object == nil then
		print("WARNING in InteractiveButtons: object is nil");
		return
	end
	if tab[index] == nil then
		print("ERROR in InteractiveButtons: tab[index] is nil");
		self:printCallstack()
		return 
	end
	
	if tab[index+1] == nil then
		object[tab[index]] = value
	else
		setHelper( object[tab[index]], tab, index+1, value )
	end
end

function InteractiveComponentInterface:getGetter()
	local fct = nil
	if     self.getter ~= nil then
		fct = self.vehicle[self.getter]
	elseif self.getterTab ~= nil then
		fct = getHelper( self.vehicle, self.getterTab, 1 )
	elseif self.event ~= nil then
		fct = function( vehicle )
			return vehicle[self.event]
		end
	elseif self.eventTab ~= nil then
		fct = function( vehicle )
			return getHelper( vehicle, self.eventTab, 1 )
		end
	end
	
	if fct ~= nil and type( fct ) ~= "function" then
		print("Error in getGetter: "..tostring(fct))
		return
	end
	
	return fct
end

function InteractiveComponentInterface:getSetter()
	local fct = nil
	if     self.setter ~= nil then
		fct = self.vehicle[self.setter]
	elseif self.setterTab ~= nil then
		fct = getHelper( self.vehicle, self.setterTab, 1 )
	elseif self.event ~= nil then
		fct = function( vehicle, value )
			vehicle[self.event] = value
		end
	elseif self.eventTab ~= nil then
		fct = function( vehicle, value )
			setHelper( vehicle, self.eventTab, 1, value )
		end
	end
	
	if fct ~= nil and type( fct ) ~= "function" then
		print("Error in getSetter: "..tostring(fct))
		return
	end
	
	return fct
end

function InteractiveComponentInterface.playAnimationToTime( vehicle, animation, animTime )
	curAnimTime = vehicle:getAnimationTime( animation )
	tgtAnimTime = Utils.clamp( animTime, 0.001, 0.999 )
	local dir = 0
	if     tgtAnimTime < curAnimTime then
		dir = -1
	elseif tgtAnimTime > curAnimTime then
		dir = 1
	end
	
	if dir ~= 0 then
		vehicle:playAnimation( animation, dir, curAnimTime, true);
		vehicle:setAnimationStopTime( animation, tgtAnimTime );
		return true
	end
	return false
end