#ifdef PRECOMPILEDHEADERS
	#include "TileEngine All.h"
#else
	#include "worlddef.h"
	#include "Random.h"
	#include "Smell.h"
	#include "worldman.h"
	#include "renderworld.h"
	#include "SaveLoadMap.h"
	#include "gamesettings.h"
	#include "message.h"
	#include "Isometric utils.h"
#endif

/*
 * Smell & Blood system
 * 
 * Smell and blood trails decay as time passes.  
 * 
 *             Decay Rate        Maximum Strength    Decay Time: Min Max (for biggest volume)
 *
 * Smell       1 per turn              31                         31  31
 * Blood    1 every 1-3 turns           7                          7  21
 *
 * Smell has a much finer resolution so that creatures which track by smell
 * can do so effectively.
 */

/*
 * Time for some crazy-ass macros!
 * The smell byte is spit as follows:
 * O \
 * O  \
 * O   \ Smell
 * O   / Strength (only on ground)
 * O  /
 * O /
 * O >   Type of blood on roof
 * O >   Type of smell/blood on ground
 *
 * The blood byte is split as follows:
 * O \
 * O  > Blood quantity on roof
 * O /
 * O \ 
 * O  > Blood quantity on ground
 * O /
 * O \  Blood decay
 * O /  time (roof and ground decay together)
 */

/*
 * In these defines,
 * s indicates the smell byte, b indicates the blood byte
 */

// LUT for which graphic to use based on strength
//															 0  1,  2,  3,  4,  5,  6, 7
UINT8 ubBloodGraphicLUT [ ] = {  3, 3,  2,  2,  1,  1,  0, 0 };


#define SMELL_STRENGTH_MAX		63
#define BLOOD_STRENGTH_MAX		7
#define BLOOD_DELAY_MAX       3

#define SMELL_TYPE_BITS( s )	(s & 0x03)

#define BLOOD_ROOF_TYPE( s )  (s & 0x02)
#define BLOOD_FLOOR_TYPE( s )	(s & 0x01)

#define BLOOD_ROOF_STRENGTH( b )    (b & 0xE0)
#define BLOOD_FLOOR_STRENGTH( b )		( (b & 0x1C) >> 2 )
#define BLOOD_DELAY_TIME( b )				(b & 0x03)
#define NO_BLOOD_STRENGTH( b )			((b & 0xFC) == 0)

#define DECAY_SMELL_STRENGTH( s ) \
{ \
	UINT8 ubStrength = SMELL_STRENGTH( (s) ); \
	ubStrength--; \
	ubStrength = ubStrength << SMELL_TYPE_NUM_BITS; \
	(s) = SMELL_TYPE_BITS( (s) ) | ubStrength; \
}

// s = smell byte
// ns = new strength
// ntf = new type on floor
// Note that the first part of the macro is designed to
// preserve the type value for the blood on the roof
#define SET_SMELL( s, ns, ntf ) \
{ \
	(s) = (BLOOD_ROOF_TYPE( s )) | SMELL_TYPE( ntf ) | (ns << SMELL_TYPE_NUM_BITS); \
}

#define DECAY_BLOOD_DELAY_TIME( b ) \
{ \
	(b)--; \
}

#define SET_BLOOD_FLOOR_STRENGTH( b, nb ) \
{ \
	(b) = ( (nb) << 2 ) | ( (b) & 0xE3); \
}

#define SET_BLOOD_ROOF_STRENGTH( b, nb ) \
{ \
	(b) = BLOOD_FLOOR_STRENGTH( (nb) ) << 5 | ( (b) & 0x1F); \
}

#define DECAY_BLOOD_FLOOR_STRENGTH( b ) \
{ \
	UINT8 ubFloorStrength; \
	ubFloorStrength = BLOOD_FLOOR_STRENGTH( (b) ); \
	ubFloorStrength--; \
	SET_BLOOD_FLOOR_STRENGTH( b, ubFloorStrength ); \
}

#define DECAY_BLOOD_ROOF_STRENGTH( b ) \
{ \
	UINT8 ubRoofStrength; \
	ubRoofStrength = BLOOD_ROOF_STRENGTH( (b) ); \
	ubRoofStrength--; \
	SET_BLOOD_FLOOR_STRENGTH( b, ubRoofStrength ); \
}

#define SET_BLOOD_DELAY_TIME( b ) \
{ \
 (b) = BLOOD_DELAY_TIME( (UINT8) Random( BLOOD_DELAY_MAX ) + 1 ) | (b & 0xFC); \
}

#define SET_BLOOD_FLOOR_TYPE( s, ntg ) \
{ \
	(s) = BLOOD_FLOOR_TYPE( ntg ) | (s & 0xFE); \
}

