//Spawn flags
const int ESF_NOSPAWNONLINK = 0x01; //Don't spawn the enemy on Link

const int ES_FAILLIMIT = 10; //How many times to try placing enemies before giving up

// For general use by some functions and algorithms.
// g_temp_array is large enough to store data for
// drawing, combos, positions, gamedata, npcs, and other built-in objects,
// but you should never need to know the actual max size or use this data across frames
// or functions where the data can become invalidated.
int g_temp_array[512];


// flags //not implemented yet
// 1  - don't spawn on top of Link
void SpawnScreenEnemyOnComboID(int enemy_id, int n, int combo_id)		{ SpawnScreenEnemy(enemy_id, n, 0, 0, combo_id, 1, 1); }
void SpawnScreenEnemyOnComboFlag(int enemy_id, int n, int combo_flag)	{ SpawnScreenEnemy(enemy_id, n, 0, combo_flag, 0, 1, 1); }

void SpawnScreenEnemy(int enemy_id, int n)								{ SpawnScreenEnemy(enemy_id, n, 0, 0, 0, 1, 1); }
void SpawnScreenEnemy(int enemy_id, int n, int hp)						{ SpawnScreenEnemy(enemy_id, n, hp, 0, 0, 1, 1); }
void SpawnScreenEnemy(int enemy_id, int n, int hp, int combo_flag, int combo_id, int edgeOffset, int flags)
{
	int numAttempts; //How many times the function has failed to place the enemy
	
	edgeOffset = Clamp(edgeOffset, 0, 3);

	int x1 = edgeOffset;
	int y1 = edgeOffset;
	int x2 = 16 - edgeOffset;
	int y2 = 11 - edgeOffset;
	int num_spawn_points = 0;

	if(combo_flag != 0)
	{
		num_spawn_points = GetAllCombosWithFlag(g_temp_array, combo_flag);
	}
	else if(combo_id != 0)
	{
		num_spawn_points = GetAllCombosOfComboID(g_temp_array, combo_id);
	}
	else
	{
		num_spawn_points = GetWalkableCombosInComboRect(g_temp_array, x1, y1, x2, y2);
	}

	if(num_spawn_points == 0) //no place to spawn.
		return;

	for(int i = 0; i < n; i++)
	{
		int r = Rand(num_spawn_points);
		int c = g_temp_array[r];

		npc e = SpawnEnemyOnComboIndex(enemy_id, c, hp);
		//If spawned on Link and this flag disabled, fail and try again
		if( (flags & ESF_NOSPAWNONLINK) != 0 && LinkCollision(e) )
		{
			//Remove the enemy before spawning again
			Remove(e);
			
			//Count number of failures
			numAttempts++;
			if ( numAttempts >= ES_FAILLIMIT )
				return;
			
			//Try this part of the loop again
			i--;
			continue; //In case more is added below this
		}
		
		//If spawn successful, reset failure count
		numAttempts = 0;
	}
}

//get indices to all the screen combos of type
//returns the number of combos found
int GetAllCombosOfComboID(int arr, int combo_id)
{
	int n = 0;
	for(int i = 0; i < 176; i++)
	{
		if(Screen->ComboD[i] == combo_id)
		{
			arr[n] = i;
			n++;
		}
	}
	return n;
}

//get indices to all the screen combos with flag
//returns the number of combos found
int GetAllCombosWithFlag(int arr, int flag)
{
	int n = 0;
	for(int i = 0; i < 176; i++)
	{
		if(Screen->ComboF[i] == flag || Screen->ComboI[i] == flag)
		{
			arr[n] = i;
			n++;
		}
	}
	return n;
}

//takes arguments as x,y coordinates
int GetWalkableCombosInRect(int arr, int x1, int y1, int x2, int y2)
{
	x1 = Clamp(Floor(x1 / 16), 0, 16);
	y1 = Clamp(Floor(y1 / 16), 0, 11);
	x2 = Clamp(Ceiling(x2 / 16), 0, 16);
	y2 = Clamp(Ceiling(y2 / 16), 0, 11);

	return GetWalkableCombosInComboRect(arr, x1, y1, x2, y2);
}

//takes arguments as x,y indices
int GetWalkableCombosInComboRect(int arr, int x1, int y1, int x2, int y2)
{
	int n = 0;
	for(int i = y1; i < y2; i++)
	{
		int yPos = i * 16;
		for(int j = x1; j < x2; j++)
		{
			if(!IsPartiallySolid(j * 16, yPos))
			{
				int index = (i * 16) + j;
				if(Screen->ComboF[index] != CF_NOENEMY)
				{
					if(Screen->ComboI[index] != CF_NOENEMY)
					{
						arr[n] = index;
						n++;
					}
				}
			}
		}
	}

	return n;
}

