/*=============================================================================
    Name    : AITeam.h
    Purpose : Definitions for AITeam.c

    Created 5/31/1998 by dstone
    Copyright Relic Entertainment, Inc.  All rights reserved.
=============================================================================*/

#ifndef __AITEAM_H
#define __AITEAM_H

#include "types.h"
#include "spaceobj.h"
#include "shipselect.h"
#include "aievents.h"
#include "formation.h"
#include "FormationDefs.h"
#include "AIVar.h"
#include "vector.h"
#include "AIUtilities.h"
#include "KAS.h"
#include "Volume.h"

//team flags
// Mask 0xff00000000 to Special Team Flags
#define AIT_SpecialTeamMask     0xff000000
#define FAST_ROVING_GUARD       0x00000001
#define SLOW_ROVING_GUARD       0x00000002
#define HARASS_TEAM             0x00000004
#define RESOURCECOLLECT_TEAM    0x00000008

#define AIT_DestroyTeam         0x00000010      //flag set to destroy the team in a safe place
#define AIT_Reissue01			0x00000020		//flag set to keep certain orders from being reissued over and over again
#define AIT_Reissue10			0x00000040		//flag set to keep certain orders from being reissued over and over again

#define TEAM_HOMOGENEOUS        0x00000100
#define TEAM_HETEROGENEOUS      0x00000200

#define TEAM_NEEDS_SUPPORT      0x00001000      //team needs a support ship
#define TEAM_GETTING_SUPPORT    0x00002000      //a support ship is near the team
#define TEAM_DOCKING            0x00004000
#define TEAM_RETREATING         0x00008000
#define TEAM_ARMADA             0x00010000
#define TEAM_Hyperspaceable     0x00020000      //the team can hyperspace (no fighters guarding or anything)
#define TEAM_TempGuard			0x00040000		//the team is an armada team temp guarding

#define TEAM_SpecialOptimal     0x00100000      //denotes whether the special team has setup with optimal targets/partners
#define TEAM_AdvancedMoveAttack 0x00200000      //the MoveAttack move will pick out targets individually
#define TEAM_SwarmTarget        0x00400000      //set when a swarm team should get it's target from the Targets structure
#define TEAM_AmIBeingWatched    0x00800000      //set when a swarm team is checking if it's being watched

#define TEAM_CloakFighters      0x01000000
#define TEAM_CloakedFighters    0x02000000
#define TEAM_CloakGenerator     0x04000000
#define TEAM_CloakedGenerator   0x08000000

//#define TEAM_GravWellGenerator  0x10000000
//#define TEAM_GravWellGenerating 0x20000000
#define TEAM_CloakCoop          0x10000000      //is cooperating with a cloak generator team

//swarmer stuff
#define MAX_NEW_SWARMERS    15

#define MAX_SPLIT           4

typedef enum {
    MOVE_DONE,                   //0
    MOVE_GUARDSHIPS,             //1
    MOVE_DEFMOSHIP,              //2
    MOVE_TEMPGUARD,              //3
    MOVE_GETSHIPS,               //4
    MOVE_FORMATION,              //5
    MOVE_MOVETEAM,               //6
    MOVE_MOVETEAMINDEX,          //7
    MOVE_MOVETEAMSPLIT,          //8
    MOVE_INTERCEPT,              //9
    MOVE_MOVETO,                 //10
    MOVE_PATROLMOVE,             //11
    MOVE_ACTIVEPATROL,           //12
    MOVE_ACTIVERECON,            //13
    MOVE_SHIPRECON,              //14
    MOVE_COUNTSHIPS,             //15
    MOVE_SPECIAL,                //16
    MOVE_ATTACK,                 //17
    MOVE_ADVANCEDATTACK,         //18
    MOVE_FLANKATTACK,            //19
    MOVE_MOVEATTACK,             //20
    MOVE_HARASSATTACK,           //21
    MOVE_SWARMATTACK,            //22
    MOVE_SWARMDEFENSE,           //23
    MOVE_SWARMPOD,               //24
    MOVE_FANCYGETSHIPS,          //25
    MOVE_DOCK,                   //26
    MOVE_LAUNCH,                 //27
    MOVE_REINFORCE,              //28
    MOVE_VARSET,                 //29
    MOVE_VARINC,                 //30
    MOVE_VARDEC,                 //31
    MOVE_VARWAIT,                //32
    MOVE_VARDESTROY,             //33
    MOVE_GUARDCOOPTEAM,          //34
    MOVE_SUPPORT,                //35
    MOVE_ARMADA,                 //36
    MOVE_CONTROLRESOURCES,       //37
    MOVE_RESVOLUME,              //38
    MOVE_CAPTURE,                //39
    MOVE_ACTIVECAPTURE,          //40
    MOVE_ACTIVEMINE,             //41
    MOVE_MINEVOLUME,             //42
    MOVE_SPECIALDEFENSE,         //43
    MOVE_ACTIVERES,              //44
    MOVE_MOTHERSHIP,             //45
    MOVE_KAMIKAZE,               //46
    MOVE_HYPERSPACE,             //47
    MOVE_DELETETEAM,             //48
    NUMBER_OF_MOVETYPES    // must go last
} AIMoveTypes;

