--
-- 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(savegame)
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