npc SpawnEnemyOnComboIndex(int enemy_id, int combo_index) { return SpawnEnemyOnComboIndex(enemy_id, combo_index, 0); }
npc SpawnEnemyOnComboIndex(int enemy_id, int combo_index, int hp)
{
	npc e = Screen->CreateNPC(enemy_id);
	e->X = ComboX(combo_index);
	e->Y = ComboY(combo_index);

	if(hp > 0)
		e->HP = hp;

	return e;
}

npc SpawnEnemyOnComboID(int enemy_id, int combo_id) { return SpawnEnemyOnComboID(enemy_id, combo_id, 0); }
npc SpawnEnemyOnComboID(int enemy_id, int combo_id, int hp)
{
	int n = GetAllCombosOfComboID(g_temp_array, combo_id);

	npc e;
	if(n > 0)
	{
		int r = Rand(n);
		int c = g_temp_array[r];
		e = SpawnEnemyOnComboIndex(enemy_id, c, hp);
	}

	return e;
}

npc SpawnEnemyOnComboFlag(int enemy_id, int combo_id) { return SpawnEnemyOnComboFlag(enemy_id, combo_id, 0); }
npc SpawnEnemyOnComboFlag(int enemy_id, int flag, int hp)
{
	int n = GetAllCombosWithFlag(g_temp_array, flag);

	npc e;
	if(n > 0)
	{
		int r = Rand(n);
		int c = g_temp_array[r];
		e = SpawnEnemyOnComboIndex(enemy_id, c, hp);
	}

	return e;
}


// returns the number of enemies spawned
int SpawnEnemyOnAllCombosOfID(int enemy_id, int combo_id ) { return SpawnEnemyOnAllCombosOfID(enemy_id, combo_id, 0); }
int SpawnEnemyOnAllCombosOfID(int enemy_id, int combo_id, int hp )
{
	int n = 0;
	for(int i = 0; i < 176; i++)
	{
		if(Screen->ComboD[i] == combo_id)
		{
			n++;
			SpawnEnemyOnComboIndex(enemy_id, i, hp);
		}
	}

	return n;
}

// returns the number of enemies spawned
int SpawnEnemyOnAllComboFlags(int enemy_id, int flag ) { return SpawnEnemyOnAllComboFlags(enemy_id, flag, 0); }
int SpawnEnemyOnAllComboFlags(int enemy_id, int flag, int hp )
{
	int n = 0;
	for(int i = 0; i < 176; i++)
	{
		if(Screen->ComboF[i] == flag || Screen->ComboI[i] == flag)
		{
			n++;
			SpawnEnemyOnComboIndex(enemy_id, i, hp);
		}
	}

	return n;
}


int GetNthValidScreenEnemy(int n)
{
	int k = 0;

	int numNPCs = Screen->NumNPCs();
	for(int i = 1;i <= numNPCs; i++)
	{
		npc e = Screen->LoadNPC(i);
		if(!e->isValid())
			continue;

		// don't count oddball or non-beatable enemies.
		if(	e->Type == NPCT_GUY  ||
			e->Type == NPCT_ZORA ||
			e->Type == NPCT_ROCK ||
			e->Type == NPCT_TRAP ||
			e->Type > 31
		)
		{
			continue;
		}

		k++;
		if(k == n)
			return i;
	}

	return 0;
}


bool ScreenEnemiesAreDead()
{
	return GetNthValidScreenEnemy(1) == 0;
}


npc CreateDummyEnemy() { return CreateDummyEnemy(NPC_TRIGGER); }
npc CreateDummyEnemy(int enemy_id)
{
	npc dummy = Screen->CreateNPC(enemy_id);
	dummy->X = -16;
	dummy->Y = -16;
	dummy->HP = 800;
	dummy->Step = 0;

	return dummy;
}

//x,y must be aligned to grid to check a single combo
bool IsPartiallySolid(int x, int y)
{
	if(Screen->isSolid(x, y)) return true;
	if(Screen->isSolid(x, y+8)) return true;
	if(Screen->isSolid(x+8, y)) return true;
	if(Screen->isSolid(x+8, y+8)) return true;

	return false;
}