//=============================================================================
// Emitter actor class.
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
//=============================================================================
class Emitter extends Actor
	native
	placeable;

var()	editconst const	ParticleSystemComponent ParticleSystemComponent;
var()	editconst const DynamicLightEnvironmentComponent LightEnvironment;
var						bool					bDestroyOnSystemFinish;

var()					bool					bPostUpdateTickGroup;

/** used to update status of toggleable level placed emitters on clients */
var repnotify bool bCurrentlyActive;

`if(`__TW_)
/** Allows particle emitters to omit depth testing (by setting to false) */
var transient bool bDepthTestEnabled;
/** Allow this emitter to be rendered in the depth pass and be used as an occluder */
var(Rendering) const bool bUseAsOccluder;
`endif

struct CheckpointRecord
{
	var bool bIsActive;
};

replication
{
	if (bNoDelete)
		bCurrentlyActive;
}

cpptext
{
	void SetTemplate(UParticleSystem* NewTemplate, UBOOL bDestroyOnFinish=false);
	void AutoPopulateInstanceProperties();

	// AActor interface.
	virtual void Spawned();
	virtual void PostBeginPlay();
	/**
	 *	ticks the actor
	 *	@param	DeltaTime	The time slice of this tick
	 *	@param	TickType	The type of tick that is happening
	 *
	 *	@return	TRUE if the actor was ticked, FALSE if it was aborted (e.g. because it's in stasis)
	 */
	virtual UBOOL Tick( FLOAT DeltaTime, enum ELevelTick TickType );

	/**
	 * Function that gets called from within Map_Check to allow this actor to check itself
	 * for any potential errors and register them with map check dialog.
	 */
#if WITH_EDITOR
	virtual void CheckForErrors();
#endif

#if USE_GAMEPLAY_PROFILER
    /** 
     * This function actually does the work for the GetProfilerAssetObject and is virtual.  
     * It should only be called from GetProfilerAssetObject as GetProfilerAssetObject is safe to call on NULL object pointers
     */
	virtual UObject* GetProfilerAssetObjectInternal() const;
#endif

	/**
	 * This will return detail info about this specific object. (e.g. AudioComponent will return the name of the cue,
	 * ParticleSystemComponent will return the name of the ParticleSystem)  The idea here is that in many places
	 * you have a component of interest but what you really want is some characteristic that you can use to track
	 * down where it came from.
	 *
	 */
	virtual FString GetDetailedInfoInternal() const;

	/**
	 *	Called to reset the emitter actor in the level.
	 *	Intended for use in editor only
	 */
	void ResetInLevel();

#if __TW_
	virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent);
#endif 
}

native noexport event SetTemplate(ParticleSystem NewTemplate, optional bool bDestroyOnFinish);

simulated event PostBeginPlay()
{
	Super.PostBeginPlay();

	// Let them die quickly on a dedicated server
 	if (WorldInfo.NetMode == NM_DedicatedServer && (RemoteRole == ROLE_None || bNetTemporary))
 	{
 		LifeSpan = 0.2;
`if(`__TW_PERFORMANCE_)
		// Lifespan won't do us much good for NoDelete actors!
		if ( bNoDelete )
		{
			SetTickIsDisabled(true);
		}
`endif
 	}

	// Set Notification Delegate
	if (ParticleSystemComponent != None)
	{
		ParticleSystemComponent.OnSystemFinished = OnParticleSystemFinished;
		bCurrentlyActive = ParticleSystemComponent.bAutoActivate;

`if(`__TW_)
		// Allow certain emitters to be rendered in the depth pass and be used as occluders
		ParticleSystemComponent.bUseAsOccluder = bUseAsOccluder;
		ReattachComponent(ParticleSystemComponent);
`endif
	}
}

simulated event ReplicatedEvent(name VarName)
{
	if (VarName == 'bCurrentlyActive')
	{
		ParticleSystemComponent.SetActive(bCurrentlyActive);
	}
	else
	{
		Super.ReplicatedEvent(VarName);
	}
}

simulated function OnParticleSystemFinished(ParticleSystemComponent FinishedComponent)
{
	if (bDestroyOnSystemFinish)
	{
		Lifespan = 0.0001f;
	}
	bCurrentlyActive = false;
}

/**
 * Handling Toggle event from Kismet.
 */
function OnToggle(SeqAct_Toggle action)
{
	// Turn ON
	if (action.InputLinks[0].bHasImpulse)
	{
		ParticleSystemComponent.ActivateSystem();
		bCurrentlyActive = TRUE;
	}
	// Turn OFF
	else if (action.InputLinks[1].bHasImpulse)
	{
		ParticleSystemComponent.DeactivateSystem();
		bCurrentlyActive = FALSE;
	}
	// Toggle
	else if (action.InputLinks[2].bHasImpulse)
	{
		// If spawning is suppressed or we aren't turned on at all, activate.
		if (ParticleSystemComponent.bSuppressSpawning || !bCurrentlyActive)
		{
			ParticleSystemComponent.ActivateSystem();
			bCurrentlyActive = TRUE;
		}
		else
		{
			ParticleSystemComponent.DeactivateSystem();
			bCurrentlyActive = FALSE;
		}
	}
	ParticleSystemComponent.LastRenderTime = WorldInfo.TimeSeconds;
	ForceNetRelevant();

	if (RemoteRole != ROLE_None)
	{
		// force replicate flag if necessary
		SetForcedInitialReplicatedProperty(Property'Engine.Emitter.bCurrentlyActive', (bCurrentlyActive == default.bCurrentlyActive));
	}
}

/**
 * Handling ParticleEventGenerator event from Kismet.
 * - Does nothing... just here to stop Kismet from complaining
 */
function OnParticleEventGenerator(SeqAct_ParticleEventGenerator action)
{
}

simulated function ShutDown()
{
	Super.ShutDown();

	bCurrentlyActive = false;
}

simulated function SetFloatParameter(name ParameterName, float Param)
{
	if (ParticleSystemComponent != none)
		ParticleSystemComponent.SetFloatParameter(ParameterName, Param);
	else
		`log("Warning: Attempting to set a parameter on "$self$" when the PSC does not exist");
}

