/*************************************************************************
Crytek Source File.
Copyright (C), Crytek Studios, 2001-2004.
-------------------------------------------------------------------------
$Id$
$DateTime$
Description: Single-shot Fire Mode Implementation

-------------------------------------------------------------------------
History:
- 11:9:2004   15:00 : Created by Márcio Martins

*************************************************************************/
#ifndef __SINGLE_H__
#define __SINGLE_H__

#if _MSC_VER > 1000
# pragma once
#endif


#include "Weapon.h"
#include "ItemParamReader.h"
#include "TracerManager.h"


#define ResetValue(name, defaultValue) if (defaultInit) name=defaultValue; reader.Read(#name, name)
#define ResetValueEx(name, var, defaultValue) if (defaultInit) var=defaultValue; reader.Read(#name, var)


#define WEAPON_HIT_RANGE				(2000.0f)
#define WEAPON_HIT_MIN_DISTANCE	(1.5f)


class CSingle :
	public IFireMode
{
protected:

	typedef struct SEffectParams
	{
		SEffectParams() { Reset(); };
		void Reset(const IItemParamsNode *params=0, bool defaultInit=true)
		{
			if (defaultInit)
			{
				effect[0].clear(); effect[1].clear();
				helper[0].clear(); helper[1].clear();
				scale[0]=scale[1]=1.0f;
				time[0]=time[1]=0.060f;
				light_helper[0].clear(); light_helper[1].clear();
				light_radius[0]=light_radius[1]=2.0f;
				light_color[0]=light_color[1]=Vec3(1,1,1);
				light_time[0]=light_time[1]=0.060f;
			}

			if (params)
			{
				const IItemParamsNode *fp=params->GetChild("firstperson");
				if (fp)
				{
					effect[0] = fp->GetAttribute("effect");
					helper[0] = fp->GetAttribute("helper");
					fp->GetAttribute("scale", scale[0]);
					fp->GetAttribute("time", time[0]);
					light_helper[0] = fp->GetAttribute("light_helper");
					fp->GetAttribute("light_radius", light_radius[0]);
					fp->GetAttribute("light_color", light_color[0]);
					fp->GetAttribute("light_time", light_time[0]);
				}

				const IItemParamsNode *tp=params->GetChild("thirdperson");
				if (tp)
				{
					effect[1] = tp->GetAttribute("effect");
					helper[1] = tp->GetAttribute("helper");
					light_helper[1] = tp->GetAttribute("light_helper");
					tp->GetAttribute("scale", scale[1]);
					tp->GetAttribute("time", time[1]);
					tp->GetAttribute("light_radius", light_radius[1]);
					tp->GetAttribute("light_color", light_color[1]);
					tp->GetAttribute("light_time", light_time[1]);
				}
			}
		}

	
		float	scale[2];
		float	time[2];
		string effect[2];
		string helper[2];
		float light_radius[2];
		float light_time[2];
		Vec3 light_color[2];
		string light_helper[2];
	} SEffectParams;

	typedef struct SRecoilParams
	{
		SRecoilParams() { Reset(); };
		void Reset(const IItemParamsNode *params=0, bool defaultInit=true)
		{
			CItemParamReader reader(params);
			ResetValue(max_recoil,	0.0f);
			ResetValue(attack,			0.0f);
			ResetValue(decay,				0.65f);
			ResetValueEx("maxx", max.x,8.0f);
			ResetValueEx("maxy", max.y,4.0f);
			ResetValue(randomness,	1.0f);

			if (defaultInit)
				hints.resize(0);

			if (params)
			{
				const IItemParamsNode *phints = params->GetChild("hints");
				if (phints && phints->GetChildCount())
				{
					Vec2 h;
					int n = phints->GetChildCount();
					hints.resize(n);

					for (int i=0; i<n; i++)
					{
						const IItemParamsNode *hint = phints->GetChild(i);
						if (hint->GetAttribute("x", h.x) && hint->GetAttribute("y", h.y))
							hints[i]=h;
					}
				}
			}
		}

		float								max_recoil;
		float								attack;
		float								decay;
		Vec2								max;
		float								randomness;
		std::vector<Vec2>		hints;
	} SRecoilParams;

	typedef struct SSpreadParams
	{
		SSpreadParams() { Reset(); };
		void Reset(const IItemParamsNode *params=0, bool defaultInit=true)
		{
			CItemParamReader reader(params);
			ResetValue(min,						0.015f);
			ResetValue(max,						0.0f);
			ResetValue(attack,				0.95f);
			ResetValue(decay,					0.65f);
			ResetValue(speed_m,				0.15f);
		}

		float	min;
		float max;
		float attack;
		float decay;
		float speed_m;
	} SSpreadParams;

	typedef struct STracerParams
	{
		STracerParams() { Reset(); };
		void Reset(const IItemParamsNode *params=0, bool defaultInit=true)
		{
			CItemParamReader reader(params);
			ResetValue(geometry,	"");
			ResetValue(effect,		"");
			ResetValue(speed,			175.0f);
			ResetValue(scale,			1.0f);
			ResetValue(lifetime,	1.5f);
			ResetValue(frequency,	1);
		};

		string	geometry;
		string	effect;
		float		speed;
		float		scale;
		float		lifetime;
		int			frequency;
	};

	typedef struct SFireParams
	{
		SFireParams() { Reset(); };
		void Reset(const IItemParamsNode *params=0, bool defaultInit=true)
		{
			CItemParamReader reader(params);
			ResetValue(rate,					400);
			ResetValue(clip_size,			30);
			ResetValue(max_clips,			20);
			ResetValue(hit_type,			"bullet");
			ResetValue(ammo_type,			"bullet");
			ResetValue(reload_time,		2.0f);
			ResetValue(offset,				0.15f);
			ResetValue(bullet_chamber,false);
			ResetValue(slider_layer,	"");
			ResetValue(damage,				32);
			ResetValue(crosshair,			"default");
			ResetValue(helper,				"");
			ResetValue(unzoom,				false);
			ResetValue(ooatracer_treshold, 0);


			ResetValue(spin_up_time,	0.0f);
			ResetValue(spin_down_time,0.0f);
			ResetValue(autoaim,		false);
			ResetValue(autozoom,	false);
      ResetValue(autoaim_distance, 10.f);
      ResetValue(autoaim_tolerance, 1.f);
      ResetValue(autoaim_locktime, 0.5f);
      ResetValue(autoaim_minvolume, 1.f);
      ResetValue(autoaim_maxvolume, 1000.f);
      ResetValue(autoaim_autofiringdir, true);

			pierceability[0] = 0.0f;
			pierceability[1] = 0.05f;
			pierceability[2] = 0.05f;
			pierceability[3] = 0.05f;
			pierceability[4] = 0.05f;
			pierceability[5] = 0.05f;
			pierceability[6] = 0.05f;
			pierceability[7] = 0.15f;
			pierceability[8] = 0.15f;
			pierceability[9] = 0.25f;
			pierceability[10] = 0.5f;
			pierceability[11] = 1.0f;
			pierceability[12] = 1.5f;
			pierceability[13] = 2.0f;
			pierceability[14] = 2.0f;
		}

		short		rate;
		short		clip_size;
		short		max_clips;
		float		reload_time;
		float		offset;
		string	helper;

		bool		unzoom;
		int			ooatracer_treshold;

		short		damage;
		
		string	crosshair;
		
		string	hit_type;
		string	ammo_type;
		float		pierceability[15];

		bool		bullet_chamber;
		string	slider_layer;

		float		spin_up_time;
		float		spin_down_time;
		
    bool	  autoaim;
    float   autoaim_distance;
    float   autoaim_tolerance;
    float   autoaim_locktime;
    float   autoaim_minvolume;
    float   autoaim_maxvolume;
    bool    autoaim_autofiringdir;
		
    bool	autozoom;
	} SFireParams;

	typedef struct SSingleActions
	{

		SSingleActions() { Reset(); };
		void Reset(const IItemParamsNode *params=0, bool defaultInit=true)
		{
			CItemParamReader reader(params);
			ResetValue(fire,					"fire");
			ResetValue(fire_cock,			"fire");
			ResetValue(empty_clip,		"empty_clip");
			ResetValue(reload,				"reload");
			ResetValue(reload_chamber_full,	"reload_chamber_full");
			ResetValue(reload_chamber_empty, "reload_chamber_empty");
			ResetValue(spin_up,				"spin_up");
			ResetValue(spin_down,			"spin_down");
		}

		string	fire;
		string	fire_cock;
		string	empty_clip;
		string	reload;
		string	reload_chamber_full;
		string	reload_chamber_empty;
		string	spin_up;
		string	spin_down;
	} SSingleActions;

public:
	CSingle();
	virtual ~CSingle();

	//IFireMode
	virtual void Init(IWeapon *pWeapon, const IItemParamsNode *params);
	virtual void Update(float frameTime, uint frameId);
	virtual void Destroy();

	virtual void ResetParams(const struct IItemParamsNode *params);
	virtual void PatchParams(const struct IItemParamsNode *patch);

	virtual void Activate(bool activate);

	virtual int GetAmmoCount() const;
	virtual int GetClipSize() const;
	virtual int GetClipCount() const;

	virtual bool OutOfAmmo() const;
	virtual bool CanReload() const;
	virtual void Reload(bool zoomed);

	virtual bool CanFire(bool considerAmmo = true) const;
	virtual void StartFire(EntityId shooterId);
	virtual void StopFire(EntityId shooterId);
	virtual bool IsFiring() { return m_firing; };

	virtual void NetShoot(const Vec3 &hit);

	virtual bool IsReadyToFire() const { return CanFire(true); };

	virtual EntityId GetProjectileId() const;
	virtual const char* GetType() const;
	virtual const char* GetAmmoType() const;

	virtual float GetSpinUpTime() const;
	virtual float GetSpinDownTime() const;

	virtual void Enable(bool enable);
	virtual bool IsEnabled() const;
	//~IFireMode

	virtual void StartReload(bool zoomed);
	virtual void EndReload(bool zoomed);
	virtual bool IsReloading();
	virtual bool Shoot(bool resetAnimation, bool autoreload=true);

	virtual bool ShootFromHelper(const Vec3 &eyepos, const Vec3 &probableHit);
	virtual Vec3 GetProbableHit(float range, bool *pbHit=0, ray_hit *hit=0);
	virtual Vec3 GetFiringPos(const Vec3 &probableHit);
	virtual Vec3 GetFiringDir(const Vec3 &probableHit);
	virtual Vec3 GetFiringVelocity();
	virtual Vec3 ApplySpread(const Vec3 &dir);

	virtual int GetDamage() const;
	virtual float GetRecoil() const;
	virtual float GetSpread() const;
	virtual const char *GetCrosshair() const;

	virtual void MuzzleFlashEffect(bool attach, bool light=true, bool effect=true);
	virtual void SpinUpEffect(bool attach);
	virtual void RejectEffect();

	// recoil/spread
	virtual void UpdateRecoil(float frameTime);
	virtual void ResetRecoil(bool spread=true);

	virtual void UpdateAutoAim(float frameTime);

protected:

	void CacheTracer();
	void ClearTracerCache();
  bool IsValidAutoAimTarget(IEntity* pEntity);
  bool CheckAutoAimTolerance(const Vec3& aimPos, const Vec3& aimDir);

	std::vector<IStatObj *> m_tracerCache;
	

	CWeapon		*m_pWeapon;

	EntityId	m_shooterId;
	bool			m_fired;
	bool			m_firing;
	bool			m_reloading;
	bool			m_emptyclip;

	float			m_next_shot_dt;
	float			m_next_shot;

	EntityId	m_projectileId;

	uint			m_mfId;
	uint			m_mflightId;
	float			m_mfTimer;
	float			m_mflTimer;

	uint			m_suId;
	uint			m_sulightId;
	float			m_suTimer;

	float			m_recoil;
	int				m_recoil_dir_idx;
	Vec2			m_recoil_dir;
	Vec2			m_recoil_offset;
	float			m_spread;

	bool			m_enabled;

	CTracerPath			m_pathprobe;
	uint						m_ammoid;

	float						m_spinUpTime;

	SFireParams			m_fireparams;
	STracerParams		m_tracerparams;
	STracerParams		m_ooatracerparams;
	SSingleActions	m_actions;
	SEffectParams		m_muzzleflash;
	SEffectParams		m_reject;
	SEffectParams		m_spinup;
	SRecoilParams		m_recoilparams;
	SSpreadParams		m_spreadparams;

	float m_zoomtimeout;
	EntityId m_lockedTarget;
	bool m_bLocked;
	float m_fStareTime;
};


#endif //__SINGLE_H__