typedef struct {
    ShipType  shipType;
    sdword    numShips;
    AIVar     *doneVar;
    sdword    priority;
} paramsGetShips;

#define MAX_NUM_ALTERNATIVES            12      // make sure it's a multiple of 4 for alignment purposes
#define ALTERNATIVE_SHIP_BASE           10      // base value for equivalence comparisons between ships
#define ALTERNATIVE_RANDOM              1       // alternativeFlags - indicates alternatives should be picked randomly

#define AIT_FAST_PATROL                 0
#define AIT_SLOW_PATROL                 1

typedef struct AlternativeShips
{
    sdword  numNextPicks;
    sdword  alternativeFlags;
    sbyte   shipTypeNextPicks[MAX_NUM_ALTERNATIVES];
    sbyte   shipNumEquivNextPicks[MAX_NUM_ALTERNATIVES];
} AlternativeShips;

#define SetNumAlternatives(alt,num) \
    alt##.numNextPicks = num;   \
    alt##.alternativeFlags = 0; \
    dbgAssert(num <= MAX_NUM_ALTERNATIVES);

#define SetNumAlternativesFlags(alt,num,flags) \
    alt##.numNextPicks = num;   \
    alt##.alternativeFlags = flags; \
    dbgAssert(num <= MAX_NUM_ALTERNATIVES);

#define SetAlternative(alt,index,stype,equivnum) \
    alt##.shipTypeNextPicks[index] = (sbyte)stype;  \
    alt##.shipNumEquivNextPicks[index] = equivnum;

typedef struct {
    char varName[AIVAR_LABEL_MAX_LENGTH+1];
    sdword value;
} paramsVarSet;

typedef struct {
    char varName[AIVAR_LABEL_MAX_LENGTH+1];
    sdword value;
} paramsVarWait;

typedef struct {
    char varName[AIVAR_LABEL_MAX_LENGTH+1];
} paramsVarInc;

typedef struct {
    char varName[AIVAR_LABEL_MAX_LENGTH+1];
} paramsVarDec;

typedef struct {
    char varName[AIVAR_LABEL_MAX_LENGTH+1];
} paramsVarDestroy;

typedef struct {
    ShipType shipType;
    sdword   numShips;
    AIVar    *doneVar;
    sdword   priority;
    AlternativeShips alternatives;
} paramsFancyGetShips;

typedef struct {
    SelectCommand *ships;
} paramsGuardShips;

typedef struct
{
    SelectCommand *targets;
} paramsDefMoship;

typedef struct
{
    //nothing yet
    udword placeholder;
} paramsTempGuard;

typedef struct {
    SelectCommand *ships;
} paramsKamikaze;

typedef struct {
    SelectCommand *ships;
} paramsAttackShips;

typedef struct
{
    SelectCommand *targets;
    Ship *target_ship;
} paramsAdvancedAttack;

typedef struct
{
    SelectCommand *targets;
    bool8          hyperspace;
} paramsFlankAttack;

typedef struct
{
    SelectCommand *targets;
    ShipPtr target_ship;
    vector  destination;
} paramsMoveAttack;

typedef struct
{
    vector destination;
    udword index;
} paramsMoveTeam;

typedef struct
{
    SelectCommand *ships;
    Path *destinations;
} paramsMoveSplit;

typedef struct
{
    ShipPtr ship;
    real32  interval;
    real32  next_int;
    vector  destination;
    bool    moving;
} paramsIntercept;

typedef struct
{
    vector destination;
    vector source;
    real32 limiter;
    real32 timer;
    udword type;
} paramsMoveTo;

typedef struct
{
    Path *path;
    struct AITeamMove *loopMove;
    udword startIndex;
} paramsPatrolMove;

