--
-- Passenger
-- Specialization for Passenger
--
-- @author  JoXXer
-- @date  26/11/11
-- @history	v1.0 - Initial implementation
--			v1.1 - 21/01/12 - Added support for more players
--

Passenger = {};


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

function Passenger:load(xmlFile)

	--local Passenger_mt = Class(Passenger, PassengerPlayer);
	-- Set passenger function
	self.setPassenger = SpecializationUtil.callSpecializationsFunction("setPassenger");

	self.numOfPassengers = Utils.getNoNil(getXMLInt(xmlFile, "vehicle.passengers#count"), 0);

	self.passengers = {};

    for i=1, self.numOfPassengers do
        local objname = string.format("vehicle.passengers.passenger" .. "%d", i);

		-- Nickname rendernode
		local nicknameRenderNode = Utils.indexToObject(self.components, getXMLString(xmlFile, objname .. "#nicknameRenderNode"));
		local nicknameRenderNodeOffset = {0,0,0};

		-- The passenger. He is the passenger, he rides and he rides.
		local player = nil;

		-- The passengers name
		local name = "";

		-- Variable to check if user is chatting, to prevent exit on InputBinding.ENTER-event
		local isChatting = false;

		-- To check if player is in range and can enter
		local inRangeOfVehicle = false;

		-- Passengers character node
		local characterNode = Utils.indexToObject(self.components, getXMLString(xmlFile, objname .. "#characterNode"));
		-- Set the characterNode to invisible
		if characterNode ~= nil then
			setVisibility(characterNode, false);
		end;

		-- Passenger camera
		local camera = {};
		local passengerCamera = VehicleCamera:new(self);

		if passengerCamera:loadFromXML(xmlFile, objname) then
			table.insert(camera, passengerCamera);
		end;

		local passengerPlayer = PassengerPlayer:new(self, nil, nicknameRenderNode, nicknameRenderNodeOffset, player, name, isChatting, characterNode, camera);

		table.insert(self.passengers, passengerPlayer);
    end;

	self.inRangeOfVehicle = false;
end;

function Passenger:delete()
	for k,v in pairs(self.passengers) do
		v.camera[1]:delete();
	end;
end;

function Passenger:readStream(streamId, connection)
	for k,v in pairs(self.passengers) do
		local passengerPlayerId = streamReadInt32(streamId);

		if passengerPlayerId ~= -1 then
			v.player = networkGetObject(passengerPlayerId);
		end;

		v.name = streamReadString(streamId);
	end;
end;

function Passenger:writeStream(streamId, connection)
	for k,v in pairs(self.passengers) do
		if v.player ~= nil then
			streamWriteInt32(streamId, networkGetObjectId(v.player));
		else
			streamWriteInt32(streamId, -1);
		end;

		streamWriteString(streamId, v.name);
	end;
end;

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

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

function Passenger:update(dt)
	for k,v in pairs(self.passengers) do
		if self.inRangeOfVehicle or v.player ~= nil then
			v:update(dt);
		end;

		if v.player ~= nil then
			if v.name == g_settingsNickname then
				if InputBinding.hasEvent(InputBinding.ENTER) and not v.isChatting then
					v.camera[1]:onDeactivate();
					setCamera(v.player.cameraId);
					self:setPassenger(k, "Exited", nil);
				end;
			end;

			if g_currentMission:getIsServer() then
				if v.player ~= nil then
					if g_currentMission.connectionsToPlayer[v.player.creatorConnection] == nil then
						self:setPassenger(k, "Disconnected", nil);
					end;
				end;
			end;
			-- if player joins a vehicle, set passenger-spot not taken!
			if v.player ~= nil then
				if not v.player.isControlled and not v.player.isEntered then
					self:setPassenger(k, "ExitedToOtherVehicle", nil);
				end;
			end;
		end;
	end;
	for k,v in pairs(self.passengers) do
		if v.player == nil then
			-- Manage key events for inrange --
			if self.inRangeOfVehicle and self.isControlled and g_settingsNickname ~= self.controllerName then
				g_currentMission:addHelpButtonText(string.format(g_i18n:getText("ENTER_PASSENGERSEAT"), self.typeDesc), InputBinding.ENTER);
				if InputBinding.hasEvent(InputBinding.ENTER) then
					if v.player == nil then
						self:setPassenger(k, g_settingsNickname, g_currentMission.player);
						v.camera[1]:onActivate();
					end;
				end;
			end;
			break;
		end;
	end;
end;

function Passenger:updateTick(dt)
	if g_currentMission.player ~= nil then
		-- Getting the distance between the player and the implement
		local nearestDistance = 4.0; --max distance allowed
		local px, py, pz = getWorldTranslation(self.rootNode);
		local vx, vy, vz = getWorldTranslation(g_currentMission.player.rootNode);
		local distance = Utils.vector3Length(px-vx, py-vy, pz-vz);
		if distance < nearestDistance then
			self.inRangeOfVehicle = true;
		else
			self.inRangeOfVehicle = false;
		end;
	end;
end;

function Passenger:draw()
	for k,v in pairs(self.passengers) do
		if v.inRangeOfVehicle or v.player ~= nil then
			v:draw();
		end;
	end;
end;

function Passenger:onLeave()
end;

function Passenger:onEnter()
end;

function Passenger:setPassenger(id, passengerName, passengerPlayer, noEventSend)
	SetPassengerEvent.sendEvent(self, id, passengerName, passengerPlayer, noEventSend);

	if passengerPlayer ~= nil then
		setTranslation(passengerPlayer.rootNode, 0, 0, 0);
		self.passengers[id].name = passengerName;
	elseif passengerName == "Exited" then
		local exitX, exitY, exitZ = getWorldTranslation(self.exitPoint);
		setTranslation(self.passengers[id].player.rootNode, exitX, exitY+2, exitZ);
		self.passengers[id].name = "";
	else
		self.passengers[id].name = "";
	end;

	self.passengers[id].player = passengerPlayer;

	--self.passengers[id]:setPassenger(passengerName, passengerPlayer);
