--
-- InteractiveControl v4.0
-- Specialization for an interactive control
--
-- SFM-Modding
-- @author:      Manuel Leithner
-- @date:        17/10/10
-- @version:    v4.0
-- @history:    v1.0 - initial implementation
--                v2.0 - convert to LS2011 and some bugfixes
--                v4.0 - by Mogli: several improvements
--
-- free for noncommerical-usage
--

InteractiveControl = InteractiveControlBase.newSpecialization();

function InteractiveControl.prerequisitesPresent(specializations)
    return SpecializationUtil.hasSpecialization(Steerable, specializations);
end;

function InteractiveControl:load(savegame)

    self.doActionOnObject = SpecializationUtil.callSpecializationsFunction("doActionOnObject");
    self.setPanelOverlay = SpecializationUtil.callSpecializationsFunction("setPanelOverlay");
    
    self.infoBar = {};
    self.infoBar.posX = 0.4;
    self.infoBar.posY = 0.9359;
    
    self.infoBar.width = 0.3156;
    self.infoBar.hud = Overlay:new("icInfobar", Utils.getFilename("Textures/interactiveControl_info.dds", self.baseDirectory), self.infoBar.posX, self.infoBar.posY, self.infoBar.width, 0.0526);
    
    self.interactiveObjects = {};    
    
    self.indoorCamIndex = 1;
    self.outdoorCamIndex = 2;
    
    self.lastMouseXPos = 0;
    self.lastMouseYPos = 0;                    

    self.panelOverlay = nil;
    self.foundInteractiveObject = nil;
    self.isMouseActive = false;
    
    if self.isClient then
        self.interactiveControlSample = Utils.loadSample(xmlFile, {}, "vehicle.interactiveControlSound", nil, self.baseDirectory)
    end
    
end;

function InteractiveControl:delete()
    for _,v in pairs(self.interactiveObjects) do
        v:delete();
    end;
    if self.infoBar ~= nil then
        self.infoBar.hud:delete();
        self.infoBar = nil;
    end;
    if self.interactiveControlSample ~= nil then
        pcall( Utils.deleteSample, self.interactiveControlSample )
        self.interactiveControlSample = nil 
    end
end;

function InteractiveControl:readStream(streamId, connection)
    local icCount = streamReadInt8(streamId);
    for i=1, icCount do
        local isOpen = streamReadBool(streamId);
        if self.interactiveObjects[i] ~= nil then
            if self.interactiveObjects[i].synch then
                self.interactiveObjects[i]:doAction(true, isOpen);    
            end;
        end;
    end;
end;

function InteractiveControl:writeStream(streamId, connection)
    streamWriteInt8(streamId, table.getn(self.interactiveObjects));
    for k,v in pairs(self.interactiveObjects) do
        streamWriteBool(streamId, v.isOpen);
    end;
end;
    
function InteractiveControl:mouseEvent(posX, posY, isDown, isUp, button)    

    self.lastMouseXPos = posX;
    self.lastMouseYPos = posY;

    if isDown then
        if Input.isMouseButtonPressed(Input.MOUSE_BUTTON_LEFT) and self.foundInteractiveObject ~= nil then            
            self:doActionOnObject(self.foundInteractiveObject);                    
        end;
        local currentCam = self.cameras[self.camIndex];
        if currentCam.allowTranslation then
            if Input.isMouseButtonPressed(Input.MOUSE_BUTTON_WHEEL_UP) then
                currentCam:zoomSmoothly(-0.75);
            elseif Input.isMouseButtonPressed(Input.MOUSE_BUTTON_WHEEL_DOWN) then
                currentCam:zoomSmoothly(0.75);
            end;
        end;    
    end;
    
    for _,v in pairs(self.interactiveObjects) do
        v:mouseEvent(posX, posY, isDown, isUp, button);
    end;
    
    if self.isMouseActive then
        self.mouseButton = MouseControlsVehicle.BUTTON_NONE;
    end;
end;

function InteractiveControl:keyEvent(unicode, sym, modifier, isDown)
    for _,v in pairs(self.interactiveObjects) do
        v:keyEvent(unicode, sym, modifier, isDown);
    end;
end;

