/*
	This file is part of Warzone 2100.
	Copyright (C) 1999-2004  Eidos Interactive
	Copyright (C) 2005-2007  Warzone Resurrection Project

	Warzone 2100 is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	Warzone 2100 is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Warzone 2100; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
	Selection.c
	Alex McLean, Pumpkin studios, EIDOS.
	Attempt to rationalise the unit selection procedure and
	also necessary as we now need to return the number of
	units selected according to specified criteria.
*/

#include "lib/framework/frame.h"
#include "objects.h"
#include "base.h"
#include "droiddef.h"
#include "statsdef.h"
#include "text.h"
#include "geometry.h"
#include "console.h"
#include "selection.h"
#include "hci.h"
#include "map.h"
#include "selection.h"
#include "display3d.h"
#include "warcam.h"
#include "display.h"

// ---------------------------------------------------------------------
// EXTERNALLY REFERENCED FUNCTIONS
UDWORD	selDroidSelection		( UDWORD	player, SELECTION_CLASS droidClass,
									SELECTIONTYPE droidType, BOOL bOnScreen );
UDWORD	selDroidDeselect		( UDWORD player );
UDWORD	selNumSelected			( UDWORD player );

// ---------------------------------------------------------------------
// STATIC SUPPORT FUNCTIONS
UDWORD	selSelectAllUnits		( UDWORD player, BOOL bOnScreen );
UDWORD	selSelectAllSameProp	( UDWORD player, PROPULSION_TYPE propType,
									BOOL bOnScreen );
UDWORD	selSelectAllCombat		( UDWORD player, BOOL bOnScreen);
UDWORD	selSelectAllDamaged		( UDWORD player, BOOL bOnScreen);
UDWORD	selSelectAllSame		( UDWORD player, BOOL bOnScreen);
UDWORD	selNameSelect			( STRING *droidName, UDWORD player, BOOL bOnScreen );
// ---------------------------------------------------------------------
/*
	Selects the units of a given player according to given criteria.
	It is also possible to request whether the units be onscreen or not.
*/

DROID	*psOldRD = NULL;	// pointer to last selected repair unit
DROID	*psOldNS = NULL;
STRUCTURE	*psOldStruct = NULL;

UDWORD	selDroidSelection( UDWORD	player, SELECTION_CLASS droidClass,
						  SELECTIONTYPE droidType, BOOL bOnScreen )
{
UDWORD	retVal;
char	selInfo[255];

	/* So far, we haven't selected any */
	retVal = 0;

	/* Establish the class of selection */
	switch(droidClass)
	{
	case	DS_ALL_UNITS:
		retVal = selSelectAllUnits(player,bOnScreen);
		break;
	case	DS_BY_TYPE:
		switch(droidType)
		{
		case	DST_VTOL:
			retVal = selSelectAllSameProp(player,LIFT,bOnScreen);
			break;
		case	DST_HOVER:
			retVal = selSelectAllSameProp(player,HOVER,bOnScreen);
			break;
		case	DST_WHEELED:
			retVal = selSelectAllSameProp(player,WHEELED,bOnScreen);
			break;
		case	DST_TRACKED:
			retVal = selSelectAllSameProp(player,TRACKED,bOnScreen);
			break;
		case	DST_HALF_TRACKED:
			retVal = selSelectAllSameProp(player,HALF_TRACKED,bOnScreen);
			break;
		case	DST_ALL_COMBAT:
			retVal = selSelectAllCombat(player,bOnScreen);
			break;
		case	DST_ALL_DAMAGED:
			retVal = selSelectAllDamaged(player,bOnScreen);
			break;
		case DST_ALL_SAME:
			retVal = selSelectAllSame(player,bOnScreen);
			break;
		default:
			ASSERT( FALSE,"Invalid selection type in uniDroidSelection" );
		}
		break;
	default:
		ASSERT( FALSE,"Invalid selection attempt in uniDroidSelection" );
		break;
	}

	/* Send back the return value */
	sprintf(selInfo,strresGetString(psStringRes,STR_GAM_UNITSEL),retVal);
	addConsoleMessage(selInfo,RIGHT_JUSTIFY);
	return(retVal);
}