#define SET_BLOOD_ROOF_TYPE( s, ntr ) \
{ \
	(s) = BLOOD_ROOF_TYPE( ntr ) | (s & 0xFD); \
}

void RemoveBlood( INT16 sGridNo, INT8 bLevel )
{
	gpWorldLevelData[ sGridNo ].ubBloodInfo = 0;

	gpWorldLevelData[ sGridNo ].uiFlags |= MAPELEMENT_REEVALUATEBLOOD;

	UpdateBloodGraphics( sGridNo, bLevel );
}


void DecaySmells( void )
{	
	UINT32					uiLoop;
	MAP_ELEMENT *		pMapElement;

	//return;

	for (uiLoop = 0, pMapElement = gpWorldLevelData; uiLoop < WORLD_MAX; uiLoop++, pMapElement++)
	{

		if (pMapElement->ubSmellInfo)
		{
			// decay smell strength!
			DECAY_SMELL_STRENGTH( pMapElement->ubSmellInfo );
			// if the strength left is 0, wipe the whole byte to clear the type
			if (SMELL_STRENGTH( pMapElement->ubSmellInfo ) == 0)
			{
				pMapElement->ubSmellInfo = 0;
			}
		}
	}
}


void DecayBlood()
{
	UINT32					uiLoop;
	MAP_ELEMENT *		pMapElement;

	for (uiLoop = 0, pMapElement = gpWorldLevelData; uiLoop < WORLD_MAX; uiLoop++, pMapElement++)
	{
		if (pMapElement->ubBloodInfo)
		{
			// delay blood timer!
			DECAY_BLOOD_DELAY_TIME( pMapElement->ubBloodInfo );
			if (BLOOD_DELAY_TIME( pMapElement->ubBloodInfo ) == 0)
			{
				// Set re-evaluate flag
				pMapElement->uiFlags |= MAPELEMENT_REEVALUATEBLOOD;

				// reduce the floor blood strength if it is above zero
				if (BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo ) > 0)
				{
					DECAY_BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo )
					if (BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo ) == 0)
					{
						// delete the blood graphic on the floor!
						// then
						if (NO_BLOOD_STRENGTH( pMapElement->ubBloodInfo ))
						{
							// wipe the whole byte to zero
							pMapElement->ubBloodInfo = 0;
						}
					}
				}
				// reduce the roof blood strength if it is above zero
				if (BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo ) > 0)
				{
					DECAY_BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo )
					if (BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo ) == 0)
					{
						// delete the blood graphic on the roof!
						if (NO_BLOOD_STRENGTH( pMapElement->ubBloodInfo ))
						{
							// wipe the whole byte to zero
							pMapElement->ubBloodInfo = 0;							
						}
					}
				}

				// if any blood remaining, reset time
				if ( pMapElement->ubBloodInfo )
				{
					SET_BLOOD_DELAY_TIME( pMapElement->ubBloodInfo );
				}
			}
			// end of blood handling
		}

		// now go on to the next gridno
	}
}

void DecayBloodAndSmells( UINT32 uiTime )
{
	UINT32					uiCheckTime;

	if ( !gfWorldLoaded )
	{
		return;
	}
	
	// period between checks, in game seconds
	switch( giTimeCompressMode )
	{
		// in time compression, let this happen every 5 REAL seconds
		case TIME_COMPRESS_5MINS: // rate of 300 seconds per real second
			uiCheckTime = 5 * 300;
			break;
		case TIME_COMPRESS_30MINS: // rate of 1800 seconds per real second
			uiCheckTime = 5 * 1800;
			break;
		case TIME_COMPRESS_60MINS: // rate of 3600 seconds per real second
		case TIME_SUPER_COMPRESS: // should not be used but just in frigging case...
			uiCheckTime = 5 * 3600;
			break;
		default: // not compressing
			uiCheckTime = 100;
			break;
	}

	// ok so "uiDecayBloodLastUpdate" is a bit of a misnomer now
	if ( ( uiTime - gTacticalStatus.uiDecayBloodLastUpdate ) > uiCheckTime )
	{
		gTacticalStatus.uiDecayBloodLastUpdate = uiTime;
		DecayBlood();
		DecaySmells();
	}
}

