//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================//

#ifndef AI_CRITERIA_H
#define AI_CRITERIA_H
#ifdef _WIN32
#pragma once
#endif

#include "tier1/utlrbtree.h"
#include "tier1/utlsymbol.h"
#include "interval.h"
#include "mathlib/compressed_vector.h"

extern const char *SplitContext( const char *raw, char *key, int keylen, char *value, int valuelen, float *duration );


class AI_CriteriaSet
{
public:
	AI_CriteriaSet();
	AI_CriteriaSet( const AI_CriteriaSet& src );
	~AI_CriteriaSet();

	void AppendCriteria( const char *criteria, const char *value = "", float weight = 1.0f );
	void RemoveCriteria( const char *criteria );
	
	void Describe();

	int GetCount() const;
	int			FindCriterionIndex( const char *name ) const;

	const char *GetName( int index ) const;
	const char *GetValue( int index ) const;
	float		GetWeight( int index ) const;

#ifdef MAPBASE
	void		MergeSet( const AI_CriteriaSet& src );
#endif

private:

	struct CritEntry_t
	{
		CritEntry_t() :
				criterianame( UTL_INVAL_SYMBOL ),
				weight( 0.0f )
		{
			value[ 0 ] = 0;
		}

		CritEntry_t( const CritEntry_t& src )
		{
			criterianame = src.criterianame;
			value[ 0 ] = 0;
			weight = src.weight;
			SetValue( src.value );
		}

		CritEntry_t& operator=( const CritEntry_t& src )
		{
			if ( this == &src )
				return *this;

			criterianame = src.criterianame;
			weight = src.weight;
			SetValue( src.value );

			return *this;
		}

		static bool LessFunc( const CritEntry_t& lhs, const CritEntry_t& rhs )
		{
			return Q_stricmp( lhs.criterianame.String(), rhs.criterianame.String() ) < 0 ? true : false;
		}

		void SetValue( char const *str )
		{
			if ( !str )
			{
				value[ 0 ] = 0;
			}
			else
			{
				Q_strncpy( value, str, sizeof( value ) );
			}
		}
				
		CUtlSymbol criterianame;
		char		value[ 64 ];
		float		weight;
	};

	CUtlRBTree< CritEntry_t, short > m_Lookup;
};

#pragma pack(1)
template<typename T>
struct response_interval_t
{
	T start;
	T range;

	interval_t &ToInterval( interval_t &dest ) const	{ dest.start = start; dest.range = range; return dest; }
	void FromInterval( const interval_t &from )			{ start = from.start; range = from.range; }
	float Random() const								{ interval_t temp = { start, range }; return RandomInterval( temp ); }
};

typedef response_interval_t<float16_with_assign> responseparams_interval_t;

struct AI_ResponseParams
{
	DECLARE_SIMPLE_DATADESC();

	enum
	{
		RG_DELAYAFTERSPEAK =	(1<<0),
		RG_SPEAKONCE =			(1<<1),
		RG_ODDS =				(1<<2),
		RG_RESPEAKDELAY =		(1<<3),
		RG_SOUNDLEVEL =			(1<<4),
		RG_DONT_USE_SCENE =		(1<<5),
		RG_STOP_ON_NONIDLE =	(1<<6),
		RG_WEAPONDELAY =		(1<<7),
		RG_DELAYBEFORESPEAK =	(1<<8),
	};

	AI_ResponseParams()
	{
		flags = 0;
		odds = 100;
		delay.start = 0;
		delay.range = 0;
		respeakdelay.start = 0;
		respeakdelay.range = 0;
		weapondelay.start = 0;
		weapondelay.range = 0;
		soundlevel = 0;
		predelay.start = 0;
		predelay.range = 0;
	}

	responseparams_interval_t				delay;			//4
	responseparams_interval_t				respeakdelay;	//8
	responseparams_interval_t				weapondelay;	//12

	short					odds;							//14

	short					flags;							//16
	byte 					soundlevel;						//17