// ---------------------------------------------------------------------
// Selects all units owned by the player - onscreen toggle.
UDWORD	selSelectAllUnits( UDWORD player, BOOL bOnScreen )
{
DROID	*psDroid;
UDWORD	count;

	selDroidDeselect(player);
	/* Go thru' all */
	for(psDroid = apsDroidLists[player],count = 0;
		psDroid; psDroid = psDroid->psNext)
		{
			/* Do we care about them being on screen? */
			if(bOnScreen ? droidOnScreen(psDroid,0) : TRUE)
			{
				/* can select everything except transporters */
				if(psDroid->droidType != DROID_TRANSPORTER)
				{
//					psDroid->selected = TRUE;
					SelectDroid(psDroid);
					count++;
				}
			}
		}

	return(count);
}
// ---------------------------------------------------------------------
// Selects all units owned by the player of a certain propulsion type.
// On Screen toggle.
UDWORD	selSelectAllSameProp( UDWORD player, PROPULSION_TYPE propType,
						   BOOL bOnScreen )
{
PROPULSION_STATS	*psPropStats;
DROID	*psDroid;
UDWORD	count;

	selDroidDeselect(player);
	/* Go thru' them all */
	for(psDroid = apsDroidLists[player], count = 0;
		psDroid; psDroid = psDroid->psNext)
	{
		/* Is on screen important */
		if(bOnScreen ? droidOnScreen(psDroid,0) : TRUE)
		{
			/* Get the propulsion type */
			psPropStats = asPropulsionStats + psDroid->asBits[COMP_PROPULSION].nStat;
			ASSERT( PTRVALID(psPropStats, sizeof(PROPULSION_STATS)),
				"moveUpdateUnit: invalid propulsion stats pointer" );
			/* Same as that asked for - don't want Transporters*/
			if ( psPropStats->propulsionType == propType AND psDroid->droidType != DROID_TRANSPORTER)
			{
				psDroid->selected = TRUE;
				count++;
			}
		}
	}

	return(selNumSelected(player));
}
// ---------------------------------------------------------------------
// Selects all units owned by the player that have a weapon. On screen
// toggle.
UDWORD	selSelectAllCombat( UDWORD player, BOOL bOnScreen)
{
DROID	*psDroid;
UDWORD	count;

	selDroidDeselect(player);
	for(psDroid = apsDroidLists[player], count = 0;
		psDroid; psDroid = psDroid->psNext)
		{
			/* Does it have a weapon? */
			//if(psDroid->numWeaps)
            if(psDroid->asWeaps[0].nStat > 0)
			{
		  		/* Is on screen relevant? */
				if(bOnScreen ? droidOnScreen(psDroid,0) : TRUE)
				{
                    //we don't want to get the Transporter
	 				if(psDroid->droidType != DROID_TRANSPORTER)
					{
						psDroid->selected = TRUE;
						count ++;
					}
				}
			}
		}
	return(count);
}
// ---------------------------------------------------------------------
// Selects all damaged units - on screen toggle.
UDWORD	selSelectAllDamaged( UDWORD player, BOOL bOnScreen)
{
DROID	*psDroid;
UDWORD	damage;
UDWORD	count;

	selDroidDeselect(player);
	for(psDroid = apsDroidLists[player], count = 0;
		psDroid; psDroid = psDroid->psNext)
		{
			/* Get present percent of damage level */
			damage = PERCENT(psDroid->body,psDroid->originalBody);

			/* Less than threshold? */
			if(damage<REPAIRLEV_LOW)
			{
				/* Is on screen relevant? */
				if(bOnScreen ? droidOnScreen(psDroid,0) : TRUE)
				{
                    //we don't want to get the Transporter
	 				if(psDroid->droidType != DROID_TRANSPORTER)
                    {
//    					psDroid->selected = TRUE;
						SelectDroid(psDroid);
	    				count ++;
                    }
				}
			}
		}
	return(count);
}
// ---------------------------------------------------------------------
// Deselects all units for the player
UDWORD	selDroidDeselect( UDWORD player )
{
UDWORD	count;
DROID	*psDroid;

	for(psDroid = apsDroidLists[player], count = 0;
		psDroid; psDroid = psDroid->psNext)
		{
			if(psDroid->selected)
			{
				count++;
			}
//			psDroid->selected = FALSE;
			DeSelectDroid(psDroid);
		}
	return(count);
}
// ---------------------------------------------------------------------
// Lets you know how many are selected for a given player
UDWORD	selNumSelected( UDWORD player )
{
UDWORD	count;
DROID	*psDroid;

	for(psDroid = apsDroidLists[player], count = 0;
		psDroid; psDroid = psDroid->psNext)
		{
			if(psDroid->selected)
			{
				count++;
			}
		}
	return(count);
}

