/**
 * Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
 */
class GameThirdPersonCameraMode extends Object
	config(Camera)
	native(Camera);

/**
 * Contains all the information needed to define a camera
 * mode.
 */

/** Ref to the camera object that owns this mode object. */
var transient GameThirdPersonCamera	ThirdPersonCam;

/** FOV for camera to use */
var() const config float			FOVAngle;

/** Blend Time to and from this view mode */
var() float					        BlendTime;

/**
 * True if, while in this mode, the camera should be tied to the viewtarget rotation.
 * This is typical for the normal walking-around camera, since the controls rotate the controller
 * and the camera follows.  This can be false if you want free control of the camera, independent
 * of the viewtarget's orient -- we use this for vehicles.  Note that if this is false,
 */
var() const protected bool bLockedToViewTarget;

/**
 * True if, while in this mode, looking around should be directly mapped to stick position
 * as opposed to relative to previous camera positions.
 */
var() const protected bool bDirectLook;

/**
 * True if, while in this mode, the camera should interpolate towards a following position
 * in relation to the target and it's motion.  Ignored if bLockedToViewTarget is set to true.
 */
var() const protected bool bFollowTarget;

/*
 * How fast the camera should track to follow behind the viewtarget.  0.f for no following.
 * Only used if bLockedToViewTarget is FALSE
 */
var() const protected float		FollowingInterpSpeed_Pitch;
var() const protected float		FollowingInterpSpeed_Yaw;
var() const protected float		FollowingInterpSpeed_Roll;

/** Actual following interp speed gets scaled from FollowingInterpSpeed to zero between velocities of this value and zero. */
var() const protected float		FollowingCameraVelThreshold;


/** True means camera will attempt to smoothly interpolate to its new position.  False will snap it to it's new position. */
var() bool		bInterpLocation;
/** Controls interpolation speed of location for camera origin.  Ignored if bInterpLocation is false. */
var() protected float	    OriginLocInterpSpeed;

/** 
 *  This is a special case of origin location interpolation. If true, interpolaton will be done on each axis independently, with the specified speeds.
 *  Ignored if bInterpLocation is false. 
 */
var() protected bool		bUsePerAxisOriginLocInterp;
var() protected vector		PerAxisOriginLocInterpSpeed;

/** True means camera will attempt to smoothly interpolate to its new rotation.  False will snap it to it's new rotation. */
var() bool		bInterpRotation;
/** Controls interpolation speed of rotation for the camera origin.  Ignored if bInterpRotation is false. */
var() protected float		OriginRotInterpSpeed;
/** Whether rotation interpolation happens at constant speed or not */
var() bool		bRotInterpSpeedConstant;


/** Adjustment vector to apply to camera view offset when target is strafing to the left */
var() const protected vector	StrafeLeftAdjustment;
/** Adjustment vector to apply to camera view offset when target is strafing to the right */
var() const protected vector	StrafeRightAdjustment;
/** Velocity at (and above) which the full adjustment should be applied. */
var() const protected float		StrafeOffsetScalingThreshold;
/** Interpolation speed for interpolating to a NONZERO strafe offsets.  Higher is faster/tighter interpolation. */
var() const protected float		StrafeOffsetInterpSpeedIn;
/** Interpolation speed for interpolating to a ZERO strafe offset.  Higher is faster/tighter interpolation. */
var() const protected float		StrafeOffsetInterpSpeedOut;
/** Strafe offset last tick, used for interpolation. */
var protected transient vector LastStrafeOffset;

/** Adjustment vector to apply to camera view offset when target is moving forward */
var() const protected vector	RunFwdAdjustment;
/** Adjustment vector to apply to camera view offset when target is moving backward */
var() const protected vector	RunBackAdjustment;
/** Velocity at (and above) which the full adjustment should be applied. */
var() const protected float		RunOffsetScalingThreshold;
/** Interpolation speed for interpolating to a NONZERO offset.  Higher is faster/tighter interpolation. */
var() const protected float		RunOffsetInterpSpeedIn;
/** Interpolation speed for interpolating to a ZERO offset.  Higher is faster/tighter interpolation. */
var() const protected float		RunOffsetInterpSpeedOut;
/** Run offset last tick, used for interpolation. */
var protected transient vector LastRunOffset;

