//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Functions related to dynamic recipes
//
//=============================================================================


#include "cbase.h"
#include "econ_dynamic_recipe.h"
#ifndef GC_DLL
	#include "quest_objective_manager.h"
#endif

// This pattern was chosen to not be:
//		- a valid string acceptable for user-input (ie., custom name)
//		- a sensical float bit pattern
//		- a common int bit pattern
//		- meaningful Unicode data
const char *g_pszAttrEncodeSeparator = "|\x01\x02\x01\x03|\x01\x02\x01\x03|";

CRecipeComponentMatchingIterator::CRecipeComponentMatchingIterator( const IEconItemInterface *pSourceItem,
																	const IEconItemInterface *pTargetItem )
	: m_pSourceItem( pSourceItem )
	, m_pTargetItem( pTargetItem )
	, m_bIgnoreCompleted( true )
	, m_nInputsTotal( 0 )
	, m_nInputsFulfilled( 0 )
	, m_nOutputsTotal( 0 )
{}

bool CRecipeComponentMatchingIterator::OnIterateAttributeValue( const CEconItemAttributeDefinition *pAttrDef, const CAttribute_DynamicRecipeComponent& value )
{
	// Don't count ourselves as a match!
	if ( m_pSourceItem && m_pTargetItem && m_pSourceItem->GetID() == m_pTargetItem->GetID() )
		return true;

	// If this isn't a match and the item isn't NULL, we skip.  We consider NULL to mean
	// that we want to tally ALL attributes of this type
	if ( !DefinedItemAttribMatch( value, m_pTargetItem ) && m_pTargetItem != NULL )
		return true;

	// Dont let non-craftable items through
	if ( m_pTargetItem && !m_pTargetItem->IsUsableInCrafting() )
		return true;

	// Is this an output?
	if ( value.component_flags() & DYNAMIC_RECIPE_FLAG_IS_OUTPUT )
	{
		m_vecMatchingOutputs.AddToTail( pAttrDef );
		m_nOutputsTotal += value.num_required();
	}
	else
	{
		m_vecMatchingInputs.AddToTail( pAttrDef );
		m_nInputsTotal += value.num_required();
		m_nInputsFulfilled += value.num_fulfilled();
	}

	return true;
}


bool DefinedItemAttribMatch( const CAttribute_DynamicRecipeComponent& attribValue,  const IEconItemInterface* pItem )
{
	if ( !pItem )
		return false;

	// If our fulfilled count is what our item count is, then we're done.  We dont want any more matches.
	if ( attribValue.num_fulfilled() == attribValue.num_required() )
		return false;

	// If the item_def flag is set, and the item's item_def doesnt match then not a match
	if ( ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET ) &&
		( attribValue.def_index() != (uint32)pItem->GetItemDefIndex() ) )
		return false;

	// If the quality flag is set, and the item's quality doesn't match, then not a match
	if ( ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_QUALITY_SET ) &&
		( attribValue.item_quality() != (uint32)pItem->GetQuality() ) )
		return false;

	// check if we have ALL required attributes
	if ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ATTRIBUTE_SET_ALL )
	{
		CUtlVector<CEconItem::attribute_t> vecAttribs;
		if( !DecodeAttributeStringIntoAttributes( attribValue, vecAttribs ) )
		{
			AssertMsg2( 0, "%s: Unable to decode dynamic recipe attributes on item %llu", __FUNCTION__, pItem->GetID() );
			return false;
		}

		FOR_EACH_VEC( vecAttribs, i )
		{
			const CEconItemAttributeDefinition *pAttr = GetItemSchema()->GetAttributeDefinition( vecAttribs[i].m_unDefinitionIndex );
			Assert( pAttr );
			uint32 itemAttributeValue;
			if ( !pAttr || !pItem->FindAttribute( pAttr, &itemAttributeValue ) || itemAttributeValue != vecAttribs[i].m_value.asUint32 )
			{
				return false;
			}
		}
	}
	// check if we have ANY required attributes
	else if ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ATTRIBUTE_SET_ANY )
	{
		CUtlVector<CEconItem::attribute_t> vecAttribs;
		if( !DecodeAttributeStringIntoAttributes( attribValue, vecAttribs ) )
		{
			AssertMsg2( 0, "%s: Unable to decode dynamic recipe attributes on item %llu", __FUNCTION__, pItem->GetID() );
			return false;
		}

		bool bHasAnyMatchingAttributes = false;
		FOR_EACH_VEC( vecAttribs, i )
		{
			const CEconItemAttributeDefinition *pAttr = GetItemSchema()->GetAttributeDefinition( vecAttribs[i].m_unDefinitionIndex );
			Assert( pAttr );
			uint32 itemAttributeValue;
			if ( pAttr && pItem->FindAttribute( pAttr, &itemAttributeValue ) && itemAttributeValue == vecAttribs[i].m_value.asUint32 )
			{
				bHasAnyMatchingAttributes = true;
				break;
			}
		}

		if ( !bHasAnyMatchingAttributes )
		{
			return false;
		}
	}

	return true;
}