// ---------------------------------------------------------------------
// Selects all units the same as the one(s) selected
UDWORD	selSelectAllSame( UDWORD player, BOOL bOnScreen)
{


DROID	*psDroid;
UDWORD	count;

	for(psDroid = apsDroidLists[player], count = 0;
		psDroid; psDroid = psDroid->psNext)
		{
			if(psDroid->selected)
			{
				count+=selNameSelect(psDroid->aName, player, bOnScreen);
			}
		}
	return(selNumSelected(player));



}
// ---------------------------------------------------------------------
// sub-function - selects all units with same name as one passed in
UDWORD	selNameSelect( STRING *droidName, UDWORD player, BOOL bOnScreen )
{

DROID	*psDroid;
UDWORD	count;

 	for(psDroid = apsDroidLists[player], count = 0;
		psDroid; psDroid = psDroid->psNext)
		{
			if(!psDroid->selected)
			{
				if(bOnScreen ? droidOnScreen(psDroid,0) : TRUE)
				{
					if(!strcmp(droidName,psDroid->aName))
					{
//						psDroid->selected = TRUE;
						SelectDroid(psDroid);
						count++;
					}
				}
			}
		}
	if(count)
		return(count+1);
	else
		return(count);

}

// ffs am
// ---------------------------------------------------------------------
void	selNextSpecifiedUnit(UDWORD unitType)
{
DROID	*psCurr;
DROID	*psResult;
DROID	*psFirst;
BOOL	bLaterInList, bMatch;

	for(psCurr = apsDroidLists[selectedPlayer],psFirst = NULL,psResult = NULL,bLaterInList = FALSE;
		psCurr AND !psResult; psCurr = psCurr->psNext)
	{
		//if( psCurr->droidType == (SDWORD)unitType )
        //exceptions - as always...
        bMatch = FALSE;
        if (unitType == DROID_CONSTRUCT)
        {
            if (psCurr->droidType == DROID_CONSTRUCT OR
                psCurr->droidType == DROID_CYBORG_CONSTRUCT)
            {
                bMatch = TRUE;
            }
        }
        else if (unitType == DROID_REPAIR)
        {
            if (psCurr->droidType == DROID_REPAIR OR
                psCurr->droidType == DROID_CYBORG_REPAIR)
            {
                bMatch = TRUE;
            }
        }
        else if(psCurr->droidType == (SDWORD)unitType)
        {
            bMatch = TRUE;
        }
        if (bMatch)
		{
		 	/* Always store away the first one we find */
			if(!psFirst)
			{
				psFirst = psCurr;
			}

			if(psCurr == psOldRD)
			{
				bLaterInList = TRUE;
			}

			/* Nothing previously found... */
			if(!psOldRD)
			{
				psResult = psCurr;
			}

			/* Only select is this isn't the old one and it's further on in list */
			else if(psCurr!=psOldRD AND bLaterInList)
			{
				psResult = psCurr;
			}

		 }
	}

	/* Did we get one? */
	if(!psResult)
	{
		/* was there at least one - the first one? Resetting */
		if(psFirst)
		{
			psResult = psFirst;
		}
	}

	if(psResult AND !psResult->died)
	{
	 	selDroidDeselect(selectedPlayer);
//		psResult->selected = TRUE;
		SelectDroid(psResult);
		if(getWarCamStatus())
		{
			camToggleStatus();			 // messy - fix this
	//		setViewPos(psCentreDroid->x>>TILE_SHIFT,psCentreDroid->y>>TILE_SHIFT);
			processWarCam(); //odd, but necessary
			camToggleStatus();				// messy - FIXME
		}
		else
			if(!getWarCamStatus())
			{
//				camToggleStatus();
				/* Centre display on him if warcam isn't active */
				setViewPos(psResult->x>>TILE_SHIFT,psResult->y>>TILE_SHIFT,TRUE);
			}
		psOldRD = psResult;
	}
	else
	{
		switch(unitType)
		{
		case	DROID_REPAIR:
			addConsoleMessage(strresGetString(psStringRes,STR_GAM_REPNOTFOUND),LEFT_JUSTIFY);
			break;
		case	DROID_CONSTRUCT:
			addConsoleMessage(strresGetString(psStringRes,STR_GAM_CONNOTFOUND),LEFT_JUSTIFY);
			break;
		case	DROID_SENSOR:
			addConsoleMessage(strresGetString(psStringRes,STR_GAM_SENNOTFOUND),LEFT_JUSTIFY);
			break;
		case	DROID_COMMAND:
			addConsoleMessage(strresGetString(psStringRes,STR_GAM_COMNOTFOUND),LEFT_JUSTIFY);
			break;
		}
	}
}
#if 0
// ---------------------------------------------------------------------
void	selNextRepairUnit( void )
{
DROID	*psCurr;
DROID	*psResult;
DROID	*psFirst;
BOOL	bLaterInList;

	for(psCurr = apsDroidLists[selectedPlayer],psFirst = NULL,psResult = NULL,bLaterInList = FALSE;
		psCurr AND !psResult; psCurr = psCurr->psNext)
	{
		if( psCurr->droidType == DROID_REPAIR OR
            psCurr->droidType == DROID_CYBORG_REPAIR )
		{

		 	/* Always store away the first one we find */
			if(!psFirst)
			{
				psFirst = psCurr;
			}

			if(psCurr == psOldRD)
			{
				bLaterInList = TRUE;
			}

			/* Nothing previously found... */
			if(!psOldRD)
			{
				psResult = psCurr;
			}

			/* Only select is this isn't the old one and it's further on in list */
			else if(psCurr!=psOldRD AND bLaterInList)
			{
				psResult = psCurr;
			}

		 }
	}

	/* Did we get one? */
	if(!psResult)
	{
		/* was there at least one - the first one? Resetting */
		if(psFirst)
		{
			psResult = psFirst;
		}
	}

	if(psResult AND !psResult->died)
	{
	 	selDroidDeselect(selectedPlayer);
//		psResult->selected = TRUE;
		SelectDroid(psResult);
		if(getWarCamStatus())
		{
			camToggleStatus();			 // messy - fix this
	//		setViewPos(psCentreDroid->x>>TILE_SHIFT,psCentreDroid->y>>TILE_SHIFT);
			processWarCam(); //odd, but necessary
			camToggleStatus();				// messy - FIXME
		}
		else
			if(!getWarCamStatus())
			{
//				camToggleStatus();
				/* Centre display on him if warcam isn't active */
				setViewPos(psResult->x>>TILE_SHIFT,psResult->y>>TILE_SHIFT,TRUE);
			}
		psOldRD = psResult;
	}
	else
	{
		addConsoleMessage(strresGetString(psStringRes,STR_GAM_REPNOTFOUND),LEFT_JUSTIFY);
	}
}
// ---------------------------------------------------------------------
#endif
// ---------------------------------------------------------------------
void	selNextUnassignedUnit( void )
{
DROID	*psCurr;
DROID	*psResult;
DROID	*psFirst;
BOOL	bLaterInList;

	for(psCurr = apsDroidLists[selectedPlayer],psFirst = NULL,psResult = NULL,bLaterInList = FALSE;
		psCurr AND !psResult; psCurr = psCurr->psNext)
	{
		/* Only look at unselected ones */
		if(psCurr->group==UBYTE_MAX)
		{

		  	/* Keep a record of first one */
			if(!psFirst)
			{
				psFirst = psCurr;
			}

			if(psCurr == psOldNS)
			{
				bLaterInList = TRUE;
			}

			/* First one...? */
			if(!psOldNS)
			{
				psResult = psCurr;
			}

			/* Dont choose same one again */
			else if(psCurr!=psOldNS AND bLaterInList)
			{
				psResult = psCurr;
			}

		}
	}

	/* If we didn't get one - then select first one */
	if(!psResult)
	{
		if(psFirst)
		{
			psResult = psFirst;
		}
	}

	if(psResult AND !psResult->died)
	{
	 	selDroidDeselect(selectedPlayer);
//		psResult->selected = TRUE;
		SelectDroid(psResult);
		if(getWarCamStatus())
		{
			camToggleStatus();			 // messy - fix this
	//		setViewPos(psCentreDroid->x>>TILE_SHIFT,psCentreDroid->y>>TILE_SHIFT);
			processWarCam(); //odd, but necessary
			camToggleStatus();				// messy - FIXME
		}
		else
			if(!getWarCamStatus())
			{
//				camToggleStatus();
				/* Centre display on him if warcam isn't active */
				setViewPos(psResult->x>>TILE_SHIFT,psResult->y>>TILE_SHIFT,TRUE);
			}
		psOldNS = psResult;
	}
	else
	{
		addConsoleMessage(strresGetString(psStringRes,STR_GAM_REPNOTFOUND),LEFT_JUSTIFY);
	}
}
// ---------------------------------------------------------------------
void	selNextSpecifiedBuilding(UDWORD	structType)
{
STRUCTURE	*psCurr;
STRUCTURE	*psResult;
STRUCTURE	*psFirst;
BOOL		bLaterInList;

	/* Firstly, start coughing if the type is invalid */
	ASSERT( structType>=REF_HQ AND structType<=NUM_DIFF_BUILDINGS,
		"Invalid structure type in selNextSpecifiedBuilding" );

	for(psCurr = apsStructLists[selectedPlayer], psFirst = NULL,psResult = NULL,bLaterInList = FALSE;
		psCurr AND !psResult; psCurr = psCurr->psNext)
		{
			if(psCurr->pStructureType->type == structType)
			{
				if(!psFirst)
				{
					psFirst = psCurr;
				}
				if(psCurr == psOldStruct)
				{
					bLaterInList = TRUE;
				}
				if(!psOldStruct)
				{
					psResult = psCurr;
				}
				else if(psCurr!=psOldStruct AND bLaterInList)
				{
					psResult = psCurr;
				}
			}
		}

		if(!psResult)
		{
			if(psFirst)
			{
				psResult = psFirst;
			}
		}

		if(psResult AND !psResult->died)
		{
			if(getWarCamStatus())
			{
				camToggleStatus();
			}
			setViewPos(psResult->x>>TILE_SHIFT,psResult->y>>TILE_SHIFT,FALSE);
			psOldStruct = psResult;
		}
		else
		{
			// Can't find required building
			addConsoleMessage("Cannot find required building!",LEFT_JUSTIFY);
		}
}