/** 
 *  An offset from the location of the viewtarget, in the viewtarget's local space.
 *  Used to calculate the "worst case" camera location, which is where the camera should retreat to
 *  if tightly obstructed. 
 */
var() protected vector	WorstLocOffset;

/** True to turn do predictive camera avoidance, false otherwise */
var() const bool		bDoPredictiveAvoidance;

/** TRUE to do a raytrace from camera base loc to worst loc, just to be sure it's cool.  False to skip it */
var() const bool		bValidateWorstLoc;

/** If TRUE, all camera collision is disabled */
var() bool		    bSkipCameraCollision;

/** Offset, in the camera target's local space, from the camera target to the camera's origin. */
var() const protected vector TargetRelativeCameraOriginOffset;

/** Supported viewport configurations. */
enum ECameraViewportTypes
{
	CVT_16to9_Full,
	CVT_16to9_VertSplit,
	CVT_16to9_HorizSplit,
	CVT_4to3_Full,
	CVT_4to3_HorizSplit,
	CVT_4to3_VertSplit,
};

struct native ViewOffsetData
{
	/** View point offset for high player view pitch */
	var() vector OffsetHigh;
	/** View point offset for medium (horizon) player view pitch */
	var() vector OffsetMid;
	/** View point offset for low player view pitch */
	var() vector OffsetLow;
};

/** contains offsets from camera target to camera loc */
var() const protected ViewOffsetData	ViewOffset;

/** TRUE to smooth the interp between high/mid/low, false to blend linearly between high <-> mid <-> low */
var() const protected bool              bSmoothViewOffsetPitchChanges;

/** viewoffset adjustment vectors for each possible viewport type, so the game looks close to the same in each */
var() const protected ViewOffsetData	ViewOffset_ViewportAdjustments[ECameraViewportTypes.EnumCount];
/** whether delta or actual view offset should be applied to the camera location */
var() bool bApplyDeltaViewOffset;


/** Optional parameters for DOF adjustments. */
var(DepthOfField) const protected bool	    bAdjustDOF;
var(DepthOfField) const protected float	    DOF_FalloffExponent;
var(DepthOfField) const protected float	    DOF_BlurKernelSize;
var(DepthOfField) const protected float	    DOF_FocusInnerRadius;
var(DepthOfField) const protected float	    DOF_MaxNearBlurAmount;
var(DepthOfField) const protected float	    DOF_MaxFarBlurAmount;
var transient protected bool	bDOFUpdated;
var protected transient float	LastDOFRadius;
var protected transient float	LastDOFDistance;
var(DepthOfField) protected const float		DOFDistanceInterpSpeed;
var(DepthOfField) protected const vector	DOFTraceExtent;

/** Maps out how the DOF inner radius changes over distance. */
var(DepthOfField) protected const float		DOF_RadiusFalloff;
var(DepthOfField) protected const vector2d	DOF_RadiusRange;
var(DepthOfField) protected const vector2d	DOF_RadiusDistRange;

/** Whether camera anims and other post processing should change the FOV */
var bool bNoFOVPostProcess;

/** If TRUE ViewOffset will only be interpolated between camera mode transitions, and then be instantaneous */
var() bool bInterpViewOffsetOnlyForCamTransition;
/** Keep track of our ViewOffset Interpolation factor */
var   float ViewOffsetInterp;

/** We optionally interpolate the results of AdjustViewOffset() to prevent pops when a cameramode changes its adjustment suddenly. */
var() protected float OffsetAdjustmentInterpSpeed;

/** Keep track of the current viewport type as computed by GetViewOffset() */
var protected transient ECameraViewportTypes CurrentViewportType;