bool DecodeAttributeStringIntoAttributes( const CAttribute_DynamicRecipeComponent& attribValue, CUtlVector<CEconItem::attribute_t>& vecAttribs )
{
	CUtlStringList vecAttributeStrings;	// Automatically free'd
	V_SplitString( attribValue.attributes_string().c_str(), g_pszAttrEncodeSeparator, vecAttributeStrings );
	
	if( vecAttributeStrings.Count() % 2 != 0 )
	{
		AssertMsg1( 0, "%s: Uneven count of encoded attribute strings!", __FUNCTION__ );
		return false;
	}

	for( int j = 0; j< vecAttributeStrings.Count(); j+=2 )
	{
		// Get the attribute definition that's stored in the string, and its type
		attrib_definition_index_t index = Q_atoi( vecAttributeStrings[j] );
		const CEconItemAttributeDefinition *pAttrDef = GEconItemSchema().GetAttributeDefinition( index );
		if ( !pAttrDef )
		{
#ifdef GC
			EmitError( SPEW_GC, __FUNCTION__ ": Unable to find attribute definition '%s' (index %d)!\n", vecAttributeStrings[j], j );
#endif
			return false;
		}

		CEconItem::attribute_t& attrib = vecAttribs[vecAttribs.AddToTail()];
		attrib.m_unDefinitionIndex = pAttrDef->GetDefinitionIndex();
		
		// Now have the attribute read in the value stored in the string
		const ISchemaAttributeType* pAttrType = pAttrDef->GetAttributeType();
		pAttrType->InitializeNewEconAttributeValue( &attrib.m_value );

		// Don't fail us now!
		const char* pszAttribValue = vecAttributeStrings[j+1];
		if ( !pAttrType->BConvertStringToEconAttributeValue( pAttrDef, pszAttribValue, &attrib.m_value ) )
		{
#ifdef GC
			EmitError( SPEW_GC, __FUNCTION__ ": Unable to parse attribute value '%s' for attribute '%s'!\n", pszAttribValue, pAttrDef->GetDefinitionName() );
#endif
			return false;
		}
	}

	return true;
}

bool DecodeItemFromEncodedAttributeString( const CAttribute_DynamicRecipeComponent& attribValue, CEconItem* pItem )
{
	// If the item_def flag is set, set that item def
	if ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET )
	{
		pItem->SetDefinitionIndex( attribValue.def_index() );
	}
	else
	{
		// If the flag is not set, then we want the item name to be generic.  In english, we want to just call it "item".
		CAttribute_String attrStr;
		attrStr.set_value( "#TF_ItemName_Item" );

		static CSchemaAttributeDefHandle pAttrDef_ItemNameTextOverride( "item name text override" );
		pItem->SetDynamicAttributeValue( pAttrDef_ItemNameTextOverride, attrStr );
	}

	// If the quality flag is set, take the quality
	if ( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_QUALITY_SET )
	{
		pItem->SetQuality( attribValue.item_quality() );

		// If there's no item def set and the quality specified is "unique", we want to be explicit
		// and have the item description actually say "unique item" so there's no confusion as to what
		// item quality we want as an input.
		if ( !( attribValue.component_flags() & DYNAMIC_RECIPE_FLAG_PARAM_ITEM_DEF_SET )
			&& pItem->GetQuality() == AE_UNIQUE )
		{
			CAttribute_String attrStr;
			attrStr.set_value( "#unique" );

			static CSchemaAttributeDefHandle pAttrDef_QualityTextOverride( "quality text override" );
			pItem->SetDynamicAttributeValue( pAttrDef_QualityTextOverride, attrStr );
		}
	}
	else
	{
		// If no quality was specified, we want to explicity say that we'll accept ANY quality.
		pItem->SetQuality( AE_UNIQUE );
		CAttribute_String attrStr;
		attrStr.set_value( "#TF_QualityText_Any" );

		static CSchemaAttributeDefHandle pAttrDef_QualityTextOverride( "quality text override" );
		pItem->SetDynamicAttributeValue( pAttrDef_QualityTextOverride, attrStr );
	}
	pItem->SetFlags( 0 );

	// Get all the attributes encoded into the attribute
	CUtlVector<CEconItem::attribute_t> vecAttribs;
	if( !DecodeAttributeStringIntoAttributes( attribValue, vecAttribs ) )
	{
		AssertMsg1( 0, " %s : Unable to decode dynamic recipe attributes", __FUNCTION__ );
		return false;
	}

	// Apply the attributes to the item
	FOR_EACH_VEC( vecAttribs, j )
	{
		// We don't expect to get here with any missing attributes.
		const CEconItemAttributeDefinition *pAttrDef = GetItemSchema()->GetAttributeDefinition( vecAttribs[j].m_unDefinitionIndex );
		Assert( pAttrDef );

		const ISchemaAttributeType *pAttrType = pAttrDef->GetAttributeType();
		pAttrType->LoadEconAttributeValue( pItem, pAttrDef, vecAttribs[j].m_value );

		// Free up our attribute memory now that we're done with it.
		pAttrType->UnloadEconAttributeValue( &vecAttribs[j].m_value );
	}

	return true;
}