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

Passenger = {};

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

function Passenger:load(xmlFile)

	-- 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);
				if sz <= 1 then
					setTextBold(false);
					setTextAlignment(RenderText.ALIGN_CENTER);

					setTextColor(0.5, 1.0, 0.5, 1.0);
					renderText(sx, sy, 0.02, player.controllerNameBackup);

					setTextAlignment(RenderText.ALIGN_LEFT);
				end;
			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);