void DropSmell( SOLDIERTYPE * pSoldier )
{
	MAP_ELEMENT *		pMapElement;
	UINT8						ubOldSmell;
	UINT8						ubOldStrength;
	UINT8						ubSmell;
	UINT8						ubStrength;

	/*
	 *  Here we are creating a new smell on the ground.  If there is blood in
	 *  the tile, it overrides dropping smells of any type
	 */

	if (pSoldier->bLevel == 0)
	{
		pMapElement = &(gpWorldLevelData[pSoldier->sGridNo]);
		if (pMapElement->ubBloodInfo)
		{
			// blood here, don't drop any smell
			return;
		}

		if (pSoldier->bNormalSmell > pSoldier->bMonsterSmell)
		{
			ubStrength = pSoldier->bNormalSmell - pSoldier->bMonsterSmell;
			ubSmell = HUMAN;
		}
		else
		{
			ubStrength = pSoldier->bMonsterSmell - pSoldier->bNormalSmell;
			if (ubStrength == 0)
			{
				// don't drop any smell
				return;
			}
			ubSmell = CREATURE_ON_FLOOR;
		}

		if (pMapElement->ubSmellInfo)
		{
			// smell already exists here; check to see if it's the same or not

			ubOldSmell = SMELL_TYPE( pMapElement->ubSmellInfo );
			ubOldStrength = SMELL_STRENGTH( pMapElement->ubSmellInfo );
			if (ubOldSmell == ubSmell)
			{
				// same smell; increase the strength to the bigger of the two strengths,
				// plus 1/5 of the smaller
				ubStrength = __max( ubStrength, ubOldStrength ) + __min( ubStrength, ubOldStrength ) / 5;
				ubStrength = __max( ubStrength, SMELL_STRENGTH_MAX );
			}
			else
			{
				// different smell; we muddy the smell by reducing the smell strength
				if (ubOldStrength > ubStrength)
				{
					ubOldStrength -= ubStrength / 3;
					SET_SMELL( pMapElement->ubSmellInfo, ubOldStrength, ubOldSmell );
				}
				else
				{
					ubStrength -= ubOldStrength / 3;
					if (ubStrength > 0)
					{
						SET_SMELL( pMapElement->ubSmellInfo, ubStrength, ubSmell );
					}
					else
					{
						// smell reduced to 0 - wipe all info on it!
						pMapElement->ubSmellInfo = 0;
					}
				}
			}
		}
		else
		{
			// the simple case, dropping a smell in a location where there is none
			SET_SMELL( pMapElement->ubSmellInfo, ubStrength, ubSmell );
		}
	}
	// otherwise skip dropping smell
}


void InternalDropBlood( INT16 sGridNo, INT8 bLevel, UINT8 ubType, UINT8 ubStrength, INT8 bVisible )
{
	MAP_ELEMENT *		pMapElement;
	UINT8						ubOldStrength=0;
	UINT8						ubNewStrength=0;
		
	/*
	 * Dropping some blood;
	 * We can check the type of blood by consulting the type in the smell byte
	 */

	// If we are in water...
	if ( GetTerrainType( sGridNo ) == DEEP_WATER || GetTerrainType( sGridNo ) == LOW_WATER || GetTerrainType( sGridNo ) == MED_WATER )
	{
		return;
	}

	// ATE: Send warning if dropping blood nowhere....
	if ( sGridNo == NOWHERE )
	{
		#ifdef JA2BETAVERSION
			ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Attempting to drop blood NOWHERE" );
		#endif
		return;
	}

	// ensure max strength is okay
	ubStrength = __min( ubStrength, BLOOD_STRENGTH_MAX );

	pMapElement = &(gpWorldLevelData[ sGridNo ]);
	if ( bLevel == 0 )
	{
		// dropping blood on ground
		ubOldStrength = BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo );
		if (ubOldStrength > 0)
		{
			// blood already there... we'll leave the decay time as it is
			if (BLOOD_FLOOR_TYPE( pMapElement->ubBloodInfo ) == ubType)
			{
				// combine blood strengths!
				ubNewStrength = __min( ( ubOldStrength + ubStrength ), BLOOD_STRENGTH_MAX ); 

				SET_BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo, ubNewStrength );
			}
			else
			{
				// replace the existing blood if more is being dropped than exists
				if (ubStrength > ubOldStrength )
				{
					// replace!
					SET_BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo, ubStrength );
				}
				// else we don't drop anything at all
			}
		}
		else
		{
			// no blood on the ground yet, so drop this amount!
			// set decay time 
			SET_BLOOD_DELAY_TIME( pMapElement->ubBloodInfo );
			SET_BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo, ubStrength );
			// NB blood floor type stored in smell byte!
			SET_BLOOD_FLOOR_TYPE( pMapElement->ubSmellInfo, ubType );
		}
	}
	else
	{
		// dropping blood on roof
		ubOldStrength = BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo );
		if (ubOldStrength > 0)
		{
			// blood already there... we'll leave the decay time as it is
			if (BLOOD_ROOF_TYPE( pMapElement->ubSmellInfo ) == ubType)
			{
				// combine blood strengths!
				ubNewStrength = __max( ubOldStrength, ubStrength ) + 1;		
				// make sure the strength is legal		
				ubNewStrength = __max( ubNewStrength, BLOOD_STRENGTH_MAX );
				SET_BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo, ubNewStrength );
			}
			else
			{
				// replace the existing blood if more is being dropped than exists
				if (ubStrength > ubOldStrength )
				{
					// replace!
					SET_BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo, ubStrength );
				}
				// else we don't drop anything at all
			}
		}
		else
		{
			// no blood on the roof yet, so drop this amount!
			// set decay time 
			SET_BLOOD_DELAY_TIME( pMapElement->ubBloodInfo );
			SET_BLOOD_ROOF_STRENGTH( pMapElement->ubBloodInfo, ubNewStrength );
			SET_BLOOD_ROOF_TYPE( pMapElement->ubSmellInfo, ubType );
		}
	}

	// Turn on flag...
	pMapElement->uiFlags |= MAPELEMENT_REEVALUATEBLOOD;

	if ( bVisible != -1 )
	{
		UpdateBloodGraphics( sGridNo, bLevel );
	}

}