typedef struct
{
    udword patroltype;
} paramsActivePatrol;

typedef struct
{
    bool outwards;
    bool enemyrecon;
} paramsActiveRecon;

typedef struct
{
    SelectCommand *ships;
    SelectCommand *foundships;
} paramsShipRecon;

typedef struct
{
    //nothing yet
    udword placeholder;
} paramsCountShips;

typedef struct {
    TypeOfFormation formationtype;
} paramsFormation;

typedef struct
{
    ShipPtr target;
} paramsHarassAttack;

typedef struct
{
    SelectCommand *targets;
    SelectCommand *othertargets;
    GrowSelection newSwarmers;
//    ShipType      targettype;
} paramsSwarmAttack;

typedef struct
{
    GrowSelection newSwarmers;
    SelectCommand *guarding;
    SelectCommand *Pods;
    bool           full_attack;
    bool           full_refuel;
} paramsSwarmDefense;

typedef struct
{
    bool first_attack;
    udword attack_delay;
} paramsSwarmPod;

#define dockmoveFlags_Normal            0
#define dockmoveFlags_AtShip            1
#define dockmoveFlags_Stay              2
#define dockmoveFlags_Instantaneously   4
typedef struct
{
    SelectCommand *shipsDocking;
    ShipPtr dockAt;
    sdword dockmoveFlags;
} paramsDock;

typedef struct
{
    struct AITeam *reinforceteam;
} paramsReinforce;

typedef struct
{
    SelectCommand *ships;
} paramsSupport;

typedef struct
{
    Volume  volume;
    ResourceSelection *volResources;
    ResourceSelection *takenResources;
    bool8 strictVolume;
} paramsResVol;


typedef struct
{
    ShipPtr ship;
} paramsCapture;

typedef struct
{
    Volume volume;
    bool   inposition;
} paramsMineVol;

//
//  teams
//
#define MSG_QUEUE_MAX_MSGS 32
typedef struct {
    sdword          head;
    char            *msgs[MSG_QUEUE_MAX_MSGS];
    struct AITeam   *msgSenders[MSG_QUEUE_MAX_MSGS];  // team that sent the corresponding msg above
                                                      // NOTE:  if a team dies, the message will be deleted from this queue
} MsgQueue;

typedef enum {
    AttackTeam,
    DefenseTeam,
    ResourceTeam,
    ScriptTeam,
    AnyTeam,        //neuter type
    NumTeamType
} TeamType;

typedef struct AITeam {
    TeamType          teamType;
    udword            teamFlags;
    udword            TeamFeatures;
    ubyte             teamDelay;
    udword            newships;
    struct AIPlayer   *aiplayerowner;
    GrowSelection     shipList;
    udword            teamStrength;
    udword            teamValue;
    ubyte             teamDifficultyLevel;
    LinkedList        moves;
    struct AITeamMove *curMove;
    void              *custTeamInfo;
    void (*TeamDiedCB)(struct AITeam *team);
    struct AITeam     *cooperatingTeam;
    void (*cooperatingTeamDiedCB)(struct AITeam *team);
    MsgQueue          *msgQueue;
    struct AITeam     *msgSender;

    // KAS attributes (if teamType == SCRIPT_TEAM)
    char                kasLabel[KAS_TEAM_NAME_MAX_LENGTH+1];
    char                kasFSMName[KAS_FSM_NAME_MAX_LENGTH+1];
    char                kasStateName[KAS_STATE_NAME_MAX_LENGTH+1];
    KASWatchFunction    kasFSMWatchFunction;
    KASWatchFunction    kasStateWatchFunction;
    ShipType            kasOrigShipsType;  // predominant ship type of this team (in mission layout file)
    sdword              kasOrigShipsCount; // original size of team (in mission layout file)
    TacticsType         kasTactics;  // current tactics setting, passed to certain moves
    TypeOfFormation     kasFormation; // current formation setting, passed to certain moves
} AITeam;

// all move processing functions look like this:
// (returning 1 when we this move has completed
//  and any required waiting is finished, 0 otherwise)
typedef sdword (*aimProcessFunction) (AITeam *team);
typedef void (*aimShipDied) (AITeam *team, struct AITeamMove *move, Ship *ship);
typedef void (*aimCloseFunction) (AITeam *team, struct AITeamMove *move);
typedef void (*aimResourceDied) (AITeam *team, struct AITeamMove *move, Resource *resource);