simulated function SetVectorParameter(name ParameterName, vector Param)
{
	if (ParticleSystemComponent != none)
		ParticleSystemComponent.SetVectorParameter(ParameterName, Param);
	else
		`log("Warning: Attempting to set a parameter on "$self$" when the PSC does not exist");
}

simulated function SetColorParameter(name ParameterName, color Param)
{
	if (ParticleSystemComponent != none)
		ParticleSystemComponent.SetColorParameter(ParameterName, Param);
	else
		`log("Warning: Attempting to set a parameter on "$self$" when the PSC does not exist");
}

simulated function SetExtColorParameter(name ParameterName, byte Red, byte Green, byte Blue, byte Alpha)
{
	local color c;

	if (ParticleSystemComponent != none)
	{
		c.r = Red;
		c.g = Green;
		c.b = Blue;
		c.a = Alpha;
		ParticleSystemComponent.SetColorParameter(ParameterName, C);
	}
	else
		`log("Warning: Attempting to set a parameter on "$self$" when the PSC does not exist");
}


simulated function SetActorParameter(name ParameterName, actor Param)
{
	if (ParticleSystemComponent != none)
		ParticleSystemComponent.SetActorParameter(ParameterName, Param);
	else
		`log("Warning: Attempting to set a parameter on "$self$" when the PSC does not exist");
}

/**
 * Kismet handler for setting particle instance parameters.
 */
simulated function OnSetParticleSysParam(SeqAct_SetParticleSysParam Action)
{
	local int Idx, ParamIdx;
	if (ParticleSystemComponent != None &&
		Action.InstanceParameters.Length > 0)
	{
		for (Idx = 0; Idx < Action.InstanceParameters.Length; Idx++)
		{
			if (Action.InstanceParameters[Idx].ParamType != PSPT_None)
			{
				// look for an existing entry
				ParamIdx = ParticleSystemComponent.InstanceParameters.Find('Name',Action.InstanceParameters[Idx].Name);
				// create one if necessary
				if (ParamIdx == -1)
				{
					ParamIdx = ParticleSystemComponent.InstanceParameters.Length;
					ParticleSystemComponent.InstanceParameters.Length = ParamIdx + 1;
				}
				// update the instance parm
				ParticleSystemComponent.InstanceParameters[ParamIdx] = Action.InstanceParameters[Idx];
				if (Action.bOverrideScalar)
				{
					ParticleSystemComponent.InstanceParameters[ParamIdx].Scalar = Action.ScalarValue;
				}
			}
		}
	}
}

function bool ShouldSaveForCheckpoint()
{
	return (bNoDelete && RemoteRole != ROLE_None);
}

function CreateCheckpointRecord(out CheckpointRecord Record)
{
	Record.bIsActive = bCurrentlyActive;
}

function ApplyCheckpointRecord(const out CheckpointRecord Record)
{
	bCurrentlyActive = Record.bIsActive;
	if (bCurrentlyActive)
	{
		ParticleSystemComponent.ActivateSystem();
	}
	else
	{
		ParticleSystemComponent.DeactivateSystem();
	}
	ForceNetRelevant();
}

/** Function used to have the emitter hide itself and put itself into stasis **/
simulated function HideSelf();


defaultproperties
{
	// Visual things should be ticked in parallel with physics
	TickGroup=TG_DuringAsyncWork

	Begin Object Class=SpriteComponent Name=Sprite
		Sprite=Texture2D'EditorResources.S_Emitter'
		HiddenGame=True
		AlwaysLoadOnClient=False
		AlwaysLoadOnServer=False
		bIsScreenSizeScaled=True
		ScreenSize=0.0025
		SpriteCategoryName="Effects"
	End Object
	Components.Add(Sprite)

	Begin Object Class=ParticleSystemComponent Name=ParticleSystemComponent0
		SecondsBeforeInactive=1
	End Object
	ParticleSystemComponent=ParticleSystemComponent0
	Components.Add(ParticleSystemComponent0)

	Begin Object Class=ArrowComponent Name=ArrowComponent0
		ArrowColor=(R=0,G=255,B=128)
		ArrowSize=1.5
		AlwaysLoadOnClient=False
		AlwaysLoadOnServer=False
		bTreatAsASprite=True
		SpriteCategoryName="Effects"
	End Object
	Components.Add(ArrowComponent0)

	bEdShouldSnap=true
	bHardAttach=true
	bGameRelevant=true
	bNoDelete=true

`if(`__TW_)
	bDepthTestEnabled=true
`endif

	SupportedEvents.Add(class'SeqEvent_ParticleEvent')
}