function InteractiveControl:update(dt)
    if self:getIsActive() then    
        self.foundInteractiveObject = nil;
        local icObject = nil;
        for k,v in pairs(self.interactiveObjects) do
            if      self.isClient 
                    and self:getIsActiveForInput(false) 
                    and not self:hasInputConflictWithSelection() 
                    and InteractiveControl.hasInputEvent( v.id ) then
                if v.isLocalOnly == nil or not v.isLocalOnly then
                    InteractiveControlEvent.sendEvent(self, k, noEventSend);    
                end;
                v:doAction()
            end
            v:update(dt);
            if v.isActive and self.isMouseActive then
                v:onExit(dt);
                if icObject == nil and self.camIndex == self.indoorCamIndex then
                    local worldX,worldY,worldZ = getWorldTranslation(v.mark);
                    local x,y,z = project(worldX,worldY,worldZ);
                    if z <= 1 then    
                        if self.lastMouseXPos > (x-v.size/2) and self.lastMouseXPos < (x+v.size/2) then
                            if self.lastMouseYPos > (y-v.size/2) and self.lastMouseYPos < (y+v.size/2) then
                                local isOverlapped = false;
                                
                                if self.panelOverlay ~= nil then                                
                                    local overlay = self.panelOverlay.mainBackground;
                                    isOverlapped = self.lastMouseXPos >= overlay.x and self.lastMouseXPos <= overlay.x+overlay.width and self.lastMouseYPos >= overlay.y and self.lastMouseYPos <= overlay.y+overlay.height;
                                end;
                                
                                if not isOverlapped then
                                    icObject = v;
                                    self.foundInteractiveObject = k;
                                    break;
                                end;
                            end;
                        end;
                    end;
                end;                
            end;
        end;
        
        if icObject ~= nil then
            icObject:onEnter(dt);
        end;
        
        if self.isClient and self:getIsActiveForInput(false) then --and not self:hasInputConflictWithSelection() then
            if InputBinding.hasEvent(InputBinding.INTERACTIVE_CONTROL_SWITCH) then
                self.isMouseActive = not self.isMouseActive;
                
                if self.isMouseActive then
                    if InteractiveControl.newVehicleCameraMouseEvent == nil then
                    
                    
                        function InteractiveControl:newVehicleCameraMouseEvent(superFunc, posX, posY, isDown, isUp, button)    
                            if self.vehicle.isMouseActive then
                                if self.isRotatable and Input.isMouseButtonPressed(Input.MOUSE_BUTTON_RIGHT) then
                                    if 0.001 < self.limitRotXDelta then
                                        self.rotX = math.min(self.rotX + InputBinding.mouseMovementY, self.rotX)
                                    elseif self.limitRotXDelta < -0.001 then
                                        self.rotX = math.max(self.rotX + InputBinding.mouseMovementY, self.rotX)
                                    else
                                        self.rotX = self.rotX + InputBinding.mouseMovementY
                                    end

                                    self.rotY = self.rotY - InputBinding.mouseMovementX
                                end
                            else
                                superFunc( self, posX, posY, isDown, isUp, button)    
                            end
                        end
                        VehicleCamera.mouseEvent = Utils.overwrittenFunction( VehicleCamera.mouseEvent, InteractiveControl.newVehicleCameraMouseEvent );    
                        
                        
                    end
                else
                    g_mouseControlsHelp.active = true
                    InputBinding.setShowMouseCursor(false);
                    for _,v in pairs(self.interactiveObjects) do
                        v:onExit(dt);
                    end;
                end;
                
                for _,v in pairs(self.interactiveObjects) do
                    v:setVisible(self.isMouseActive);
                end;
            end;
            if self.isMouseActive then                
                if Input.isMouseButtonPressed(Input.MOUSE_BUTTON_RIGHT) then
                    g_mouseControlsHelp.active = true
                    InputBinding.setShowMouseCursor(false);
                else
                    g_mouseControlsHelp.active = false
                    InputBinding.setShowMouseCursor(true);
                end;
            else
                self.foundInteractiveObject = nil;
            end;
        else
            self.foundInteractiveObject = nil;
        end;
    end;    
end;

function InteractiveControl:doActionOnObject(id, noEventSend)
    if self.interactiveObjects[id].isLocalOnly == nil or not self.interactiveObjects[id].isLocalOnly then
        InteractiveControlEvent.sendEvent(self, id, noEventSend);    
    end;
    self.interactiveObjects[id]:doAction(noEventSend);    
end;

function InteractiveControl:setObjectActive(id, isActive, noEventSend)
    local act = isActive 
    if isActive == nil then
        act = true
    end
    if self.interactiveObjects[id].isLocalOnly == nil or not self.interactiveObjects[id].isLocalOnly then
        InteractiveControlActEvent.sendEvent(self, id, act, noEventSend);    
    end;
    self.interactiveObjects[id]:setActive(act);
end;

function InteractiveControl:getObjectId( id )
    for k,v in pairs(self.interactiveObjects) do
        if v.id == id then
            return k
        end
    end
    return nil
end

function InteractiveControl:draw()    
    for _,v in pairs(self.interactiveObjects) do
        if v.showHelp and InputBinding[v.id] ~= nil then
            local text=""
            if v.isOpen then
                text = string.format(v.offMessage, v.name);
            else
                text = string.format(v.onMessage, v.name);
            end;
            g_currentMission:addHelpButtonText(text, InputBinding[v.id]);
        end;
    
        v:draw();
    end;
    if self.isMouseActive then
        g_currentMission:addHelpButtonText(g_i18n:getText("InteractiveControl_Off"), InputBinding.INTERACTIVE_CONTROL_SWITCH);
    else
        g_currentMission:addHelpButtonText(g_i18n:getText("InteractiveControl_On"), InputBinding.INTERACTIVE_CONTROL_SWITCH);
    end;