typedef struct AITeamMove {
    Node listNode;
    AIMoveTypes type;

    union {
        paramsGuardShips        guardShips;
        paramsDefMoship         defmoship;
        paramsTempGuard         tempguard;
        paramsGetShips          getShips;
        paramsFormation         formation;
        paramsMoveTeam          move;
        paramsMoveSplit         movesplit;
        paramsIntercept         intercept;
        paramsMoveTo            moveTo;
        paramsPatrolMove        patrolmove;
        paramsActivePatrol      activepatrol;
        paramsActiveRecon       activerecon;
        paramsShipRecon         shiprecon;
        paramsCountShips        countShips;
        paramsAttackShips       attack;
        paramsAdvancedAttack    advatt;
        paramsFlankAttack       flankatt;
        paramsMoveAttack        moveatt;
        paramsHarassAttack      harass;
        paramsSwarmAttack       swarmatt;
        paramsSwarmDefense      swarmdef;
        paramsSwarmPod          swarmpod;
        paramsFancyGetShips     fancyGetShips;
        paramsDock              dock;
        paramsReinforce         reinforce;
        paramsSupport           support;
        paramsSupport           rescontrol;
        paramsResVol            resvolume;
        paramsCapture           capture;
        paramsMineVol           minevolume;
        paramsVarSet            varSet;
        paramsVarInc            varInc;
        paramsVarDec            varDec;
        paramsVarWait           varWait;
        paramsVarDestroy        varDestroy;
        paramsKamikaze          kamikaze;

        // ...
    } params;

    AIEvents events;

    aimProcessFunction processFunction;
    aimShipDied moveShipDiedFunction;
    aimCloseFunction moveCloseFunction;

    //only for resource oriented moves
    aimResourceDied moveResourceDiedFunction;

    bool8 processing;   // if the process function has starting working on this move yet

    bool8 wait;    // non-zero = wait for the move to finish
    bool8 remove;  // non-zero = remove this move from the move list when it has finished
    TypeOfFormation formation;
    TacticsType     tactics;

} AITeamMove;

/*=============================================================================
    Macros:
=============================================================================*/
#define InitNewMove(newMove,movetype,waitflag,removeflag,form,tactic,moveprocessfunc,moveshipdiedfunc,moveclosefunc)    \
    newMove##->type                     = movetype;               \
    newMove##->processing               = FALSE;                  \
    newMove##->wait                     = waitflag;               \
    newMove##->remove                   = removeflag;             \
    newMove##->formation                = form;                   \
    dbgAssert(tactic >= 0);                                       \
    dbgAssert(tactic < NUM_TACTICS_TYPES);                        \
    newMove##->tactics                  = tactic;                 \
    newMove##->processFunction          = moveprocessfunc;        \
    newMove##->moveShipDiedFunction     = moveshipdiedfunc;       \
    newMove##->moveCloseFunction        = moveclosefunc;          \
    aieHandlersClear(newMove)

#define FixMoveFuncPtrs(move,moveprocessfunc,moveshipdiedfunc,moveclosefunc)     \
    move##->processFunction          = moveprocessfunc;        \
    move##->moveShipDiedFunction     = moveshipdiedfunc;       \
    move##->moveCloseFunction        = moveclosefunc

#define aitApproxTeamPos(team)          ((team)->shipList.selection->ShipPtr[0]->posinfo.position)
#define aitNumTeamShips(team)           ((team)->shipList.selection->numShips)
#define aitTeamShipTypeIs(type, team)   (((team)->shipList.selection->numShips) && ((team)->shipList.selection->ShipPtr[0]->shiptype == (type)))
#define aitTeamShipClassIs(cls, team)   (((team)->shipList.selection->numShips) && ((team)->shipList.selection->ShipPtr[0]->staticinfo->shipclass == (cls)))

/*=============================================================================
    Function Prototypes:
=============================================================================*/
//teams initialization and closing routines
void aitInit(struct AIPlayer *aiplayer);
void aitClose(struct AIPlayer *aiplayer);


/*-----------------------------------------------------------------------------
    General Utility Functions:
-----------------------------------------------------------------------------*/
void aitAddmoveBeforeAndMakeCurrent(AITeam *team, AITeamMove *newMove, AITeamMove *thisMove);
void aitAddmoveBeforeAndMakeCurrentNoSpecial(AITeam *team, AITeamMove *newMove, AITeamMove *thisMove);