end;



--
-- PassengerPlayer Class
-- Specifies a PassengerPlayer
--
-- @author  JoXXer
-- @date  21/01/12
--

PassengerPlayer = {};

local PassengerPlayer_mt = Class(PassengerPlayer);

function PassengerPlayer:new(vehicle, customMt, nicknameRenderNode, nicknameRenderNodeOffset, player, name, isChatting, characterNode, camera)

	local instance = {};
	if customMt ~= nil then
		setmetatable(instance, customMt);
	else
		setmetatable(instance, PassengerPlayer_mt);
	end;

	instance.vehicle = vehicle;
	instance.nicknameRenderNode = nicknameRenderNode;
	instance.nicknameRenderNodeOffset = nicknameRenderNodeOffset;
	instance.player = player;
	instance.name = name;
	instance.isChatting = isChatting;
	instance.characterNode = characterNode;
	instance.camera = camera;

	return instance;
end;

function PassengerPlayer:delete()
end;

function PassengerPlayer:readStream(streamId, connection)

end;

function PassengerPlayer:writeStream(streamId, connection)

end;

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

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

function PassengerPlayer:update(dt)
	if self.player ~= nil then

		self.camera[1]:update(dt);

		if self.name == g_settingsNickname then
			if InputBinding.hasEvent(InputBinding.CHAT) and not self.isChatting then
				self.isChatting = true;
			end;
			if InputBinding.hasEvent(InputBinding.SEND_CHAT_MESSAGE) and self.isChatting then
				self.isChatting = false;
			end;

			if self.player ~= nil then
				local pcx, pcy, pcz = getRotation(self.player.cameraId);
				local prx, pry, prz = getRotation(self.player.graphicsRootNode);

				setRotation(self.camera[1].cameraNode, pcx, pry, 0);
			end;

			g_currentMission:addHelpButtonText(string.format(g_i18n:getText("EXIT_PASSENGERSEAT"), self.typeDesc), InputBinding.ENTER);
		else
			if self.characterNode ~= nil then
				-- Set the character to visible for everyone but the passenger
				if not getVisibility(self.characterNode) then
					setVisibility(self.characterNode, true);
				end;
			end;
		end;
	else
		if self.characterNode ~= nil then
			-- No passenger, characterNode should not be visible.
			if getVisibility(self.characterNode) then
				setVisibility(self.characterNode, false);
			end;
		end;
	end;
end;

function PassengerPlayer:updateTick(dt)
end;

function PassengerPlayer:draw()
	-- Renders the passenger name for everyone except the passenger
	if g_settingsNickname ~= nil then
		if self.name ~= g_settingsNickname and self.vehicle.isControlled and self.player ~= nil and g_gui.currentGui == nil then
			local x,y,z = getWorldTranslation(self.nicknameRenderNode);
			local x1,y1,z1 = getWorldTranslation(getCamera());
			local distSq = Utils.vector3LengthSq(x-x1,y-y1,z-z1);
			if distSq <= 100*100 then
				x = x + self.nicknameRenderNodeOffset[1];
				y = y + self.nicknameRenderNodeOffset[2];
				z = z + self.nicknameRenderNodeOffset[3];
				local sx,sy,sz = project(x,y,z);
				if sz <= 1 then
					setTextAlignment(RenderText.ALIGN_CENTER);
					setTextBold(false);
					setTextColor(0.0, 0.0, 0.0, 0.75);
					renderText(sx, sy-0.0015, 0.02, self.name);

					setTextColor(0.5, 1.0, 0.5, 1.0);
					renderText(sx, sy, 0.02, self.name);

					setTextAlignment(RenderText.ALIGN_LEFT);
				end;
			end;
		end;
	end;
end;

-- Passenger on enter event-class
SetPassengerEvent = {};
SetPassengerEvent_mt = Class(SetPassengerEvent, Event);

InitEventClass(SetPassengerEvent, "SetPassengerEvent");

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

function SetPassengerEvent:new(vehicle, passengerId, passengerName, passengerPlayer)
    local self = SetPassengerEvent:emptyNew()
    self.vehicle = vehicle;
	self.passengerId = passengerId;
	self.passengerName = passengerName;
	self.passengerPlayer = passengerPlayer;
    return self;
end;

function SetPassengerEvent:readStream(streamId, connection)
    local id = streamReadInt32(streamId);
	local passengerPlayerId = streamReadInt32(streamId);
	self.passengerName = streamReadString(streamId);
	self.passengerId = streamReadInt8(streamId);

    self.vehicle = networkGetObject(id);
	self.passengerPlayer = networkGetObject(passengerPlayerId);
    self:run(connection);
end;

function SetPassengerEvent:writeStream(streamId, connection)
    streamWriteInt32(streamId, networkGetObjectId(self.vehicle));
	streamWriteInt32(streamId, networkGetObjectId(self.passengerPlayer));
	streamWriteString(streamId, self.passengerName);
	streamWriteInt8(streamId, self.passengerId);
end;

function SetPassengerEvent:run(connection)
	self.vehicle:setPassenger(self.passengerId, self.passengerName, self.passengerPlayer, true);
    if not connection:getIsServer() then
        g_server:broadcastEvent(SetPassengerEvent:new(self.vehicle, self.passengerId, self.passengerName, self.passengerPlayer), nil, connection, self.vehicle);
    end;
end;

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