	responseparams_interval_t				predelay;		//21
};
#pragma pack()

//-----------------------------------------------------------------------------
// Purpose: Generic container for a response to a match to a criteria set
//  This is what searching for a response returns
//-----------------------------------------------------------------------------
enum ResponseType_t
{
	RESPONSE_NONE = 0,
	RESPONSE_SPEAK,
	RESPONSE_SENTENCE,
	RESPONSE_SCENE,
	RESPONSE_RESPONSE, // A reference to another response by name
	RESPONSE_PRINT,

	NUM_RESPONSES,
};

#ifdef MAPBASE
// I wanted to add more options to apply contexts to,
// so I replaced the existing system with a flag-based integer instead of a bunch of booleans.
// 
// New ones should be implemented in: 
// CResponseSystem::ParseRule() - AI_ResponseSystem.cpp
// AI_Response::Describe() - AI_Criteria.cpp
// CAI_Expresser::SpeakDispatchResponse() - ai_speech.cpp
enum
{
	APPLYCONTEXT_SELF = (1 << 0), // Included for contexts that apply to both self and something else
	APPLYCONTEXT_WORLD = (1 << 1), // Apply to world

	APPLYCONTEXT_SQUAD = (1 << 2), // Apply to squad
	APPLYCONTEXT_ENEMY = (1 << 3), // Apply to enemy
};
#endif

class AI_Response
{
public:
	DECLARE_SIMPLE_DATADESC();

	AI_Response();
	AI_Response( const AI_Response &from );
	~AI_Response();
	AI_Response &operator=( const AI_Response &from );

	void	Release();

	void			GetName( char *buf, size_t buflen ) const;
	void			GetResponse( char *buf, size_t buflen ) const;
	const AI_ResponseParams *GetParams() const { return &m_Params; }
	ResponseType_t	GetType() const { return (ResponseType_t)m_Type; }
	soundlevel_t	GetSoundLevel() const;
	float			GetRespeakDelay() const;
	float			GetWeaponDelay() const;
	bool			GetSpeakOnce() const;
	bool			ShouldntUseScene( ) const;
	bool			ShouldBreakOnNonIdle( void ) const;
	int				GetOdds() const;
	float			GetDelay() const;
	float			GetPreDelay() const;

	void			SetContext( const char *context );
	const char *	GetContext( void ) const { return m_szContext; }

#ifdef MAPBASE
	int				GetContextFlags() { return m_iContextFlags; }
	bool			IsApplyContextToWorld( void ) { return (m_iContextFlags & APPLYCONTEXT_WORLD) != 0; }
#else
	bool			IsApplyContextToWorld( void ) { return m_bApplyContextToWorld; }
#endif

	void Describe();

	const AI_CriteriaSet* GetCriteria();

	void	Init( ResponseType_t type, 
				const char *responseName, 
				const AI_CriteriaSet& criteria, 
				const AI_ResponseParams& responseparams,
				const char *matchingRule,
				const char *applyContext,
				bool bApplyContextToWorld );

#ifdef MAPBASE
	void	Init( ResponseType_t type, 
				const char *responseName, 
				const AI_CriteriaSet& criteria, 
				const AI_ResponseParams& responseparams,
				const char *matchingRule,
				const char *applyContext,
				int iContextFlags );
#endif

	static const char *DescribeResponse( ResponseType_t type );

	enum
	{
		MAX_RESPONSE_NAME = 64,
		MAX_RULE_NAME = 64
	};


private:
	byte			m_Type;
	char			m_szResponseName[ MAX_RESPONSE_NAME ];
	char			m_szMatchingRule[ MAX_RULE_NAME ];

	// The initial criteria to which we are responsive
	AI_CriteriaSet	*m_pCriteria;

	AI_ResponseParams m_Params;

	char *			m_szContext;
#ifdef MAPBASE
	int				m_iContextFlags;
#else
	bool			m_bApplyContextToWorld;
#endif
};

#endif // AI_CRITERIA_H