cpptext
{
	// GearThirdPersonCameraMode interface

	/**
	 * Calculates and returns the ideal view offset for the specified camera mode.
	 * The offset is relative to the Camera's pos/rot and calculated by interpolating
	 * 2 ideal view points based on the player view pitch.
	 *
	 * @param	ViewedPawn			Camera target pawn
	 * @param	DeltaTime			Delta time since last frame.
	 * @param	ViewRotation		Rot of the camera
	 */
	virtual FVector GetViewOffset(class APawn* ViewedPawn, FLOAT DeltaTime, const FVector& ViewOrigin, const FRotator& ViewRotation);
	FLOAT GetViewOffsetInterpSpeed(class APawn* ViewedPawn, FLOAT DeltaTime);
	virtual FRotator GetViewOffsetRotBase( class APawn* ViewedPawn, const FTViewTarget& VT );

	/** Returns View relative offsets */
	virtual void GetBaseViewOffsets(class APawn* ViewedPawn, BYTE ViewportConfig, FLOAT DeltaTime, FVector& out_Low, FVector& out_Mid, FVector& out_High);

	/** Returns true if mode should be using direct-look mode, false otherwise */
	virtual UBOOL UseDirectLookMode(class APawn* CameraTarget);

	/** Returns true if mode should lock camera to view target, false otherwise */
	virtual UBOOL LockedToViewTarget(class APawn* CameraTarget);

	/**
	 * Returns true if this mode should do target following.  If true is returned, interp speeds are filled in.
	 * If false is returned, interp speeds are not altered.
	 */
	virtual UBOOL ShouldFollowTarget(class APawn* CameraTarget, FLOAT& PitchInterpSpeed, FLOAT& YawInterpSpeed, FLOAT& RollInterpSpeed);

	/** Returns an offset, in pawn-local space, to be applied to the camera origin. */
	virtual FVector GetTargetRelativeOriginOffset(class APawn* TargetPawn);

	/**
	 * Returns location and rotation, in world space, of the camera's basis point.  The camera will rotate
	 * around this point, offsets are applied from here, etc.
	 */
	virtual void GetCameraOrigin(class APawn* TargetPawn, FVector& OriginLoc, FRotator& OriginRot);

	/**
	 * Interpolates from previous location/rotation toward desired location/rotation
	 */
	virtual void InterpolateCameraOrigin( class APawn* TargetPawn, FLOAT DeltaTime, FVector& out_ActualCameraOrigin, FVector const& IdealCameraOrigin, FRotator& out_ActualCameraOriginRot, FRotator const& IdealCameraOriginRot );

	/** Returns time to interpolate location/rotation changes. */
	virtual FLOAT GetBlendTime(class APawn* Pawn);

	/** Returns time to interpolate FOV changes. */
	virtual FLOAT GetFOVBlendTime(class APawn* Pawn);

	/*
	 *   Interpolates a camera's origin from the last location to a new ideal location
	 *   @CameraTargetRot - Rotation of the camera target, used to create a reference frame for interpolation
	 *   @LastLoc - Last location of the camera
	 *   @IdealLoc - Ideal location for the camera this frame
	 *   @DeltaTime - time step
	 *   @return if bInterpLocation is false, returns IdealLoc, otherwise if bUsePerAxisOriginLocInterp is TRUE it interpolates relative to the target axis via PerAxisOriginLocInterpSpeed, else via constant OriginLocInterpSpeed
	 */
	virtual FVector InterpolateCameraOriginLoc(class APawn* TargetPawn, FRotator const& CameraTargetRot, FVector const& LastLoc, FVector const& IdealLoc, FLOAT DeltaTime);
	virtual FRotator InterpolateCameraOriginRot(class APawn* TargetPawn, FRotator const& LastRot, FRotator const& IdealRot, FLOAT DeltaTime);

	virtual FVector	ApplyViewOffset( class APawn* ViewedPawn, const FVector& CameraOrigin, const FVector& ActualViewOffset, const FVector& DeltaViewOffset, const FTViewTarget& OutVT );

protected:
	virtual FLOAT GetViewPitch(APawn* TargetPawn, FRotator const& ViewRotation) const;
public:
};

/** Called when this mode is initially created/new'd */
function Init();

/** Called when Camera mode becomes active */
function OnBecomeActive(Pawn TargetPawn, GameThirdPersonCameraMode PrevMode)
{
	// Setup BlendTime for ViewOffsets
	if( BlendTime > 0.f )
	{
		ViewOffsetInterp = 1 / BlendTime;
	}
	else
	{
		ViewOffsetInterp = 0;
	}
}

