-- passenger.lua 
-- @authors: patar, rafftnix
-- @date patar: unknown, rafftnix: 29.9.2013

Passenger = {};

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

function Passenger:load(savegame)

    -- additional functions
    self.enterAsPassenger = SpecializationUtil.callSpecializationsFunction("enterAsPassenger");
    self.leaveAsPassenger = SpecializationUtil.callSpecializationsFunction("leaveAsPassenger");
    self.removePassenger = SpecializationUtil.callSpecializationsFunction("removePassenger");
    self.getPlayerInRange = Passenger.getPlayerInRange;
    self.getFreePlace = Passenger.getFreePlace;
    self.getOwnPlace = Passenger.getOwnPlace;
    
    -- intialise places
    self.places = {};
    local i = 1;
    while true do
        local entry = {};
        local key = string.format("vehicle.passenger.place(%d)", i-1);
        if not hasXMLProperty(xmlFile, key) then
            break;
        end;
        local camera = VehicleCamera:new(self);
        camera:loadFromXML(xmlFile, key);
        entry.cam = camera;
        entry.passengerNode = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#passengerIndex"));
        entry.nickNameRenderNode = Utils.indexToObject(self.components, getXMLString(xmlFile, key.."#passengerNickNameRenderNode"));
        entry.isUsed = false;
        
        if entry.cam == nil then
            print("Passengermod - Error: wrong camIndex on place " .. i);
            break;
        end
        
        if entry.passengerNode == nil then
            print("Passengermod - Error: wrong passengerIndex on place "..i);
        else
            setVisibility(entry.passengerNode, false);
        end;
        
        local x, y, z = getRotation(entry.cam.cameraNode);
        entry.camOrigX = x;
        entry.camOrigY = y;
        entry.camOrigZ = z;        
        
        entry.exitNode = Utils.indexToObject(self.components, getXMLString(xmlFile, key .. "#exitPointIndex"));
        if entry.exitNode == nil then
            entry.exitNode = self.exitPoint;
            if entry.exitNode == nil then
                print("Passenger: vehicle has no passenger exit point or driver exit point but it needs one");
            end;
        end;
        
        table.insert(self.places, entry);
        i = i + 1;
    end;
end;

function Passenger:readStream(streamId, connection)
    for i=1, streamReadInt8(streamId) do
        local isUsed = streamReadBool(streamId);
        if isUsed then
            self:enterAsPassenger(networkGetObject(streamReadInt32(streamId)), i, true);
        end;
    end;
end;

function Passenger:writeStream(streamId, connection)
    streamWriteInt8(streamId, table.getn(self.places));
    for i = 1, table.getn(self.places) do
        streamWriteBool(streamId, self.places[i].isUsed);
        if self.places[i].isUsed then
            streamWriteInt32(streamId, networkGetObjectId(self.places[i].sittingPlayer));
        end;
    end;
end;


function Passenger:delete()
    if self:getOwnPlace() ~= nil then
        self:removePassenger(self:getOwnPlace());
    end;
end;

function Passenger:mouseEvent(posX, posY, isDown, isUp, button)

end;

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

function Passenger:update(dt)
    if g_gui.currentGui == nil then
        if self:getPlayerInRange() and self:getOwnPlace() == nil then
            g_currentMission:addHelpButtonText(g_i18n:getText("enterAsPassenger"), InputBinding.CAMERA_SWITCH); 
            if InputBinding.hasEvent(InputBinding.CAMERA_SWITCH) then
                self:enterAsPassenger(g_currentMission.player, self:getFreePlace());
            end;
        elseif self:getOwnPlace() ~= nil then
            g_currentMission:addHelpButtonText(g_i18n:getText("exitAsPassenger"), InputBinding.CAMERA_SWITCH);
            if InputBinding.hasEvent(InputBinding.CAMERA_SWITCH) then
                self:leaveAsPassenger(g_currentMission.player, self:getOwnPlace());    
            end;
        end;
    end;
    
    if self:getOwnPlace() ~= nil then
        self.places[self:getOwnPlace()].cam:update(dt);
    end;

    -- render the passengers nicknames
    for i = 1, table.getn(self.places) do 
        local player = self.places[i].sittingPlayer;
        if self.places[i].isUsed and player ~= nil and g_gui.currentGui == nil then
            local nickNameRenderNode = self.places[i].nickNameRenderNode;
            local x,y,z = getWorldTranslation(nickNameRenderNode);
            local x1,y1,z1 = getWorldTranslation(getCamera());
            local dist = Utils.vector3Length(x-x1,y-y1,z-z1);
            if dist <= 50 then
                local sx,sy,sz = project(x,y,z);
            end;
        end;
    end;
end;