/*-----------------------------------------------------------------------------
    Team State Query Functions:
-----------------------------------------------------------------------------*/
bool aitAnyTeamOfPlayerAttackingThisShip(struct AIPlayer *aiplayer,Ship *ship);

udword aitFindNumTeamsWithFlag(udword flag);
AITeam *aitFindNextTeamWithFlag(AITeam *team, udword flag);

sdword aitCountTeamsWaitingForShips(TeamType type);

//team status functions
bool aitTeamIsDone(AITeam *team);
bool aitTeamIsIdle(AITeam *team);
bool aitTeamIsGuarding(AITeam *team);
bool aitTeamIsAttacking(AITeam *team);
bool aitTeamIsntDefendingMothership(AITeam *team, SelectCommand *enemyships);
bool aitTeamIsFinishedMoving(AITeam *team, vector destination, real32 range);
bool aitTeamIsDoingSpecialOp(AITeam *team);
bool aitCheckIfOtherDefTeamAnsweringSignalNeedsHelp(AITeam *team, SelectCommand *ships);
bool aitTeamIsInRange(AITeam *team, ShipPtr ship, real32 time);
bool aitTeamIsInMothershipRange(AITeam *team);

void aitRecallGuardTeam(AITeam *team);
bool aitNeedStrikeSupport(udword minstr);

//set team attributes
void aitMakeTeamSupportShips(AITeam *team, SelectCommand *ships);
void aitSetTeamHomoHetero(AITeam *team);
void aitSetTeamSpecialFlags(AITeam *team);
bool aitTeamHomogenous(AITeam *team);
void aitSetTeamStrengthValue(AITeam *team);
ubyte aitSetTeamDifficultyLevel(AITeam *team, udword playerDL, udword teamDL);

AITeam *aitFindMostValuable(udword valueness);

AITeam *aitFindGoodCoopTeam(ShipType type);

AITeam *aitCreate(TeamType teamType);
void aitDestroy(struct AIPlayer *aiplayer, AITeam *team, bool removeAllReferencesToTeam);

void aitDeleteCurrentMove(AITeam *team);
void aitDeleteMovesUntilMoveType(AITeam *team, AIMoveTypes type);
void aitDeleteAllTeamMoves(AITeam *team);

void aitAddShip(AITeam *team, ShipPtr ship);

//swarm specific code
void aitMoveSwarmShipDefenseToAttack(AITeam *attackSwarm, AITeam *defenseSwarm, ShipPtr ship);
void aitMoveAllSwarmShipsDefenseToAttack(AITeam *defenseSwarm, AITeam *attackSwarm);
void aitMoveAllSwarmShipsAttackToDefense(AITeam *attackSwarm, AITeam *defenseSwarm);
void aitMoveAllSwarmShipsDefense(AITeam *destTeam, AITeam *sourceTeam);
void aitMoveAllSwarmShipsAttack(AITeam *destTeam, AITeam *sourceTeam);
AITeam *aitFindNewPod(AITeam *defenseTeam);
void aitMoveSwarmersToNewPod(AITeam *defenseTeam, AITeam *podTeam);
bool aitAllDefenseSwarmersFull(void);

void aitSpecialDefenseCoopTeamDiedCB(AITeam *team);
void GenericCooperatingTeamDiedCB(AITeam *team);

// returns TRUE if removed ship
bool aitRemoveShip(AITeam *team, ShipPtr ship);

//process current move for each team
void aitExecute(void);


void aitShipDied(struct AIPlayer *aiplayer,ShipPtr ship);
void aitResourceDied(struct AIPlayer *aiplayer, Resource *resource);

bool aitCheckForLeaderAndMoveToFront(AITeam *team);

bool aitCheckAmIBeingWatched(AITeam *team, SelectCommand *sel);
void aitSetAmIBeingWatched(AITeam *team, SelectCommand *sel);


sdword aitMsgReceived(AITeam *teamp, char *msg);
void aitMsgSend(AITeam *fromTeamp, AITeam *teamp, char *msg);
void aitMsgQueueFree(AITeam *teamp);

void aitSave(struct AIPlayer *aiplayer);
void aitLoad(struct AIPlayer *aiplayer);
void aitFix(struct AIPlayer *aiplayer);

//
//  reserved pointers to all allocated AIVars
//
#define AITEAM_ALLOC_INITIAL   64
#define AITEAM_ALLOC_INCREMENT 32

/*=============================================================================
    Globals:
=============================================================================*/

extern AITeam *savingThisAITeam;

#endif