// ---------------------------------------------------------------------

// see if a commander is the n'th command droid
BOOL droidIsCommanderNum(DROID *psDroid, SDWORD n)
{
	DROID	*psCurr;
	SDWORD	numLess;

	if (psDroid->droidType != DROID_COMMAND)
	{
		return FALSE;
	}

	numLess = 0;
	for(psCurr = apsDroidLists[psDroid->player]; psCurr; psCurr=psCurr->psNext)
	{
		if ((psCurr->droidType == DROID_COMMAND) &&
			(psCurr->id < psDroid->id))
		{
			numLess += 1;
		}
	}

	if (numLess == (n - 1))
	{
		return TRUE;
	}

	return FALSE;
}

// select the n'th command droid
void selCommander(SDWORD n)
{
	DROID	*psCurr;

	for(psCurr = apsDroidLists[selectedPlayer]; psCurr; psCurr=psCurr->psNext)
	{
		if (droidIsCommanderNum(psCurr, n))
		{
			if (!psCurr->selected)
			{
				clearSelection();
				psCurr->selected = TRUE;
			}
			else
			{
				clearSelection();
				psCurr->selected = TRUE;

				// this horrible bit of code is taken from activateGroupAndMove
				// and sets the camera position to that of the commander

				if(getWarCamStatus())
				{
					camToggleStatus();			 // messy - fix this
					processWarCam(); //odd, but necessary
					camToggleStatus();				// messy - FIXME
				}
				else
				if(!getWarCamStatus())
				{
					/* Centre display on him if warcam isn't active */
					setViewPos(psCurr->x>>TILE_SHIFT,psCurr->y>>TILE_SHIFT,TRUE);
				}

			}
			setSelectedCommander((UDWORD)n);
			return;
		}
	}
}

// ---------------------------------------------------------------------