end;

function InteractiveControl:onLeave()
--self.cameras[self.indoorCamIndex].isActivated = true;
    g_mouseControlsHelp.active = true;
    if g_gui.currentGui == nil then
        InputBinding.setShowMouseCursor(false);
    end;
end;


function InteractiveControl:setPanelOverlay(panel)
    
    if self.panelOverlay ~= nil then
        if self.panelOverlay.setActive ~= nil then
            self.panelOverlay:setActive(false);
        end;
    end;
    self.panelOverlay = panel;

    if panel ~= nil then
        if panel.setActive ~= nil then
            panel:setActive(true);
        end;
    end;
end;

function InteractiveControl:getSaveAttributesAndNodes(nodeIdent)
  local nodes = ""
    
    for _,v in pairs(self.interactiveObjects) do
        if v.vehicle == self and v.saveAttributes then
            local str = v:getSaveAttributes()
            if str ~= nil and str ~= "" then
                if nodes ~= "" then
                    nodes = nodes .. "\n"
                end
                nodes = nodes .. nodeIdent .. "<interactiveComponent name=\"" .. v.id .. "\" ".. str .. " />"                    
            end
        end
    end
    
    return "", nodes
end

function InteractiveControl:loadFromAttributesAndNodes(xmlFile, key, resetVehicles)
    local i=0
    while true do
        local xmlName = string.format(key..".interactiveComponent(%d)", i);    
        local id      = getXMLString(xmlFile, xmlName .. "#name")
        if id == nil then
            break
        end
        for _,v in pairs(self.interactiveObjects) do
            if v.id == id then
                if v.vehicle == self and v.saveAttributes then
                    local tmp = v.playSound
                    v.playSound = false
                    v:setSaveAttributes(xmlFile, xmlName)
                    v.playSound = tmp
                end
                break
            end
        end
        i = i + 1
    end

 return BaseMission.VEHICLE_LOAD_OK
end

--
-- InteractiveControlEvent 
-- Specialization for an interactive control
--
-- SFM-Modding
-- @author:      Manuel Leithner
-- @date:        14/12/11
-- @version:    v2.0
-- @history:    v1.0 - initial implementation
--                v2.0 - convert to LS2011 and some bugfixes
--
InteractiveControlEvent = {};
InteractiveControlEvent_mt = Class(InteractiveControlEvent, Event);

InitEventClass(InteractiveControlEvent, "InteractiveControlEvent");

function InteractiveControlEvent:emptyNew()
    local self = Event:new(InteractiveControlEvent_mt);
    return self;
end;

function InteractiveControlEvent:new(vehicle, interactiveControlID)
    local self = InteractiveControlEvent:emptyNew()
    self.vehicle = vehicle;
    self.interactiveControlID = interactiveControlID;
    return self;
end;

function InteractiveControlEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId);
    self.interactiveControlID = streamReadInt8(streamId);
    self.vehicle = networkGetObject(id);
    self:run(connection);
end;

function InteractiveControlEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.vehicle));    
    streamWriteInt8(streamId, self.interactiveControlID);
end;

function InteractiveControlEvent:run(connection)
    self.vehicle:doActionOnObject(self.interactiveControlID, true);
    if not connection:getIsServer() then
        g_server:broadcastEvent(InteractiveControlEvent:new(self.vehicle, self.interactiveControlID), nil, connection, self.vehicle);
    end;
end;

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




InteractiveControlActEvent = {};
InteractiveControlActEvent_mt = Class(InteractiveControlActEvent, Event);

InitEventClass(InteractiveControlActEvent, "InteractiveControlActEvent");

function InteractiveControlActEvent:emptyNew()
    local self = Event:new(InteractiveControlActEvent_mt);
    return self;
end;

function InteractiveControlActEvent:new(vehicle, interactiveControlID, isActive)
  local self = InteractiveControlActEvent:emptyNew()
  self.vehicle = vehicle;
    self.interactiveControlID = interactiveControlID;
    self.isActive = isActive 
  return self;
end;

function InteractiveControlActEvent:readStream(streamId, connection)
  local id = streamReadInt32(streamId);
    self.interactiveControlID = streamReadInt8(streamId);
    self.isActive = streamReadBool(streamId);
  self.vehicle = networkGetObject(id);
  self:run(connection);
end;

function InteractiveControlActEvent:writeStream(streamId, connection)
  streamWriteInt32(streamId, networkGetObjectId(self.vehicle));    
    streamWriteInt8(streamId, self.interactiveControlID);
    streamWriteBool(streamId, self.isActive);
end;

function InteractiveControlActEvent:run(connection)
    self.vehicle:setObjectActive(self.interactiveControlID, self.isActive, true);
    if not connection:getIsServer() then
        g_server:broadcastEvent(InteractiveControlActEvent:new(self.vehicle, self.interactiveControlID, self.isActive), nil, connection, self.vehicle);
    end;
end;

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


