----------------------------------------------------------------------------------------------------
--
-- All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
-- its licensors.
--
-- For complete copyright and license terms please see the LICENSE at the root of this
-- distribution (the "License"). All use of this software is governed by the License,
-- or, if provided, by the license below or the license accompanying this file. Do not
-- remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--
-- Original file Copyright Crytek GMBH or its affiliates, used under license.
--
--
--	Description: Network-ready Area Trigger
--
----------------------------------------------------------------------------------------------------
AreaTrigger =
{
	Properties =
	{
		bEnabled = 1,
		bTriggerOnce = 0,
		bOnlyPlayers = 1,
		bOnlyLocalPlayer= 0,
		esFactionFilter = "",
		ScriptCommand = "",
		PlaySequence = "",
		bInVehicleOnly = 0,
		MultiplayerOptions =
		{
			bNetworked = 0,
			bPerPlayer = 0,
		},
	},

	Client = {},
	Server = {},

	Editor =
	{
		Model = "Editor/Objects/T.cgf",
		Icon = "AreaTrigger.bmp",
		IsScalable = false;
		IsRotatable = false;
	},
}


Net.Expose
{
	Class = AreaTrigger,
	ClientMethods = 
	{
		ClEnter = { RELIABLE_ORDERED, PRE_ATTACH, ENTITYID, INT8 },
		ClLeave = { RELIABLE_ORDERED, PRE_ATTACH, ENTITYID, INT8 },
	},
	ServerMethods = {},
	ServerProperties = {}
}


function AreaTrigger:OnPropertyChange()
	self:OnReset();
end


function AreaTrigger:OnReset()
	self.enabled = nil;
	self.triggerOnce = tonumber(self.Properties.bTriggerOnce)~=0;
	self.localOnly = self.Properties.MultiplayerOptions.bNetworked==0;
	self.perPlayer = tonumber(self.Properties.MultiplayerOptions.bPerPlayer)~=0;

	self.isServer=CryAction.IsServer();
	self.isClient=CryAction.IsClient();

	self.inside={};
	self.insideCount=0;

	if (not self.localOnly) then
		self.triggeredPP={};
		self.triggeredOncePP={};
	else
		self:SetFlags(ENTITY_FLAG_CLIENT_ONLY,0);
	end

	self.triggeredOnce=nil;
	self.triggered=nil;

	self:Enable(tonumber(self.Properties.bEnabled)~=0);
	self:ActivateOutput("NrOfEntitiesInside", 0);
end


function AreaTrigger:Enable(enable)
	self.enabled=enable;
	self:RegisterForAreaEvents(enable and 1 or 0);
end


function AreaTrigger:OnSpawn()
	self:OnReset();
end


function AreaTrigger:OnSave(props)
	props.enabled = self.enabled;
	props.triggered = self.triggered;
	props.triggeredOnce = self.triggeredOnce;
end


function AreaTrigger:OnLoad(props)
	self:OnReset();
	self.enabled = props.enabled;
	self.triggered = props.triggered;
	self.triggeredOnce = props.triggeredOnce;
end


function AreaTrigger:CanTrigger(entityId)

	local entity=System.GetEntity(entityId);

	if (not entity) then return; end;

	local Properties = self.Properties;

	local isAPlayer = ActorSystem.IsPlayer(entity.id);
	if (Properties.bOnlyPlayers ~= 0 and not(isAPlayer)) then
		return false;
	end

	if (Properties.bOnlyLocalPlayer ~= 0 and entity ~= g_localActor) then
		return false;
	end

	if (Properties.bInVehicleOnly ~= 0 and not entity.vehicleId) then
		return false;
	end

	if (Properties.esFactionFilter ~= "") then
		local faction = AI.GetFactionOf(entity.id) or "";
		if (faction ~= Properties.esFactionFilter) then
			return false
		end
	end

	return true;
end


function AreaTrigger:Trigger(entityId, enter)
	self:ActivateOutput("NrOfEntitiesInside", self.insideCount);
	self:ActivateOutput("Sender", entityId or NULL_ENTITY);
	self:ActivateOutput("Faction", AI.GetFactionOf(entityId or NULL_ENTITY) or "");

	if (enter) then
		if(self.Properties.ScriptCommand and self.Properties.ScriptCommand~="")then
			local f = loadstring(self.Properties.ScriptCommand);
			if (f~=nil) then
				f();
			end
		end

		if(self.Properties.PlaySequence~="")then
			Movie.PlaySequence(self.Properties.PlaySequence);
		end
	end
end


function AreaTrigger:EnteredArea(entity, areaId)
	if (not self:CanTrigger(entity.id, areaId)) then
		return;
	end

	self.inside[entity.id]=true;
	self.insideCount=self.insideCount+1;

	self:Event_Enter(entity.id);
end