/** Called when camera mode becomes inactive */
function OnBecomeInActive(Pawn TargetPawn, GameThirdPersonCameraMode NewMode);

/** 
 * Allows mode to make any final situational adjustments to the base view offset.
 */
event vector AdjustViewOffset( Pawn P, vector Offset )
{
	// no adjustment by default
	return Offset;
}

/** Returns FOV that this camera mode desires. */
function float GetDesiredFOV( Pawn ViewedPawn )
{
	return FOVAngle;
}


/**
 * Returns the "worst case" camera location for this camera mode.
 * This is the position that the camera penetration avoidance is based off of,
 * so it should be a guaranteed safe place to put the camera.
 * @param TargetPawn Pawn being viewed.
 * @param CurrentViewTarget current desired camera POV. Minus penetration checks.
 */
simulated event vector GetCameraWorstCaseLoc(Pawn TargetPawn, TViewTarget CurrentViewTarget)
{
	return TargetPawn.Location + (WorstLocOffset >> TargetPawn.Rotation);
}

/**
 * Camera mode has a chance to set a focus point, if it so chooses.
 * Return true if setting one, false if not.
 */
simulated function bool SetFocusPoint(Pawn ViewedPawn)
{
	// no focus point by default
	return FALSE;
}

simulated function ProcessViewRotation( float DeltaTime, Actor ViewTarget, out Rotator out_ViewRotation, out Rotator out_DeltaRot );


simulated protected function vector GetDOFFocusLoc(Actor TraceOwner, vector StartTrace, vector EndTrace)
{
	return DOFTrace(TraceOwner, StartTrace, EndTrace);
}

/** Modeled after CalcWeaponFire to avoid triggers.  Consider moving to native code and using a single line check call? */
simulated protected function vector DOFTrace(Actor TraceOwner, vector StartTrace, vector EndTrace)
{
	local vector	HitLocation, HitNormal;
	local Actor		HitActor;

	// Perform trace to retrieve hit info
	HitActor = TraceOwner.Trace(HitLocation, HitNormal, EndTrace, StartTrace, TRUE, DOFTraceExtent,, TraceOwner.TRACEFLAG_Bullet);

	// If we didn't hit anything, then set the HitLocation as being the EndTrace location
	if( HitActor == None )
	{
		HitLocation	= EndTrace;
	}

//	`log("DOF trace hit"@HitActor);

	// check to see if we've hit a trigger.
	// In this case, we want to add this actor to the list so we can give it damage, and then continue tracing through.
	if( HitActor != None )
	{
		if ( !HitActor.bBlockActors && 
			 ( HitActor.IsA('Trigger') || HitActor.IsA('TriggerVolume') ) )
		{	
			// disable collision temporarily for the trigger so that we can catch anything inside the trigger
			HitActor.bProjTarget = false;
			// recurse another trace
//			`log("... recursing!");
			HitLocation = DOFTrace(TraceOwner, HitLocation, EndTrace);
			// and reenable collision for the trigger
			HitActor.bProjTarget = true;
		}
	}

	return HitLocation;
}


/** Gives mode a chance to adjust/override postprocess as desired. */
simulated function UpdatePostProcess(const out TViewTarget VT, float DeltaTime)
{
	local vector FocusLoc, StartTrace, EndTrace, CamDir;
	local float FocusDist, SubjectDist, Pct;

	bDOFUpdated = FALSE;

	if (bAdjustDOF)
	{
		// nudge forward a little, in case camera gets itself in a wierd place.
		CamDir = vector(VT.POV.Rotation);
		StartTrace = VT.POV.Location + CamDir * 10;
		EndTrace = StartTrace + CamDir * 50000;
		FocusLoc = GetDOFFocusLoc(VT.Target, StartTrace, EndTrace);
		SubjectDist = VSize(FocusLoc - StartTrace);

		if (!ThirdPersonCam.bResetCameraInterpolation)
		{
			FocusDist = FInterpTo(LastDOFDistance, SubjectDist, DeltaTime, DOFDistanceInterpSpeed);
		}
		else
		{
			FocusDist = SubjectDist;
		}
		LastDOFDistance = FocusDist;

		// find focus radius
		// simpler method, with better-feeling results than actual optics math
		Pct = GetRangePctByValue(DOF_RadiusDistRange, FocusDist);
		LastDOFRadius = GetRangeValueByPct(DOF_RadiusRange, FClamp(Pct, 0.f, 1.f)**DOF_RadiusFalloff);

		bDOFUpdated = TRUE;
	}
}