function printContent(content, depth)
    if depth == nil then
        depth = 0;
    end;
        
    if depth < 5 then
        for k, v in pairs(content) do
            local space = "";
            for a=1, depth do
                space = space .. "   ";
            end;

            print(space .. "k: " .. tostring(k));
            
            if type(k) == "table" then
                printContent(k, depth+1)
            end;
            
            print(space .. "v: " .. tostring(v));
            
            if type(v) == "table" then
                printContent(v, depth+1)
            end;
        end;
    end;
end;

function Passenger:updateTick(dt)
    for i = 1, table.getn(self.places) do 
        if self.places[i].isUsed and self.places[i].sittingPlayer ~= nil then
            if not self.places[i].sittingPlayer.deleteDonePassengerSkript then
                local x, y, z = getWorldTranslation(self.places[i].exitNode);
                self.places[i].sittingPlayer.walkDistance = 0;
                self.places[i].sittingPlayer.currentWalkStep = 0;
                setTranslation(self.places[i].sittingPlayer.rootNode, x, y + 1.75, z);
            elseif self.isServer then
                self:removePassenger(i);
            end;
        end;
    end;
end;

function Passenger:draw()

end;

function Passenger:onEnter(isControlling)
    if isControlling and self:getOwnPlace() ~= nil then
        self:removePassenger(self:getOwnPlace());
    end;
end;

function Passenger:onLeave()

end;

function Passenger:getPlayerInRange()
    if g_currentMission.player ~= nil then
        local isPlayerInRange = false;
        local place = self:getFreePlace();
        if place ~= nil then
            local px, py, pz = getWorldTranslation(self.places[place].exitNode);
            local vx, vy, vz = getWorldTranslation(g_currentMission.player.rootNode);
            local distance = Utils.vector3Length(px-vx, py-vy, pz-vz);
            if distance < 2 and place ~= nil and g_currentMission.controlledVehicle == nil then        
                return true;
            end;
        end;
    end;
    return false;
end;

function Passenger:getFreePlace() 
    for i = 1, table.getn(self.places) do
        if not self.places[i].isUsed then
            return i;
        end;
    end;
end;

function Passenger:getOwnPlace()
    if g_currentMission.passengerHoldingVehicle == self then
        return g_currentMission.passengerPlace;
    end;
end;

function Passenger:enterAsPassenger(passenger, place, noEventSend)
    if table.getn(g_currentMission.playersLoading) == 0 then
        EnterAsPassengerEvent.sendEvent(self, passenger, place, noEventSend);

        self.places[place].isUsed = true;
        self.places[place].sittingPlayer = passenger;
        setVisibility(passenger.graphicsRootNode, false);
        passenger.walkingSpeed = 0;
        passenger.isFrozen = true;
        passenger.controllerNameBackup = passenger.controllerName;
        passenger.controllerName = "";
            
        if passenger == g_currentMission.player then
            self.places[place].cam:onActivate();
            g_currentMission.passengerPlace = place;
            g_currentMission.passengerHoldingVehicle = self;
            g_currentMission.hasSpecialCamera = true;
            setVisibility(passenger.graphicsRootNode, false);
        else
            if self.places[place].passengerNode ~= nil then
                setVisibility(self.places[place].passengerNode, true)
            end;
            setVisibility(passenger.meshThirdPerson, false);
        end;
    end;
end;

function Passenger:leaveAsPassenger(passenger, place, noEventSend)
    LeaveAsPassengerEvent.sendEvent(self, passenger, place, noEventSend);
    
    self.places[place].isUsed = false;
    self.places[place].sittingPlayer = nil;
    
    if self.places[place].passengerNode ~= nil then
        setVisibility(self.places[place].passengerNode, false)
    end;
    passenger.walkingSpeed = 0.004;
    passenger.isFrozen = false;
    passenger.controllerName = passenger.controllerNameBackup;
    
    if passenger == g_currentMission.player then
        g_currentMission.passengerPlace = nil;
        self.places[place].cam:onDeactivate();
        g_currentMission.hasSpecialCamera = false;
        g_currentMission.passengerHoldingVehicle = nil;
    else
        setVisibility(passenger.meshThirdPerson, true);
    end;
end;

function Passenger:removePassenger(place, noEventSend)
    RemovePassengerEvent.sendEvent(self, place, noEventSend);
    
    if self.places[place].sittingPlayer == g_currentMission.player then
        g_currentMission.passengerPlace = nil;
        g_currentMission.passengerHoldingVehicle = nil;
    elseif self.places[place].sittingPlayer ~= nil and not self.places[place].sittingPlayer.deleteDonePassengerSkript then
        setVisibility(self.places[place].sittingPlayer.meshNode, true);    
    end;
    
    self.places[place].sittingPlayer.controllerName = self.places[place].sittingPlayer.controllerNameBackup;
    self.places[place].isUsed = false;
    self.places[place].sittingPlayer.walkingSpeed = 0.004;
    self.places[place].sittingPlayer.isFrozen = false;
    self.places[place].sittingPlayer = nil;

    if self.places[place].passengerNode ~= nil then
        setVisibility(self.places[place].passengerNode, false)
    end;