function AreaTrigger:LeftArea(entity, areaId)
	if (not self:CanTrigger(entity.id, areaId)) then
		return;
	end

	self.inside[entity.id]=nil;
	self.insideCount=self.insideCount-1;

	self:Event_Leave(entity.id);
end


function AreaTrigger.Server:OnEnterArea(entity, areaId)
	if (self:CanTrigger(entity.id)) then
		self:EnteredArea(entity, areaId);
	end
end


function AreaTrigger.Server:OnLeaveArea(entity, areaId)
	self:LeftArea(entity, areaId);
end


function AreaTrigger.Client:OnEnterArea(entity, areaId)
	if (not self:CanTrigger(entity.id)) then return; end;

	if (not self.localOnly or self.isServer) then return; end;

	self:EnteredArea(entity, areaId);
end


function AreaTrigger.Client:OnLeaveArea(entity, areaId)
	if (not self.localOnly or self.isServer) then return; end;

	self:LeftArea(entity, areaId);
end


function AreaTrigger:Event_Enter(entityId)
	if (not self.enabled) then return; end; -- TODO: might need a self.active here
	if (self.triggerOnce) then
		if (self.localOnly) then
			if (self.triggeredOnce) then
				return;
			end
		elseif (self.perPlayer and self.triggeredOncePP[entityId]) then	-- TODO: will need to skip this for non-player entities
			return;
		elseif (not self.perPlayer and self.triggeredOnce) then
			return;
		end
	end

	self.triggered=true;
	self.triggeredOnce=true;

	if (not self.localOnly and entityId) then
		self.triggeredPP[entityId]=true;
		self.triggeredOncePP[entityId]=true;
	end

	self:Trigger(entityId, true);

	--BroadcastEvent(self, "Enter");
	self:ActivateOutput("Enter", entityId);

	if (not self.localOnly and self.isServer) then
		if (self.isClient) then
			self.otherClients:ClEnter(g_localChannelId, entityId or NULL_ENTITY, self.insideCount);
		else
			self.allClients:ClEnter(entityId or NULL_ENTITY, self.insideCount);
		end
	end
end


function AreaTrigger:Event_Leave(entityId)
	if (not self.enabled) then return; end;
	if (self.localOnly and not self.triggered) then return; end;

	if (self.perPlayer) then
		if (not self.localOnly and entityId) then
			if (not self.triggeredPP[entityId]) then return; end;
		end
	else
		if (not self.triggered) then return; end;
	end

	--only disable triggered when all players are gone
	if(self.insideCount == 0) then
		self.triggered=nil;
	end

	if (not self.localOnly and entityId and self.insideCount == 0) then
		self.triggeredPP[entityId]=nil;
	end

	self:Trigger(entityId, false);

	--BroadcastEvent(self, "Leave");
	self:ActivateOutput("Leave", entityId or NULL_ENTITY);

	if (not self.localOnly and self.isServer) then
		if (self.isClient) then
			self.otherClients:ClLeave(g_localChannelId, entityId or NULL_ENTITY, self.insideCount);
		else
			self.allClients:ClLeave(entityId or NULL_ENTITY, self.insideCount);
		end
	end
end


function AreaTrigger.Client:ClEnter(entityId, insideCount)
	self.insideCount = insideCount;
	self.inside[entityId] = true;

	self:Trigger(entityId, true);

	--BroadcastEvent(self, "Enter");
	self:ActivateOutput("Enter", entityId);
end


function AreaTrigger.Client:ClLeave(entityId, inside)
	self.insideCount = inside;
	self.inside[entityId] = nil;

	self:Trigger(entityId, false);

	self:ActivateOutput("Sender", entityId);
	self:ActivateOutput("NrOfEntitiesInside", inside);

	--BroadcastEvent(self, "Leave");
	self:ActivateOutput("Leave", entityId);
end


function AreaTrigger:Event_Enable()
	self:Enable(true);

	local entityIdInside = next(self.inside);
	if (entityIdInside) then
	  self:Event_Enter( entityIdInside );
	end;
	self:ActivateOutput("NrOfEntitiesInside", self.insideCount);

	BroadcastEvent(self, "Enable");
end


function AreaTrigger:Event_Disable()
	self:Enable(false);

	BroadcastEvent(self, "Disable");
end


AreaTrigger.FlowEvents =
{
	Inputs =
	{
		Disable = { AreaTrigger.Event_Disable, "bool" },
		Enable = { AreaTrigger.Event_Enable, "bool" },
		Enter = { AreaTrigger.Event_Enter, "bool" },
		Leave = { AreaTrigger.Event_Leave, "bool" },
	},
	Outputs =
	{
		Disable = "bool",
		Enable = "bool",
		Enter = "entity",
		Leave = "entity",
		NrOfEntitiesInside = "int",
		Sender = "entity",
		Faction = "string",
	},
}