void DropBlood( SOLDIERTYPE * pSoldier, UINT8 ubStrength, INT8 bVisible )
{
	UINT8						ubType;
	UINT8						ubOldStrength=0;
	UINT8						ubNewStrength=0;
		
	/*
	 * Dropping some blood;
	 * We can check the type of blood by consulting the type in the smell byte
	 */

	// figure out the type of blood that we're dropping
	if ( pSoldier->uiStatusFlags & SOLDIER_MONSTER )
	{
		if ( pSoldier->bLevel == 0 )
		{
			ubType = CREATURE_ON_FLOOR;
		}
		else
		{
			ubType = CREATURE_ON_ROOF;
		}
	}
	else
	{
		ubType = 0;
	}


	InternalDropBlood( pSoldier->sGridNo, pSoldier->bLevel, ubType, ubStrength, bVisible );
}



void UpdateBloodGraphics( INT16 sGridNo, INT8 bLevel )
{
	MAP_ELEMENT *		pMapElement;
	INT8						bValue;
	UINT16					usIndex, usNewIndex;
	
	// OK, based on level, type, display graphics for blood
	pMapElement = &(gpWorldLevelData[ sGridNo ]);

	// CHECK FOR BLOOD OPTION
	if ( !gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] )
	{
		return;
	}

	if ( pMapElement->uiFlags & MAPELEMENT_REEVALUATEBLOOD )
	{

		// Turn off flag!
		pMapElement->uiFlags &= ( ~MAPELEMENT_REEVALUATEBLOOD );

		// Ground
		if ( bLevel == 0 )
		{
			bValue = BLOOD_FLOOR_STRENGTH( pMapElement->ubBloodInfo );
		
			// OK, remove tile graphic if one exists....
			if ( TypeRangeExistsInObjectLayer( sGridNo, HUMANBLOOD, CREATUREBLOOD, &usIndex ) )
			{
				//This has been removed and it is handled by the ubBloodInfo level when restoring a saved game.
				//Set a flag indicating that the following changes are to go the the maps temp file
				//ApplyMapChangesToMapTempFile( TRUE );

				// Remove
				RemoveObject( sGridNo, usIndex );

				//ApplyMapChangesToMapTempFile( FALSE );
			}

			// OK, pick new one. based on strength and randomness

			if ( bValue > 0 )
			{
				usIndex = (UINT16)( ( Random( 4 ) * 4 ) + ubBloodGraphicLUT[ bValue ] );

				if ( BLOOD_FLOOR_TYPE( pMapElement->ubSmellInfo )	== 0 )
				{
					GetTileIndexFromTypeSubIndex( HUMANBLOOD, (UINT16)(usIndex + 1), &usNewIndex );
				}
				else
				{
					GetTileIndexFromTypeSubIndex( CREATUREBLOOD, (UINT16)(usIndex + 1), &usNewIndex );
				}

				//This has been removed and it is handled by the ubBloodInfo level when restoring a saved game.
				//Set a flag indicating that the following changes are to go the the maps temp file
				//ApplyMapChangesToMapTempFile( TRUE );

				// Add!
				AddObjectToHead( sGridNo, usNewIndex );

				//ApplyMapChangesToMapTempFile( FALSE );


				// Update rendering!
				pMapElement->uiFlags|=MAPELEMENT_REDRAW;
				SetRenderFlags(RENDER_FLAG_MARKED);

			}
		}
		// Roof
		else
		{


		}
	}
}