end;

local oldPlayerDelete = Player.delete;
Player.delete = function(self)
    self.deleteDonePassengerSkript = true;
    oldPlayerDelete(self);
end;

EnterAsPassengerEvent = {};
EnterAsPassengerEvent_mt = Class(EnterAsPassengerEvent, Event);

InitEventClass(EnterAsPassengerEvent, "EnterAsPassengerEvent");

function EnterAsPassengerEvent:emptyNew()
    local self = Event:new(EnterAsPassengerEvent_mt);
    self.className="EnterAsPassengerEventEvent";
    return self;
end;

function EnterAsPassengerEvent:new(object, passenger, place)
    local self = EnterAsPassengerEvent:emptyNew()
    self.object = object;
    self.passenger = passenger;
    self.place = place;
    return self;
end;

function EnterAsPassengerEvent:readStream(streamId, connection)
    self.object = networkGetObject(streamReadInt32(streamId));
    self.passenger = networkGetObject(streamReadInt32(streamId));
    self.place = streamReadInt8(streamId);
    self:run(connection);
end;

function EnterAsPassengerEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.object));
    streamWriteInt32(streamId, networkGetObjectId(self.passenger));
    streamWriteInt8(streamId, self.place);
end;

function EnterAsPassengerEvent:run(connection)
    self.object:enterAsPassenger(self.passenger, self.place, true);
    if not connection:getIsServer() then
        g_server:broadcastEvent(EnterAsPassengerEvent:new(self.object, self.passenger, self.place), nil, connection, self.object);
    end;
end;

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


LeaveAsPassengerEvent = {};
LeaveAsPassengerEvent_mt = Class(LeaveAsPassengerEvent, Event);

InitEventClass(LeaveAsPassengerEvent, "LeaveAsPassengerEvent");

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

function LeaveAsPassengerEvent:new(object, passenger, place)
    local self = LeaveAsPassengerEvent:emptyNew()
    self.object = object;
    self.passenger = passenger;
    self.place = place;
    return self;
end;

function LeaveAsPassengerEvent:readStream(streamId, connection)
    self.object = networkGetObject(streamReadInt32(streamId));
    self.passenger = networkGetObject(streamReadInt32(streamId));
    self.place = streamReadInt8(streamId);
    
    self:run(connection);
end;

function LeaveAsPassengerEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.object));
    streamWriteInt32(streamId, networkGetObjectId(self.passenger));
    streamWriteInt8(streamId, self.place);
end;

function LeaveAsPassengerEvent:run(connection)
    self.object:leaveAsPassenger(self.passenger, self.place, true);
    if not connection:getIsServer() then
        g_server:broadcastEvent(LeaveAsPassengerEvent:new(self.object, self.passenger, self.place), nil, connection, self.object);
    end;
end;

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

RemovePassengerEvent = {};
RemovePassengerEvent_mt = Class(RemovePassengerEvent, Event);

InitEventClass(RemovePassengerEvent, "RemovePassengerEvent");

function RemovePassengerEvent:emptyNew()
    local self = Event:new(RemovePassengerEvent_mt);
    self.className="RemovePassengerEventEvent";
    return self;
end;

function RemovePassengerEvent:new(object, place)
    local self = RemovePassengerEvent:emptyNew()
    self.object = object;
    self.place = place;
    return self;
end;

function RemovePassengerEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId);
    self.place = streamReadInt8(streamId);
    self.object = networkGetObject(id);
    self:run(connection);
end;

function RemovePassengerEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.object));
    streamWriteInt8(streamId, self.place);
end;

function RemovePassengerEvent:run(connection)
    self.object:removePassenger(self.place, true);
    if not connection:getIsServer() then
        g_server:broadcastEvent(RemovePassengerEvent:new(self.object, self.place), nil, connection, self.object);
    end;
end;

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

EventListener = {};

function EventListener:loadMap(name)
    
end;

function EventListener:deleteMap()
    
end;

function EventListener:keyEvent(unicode, sym, modifier, isDown)

end;

function EventListener:mouseEvent(posX, posY, isDown, isUp, button)
    local vehicle = g_currentMission.passengerHoldingVehicle;
    if vehicle ~= nil and vehicle.getOwnPlace ~= nil and vehicle:getOwnPlace() ~= nil then
        vehicle.places[vehicle:getOwnPlace()].cam:mouseEvent(posX, posY, isDown, isUp, button);
    end;
end;

function EventListener:update(dt)

end;

function EventListener:draw()

end;

addModEventListener(EventListener);