simulated function ModifyPostProcessSettings(out PostProcessSettings PP)
{
	if (bDOFUpdated)
	{
		PP.bEnableDOF = TRUE;
`if(`__TW_POSTPROCESS_)
		PP.LegacySettings.DOF_FalloffExponent = DOF_FalloffExponent;
		PP.LegacySettings.DOF_BlurKernelSize = DOF_BlurKernelSize;
		PP.LegacySettings.DOF_MaxNearBlurAmount = DOF_MaxNearBlurAmount;
		PP.LegacySettings.DOF_MaxFarBlurAmount = DOF_MaxFarBlurAmount;
		PP.LegacySettings.DOF_FocusType = FOCUS_Distance;
		PP.LegacySettings.DOF_FocusInnerRadius = DOF_FocusInnerRadius;

		PP.LegacySettings.DOF_FocusDistance = LastDOFDistance;
		PP.LegacySettings.DOF_FocusInnerRadius = LastDOFRadius;
`else
		PP.DOF_FalloffExponent = DOF_FalloffExponent;
		PP.DOF_BlurKernelSize = DOF_BlurKernelSize;
		PP.DOF_MaxNearBlurAmount = DOF_MaxNearBlurAmount;
		PP.DOF_MaxFarBlurAmount = DOF_MaxFarBlurAmount;
		PP.DOF_FocusType = FOCUS_Distance;
		PP.DOF_FocusInnerRadius = DOF_FocusInnerRadius;

		PP.DOF_FocusDistance = LastDOFDistance;
		PP.DOF_FocusInnerRadius = LastDOFRadius;
`endif

		bDOFUpdated = FALSE;
	}
}

native final function SetViewOffset(const out ViewOffsetData NewViewOffset);

defaultproperties
{
	BlendTime=0.67

	bLockedToViewTarget=TRUE
	bDoPredictiveAvoidance=TRUE
	bValidateWorstLoc=TRUE

	bInterpLocation=TRUE
	OriginLocInterpSpeed=8

	StrafeLeftAdjustment=(X=0,Y=0,Z=0)
	StrafeRightAdjustment=(X=0,Y=0,Z=0)
	StrafeOffsetInterpSpeedIn=12.f
	StrafeOffsetInterpSpeedOut=20.f
	RunFwdAdjustment=(X=0,Y=0,Z=0)
	RunBackAdjustment=(X=0,Y=0,Z=0)
	RunOffsetInterpSpeedIn=6.f
	RunOffsetInterpSpeedOut=12.f

	WorstLocOffset=(X=-8,Y=1,Z=90)

	bInterpViewOffsetOnlyForCamTransition=TRUE
	/** all offsets are hand-crafted for this mode, so they should theoretically be all zeroes for all modes */
	ViewOffset_ViewportAdjustments(CVT_16to9_Full)={(
		OffsetHigh=(X=0,Y=0,Z=0),
		OffsetLow=(X=0,Y=0,Z=0),
		OffsetMid=(X=0,Y=0,Z=0),
		)}

	bAdjustDOF=FALSE
	DOF_FalloffExponent=1.f
	DOF_BlurKernelSize=3.f
//	DOF_FocusInnerRadius=2500.f
	DOF_MaxNearBlurAmount=0.6f
	DOF_MaxFarBlurAmount=1.f
	DOFDistanceInterpSpeed=10.f
	DOFTraceExtent=(X=0.f,Y=0.f,Z=0.f)

	DOF_RadiusFalloff=1.f
	DOF_RadiusRange=(X=2500,Y=60000)
	DOF_RadiusDistRange=(X=1000,Y=50000)

	OffsetAdjustmentInterpSpeed=12.f
}





