/*=============================================================================
    Name    : Tactical.c
    Purpose : Logic for the tactical overlay

    Created 8/8/1997 by lmoloney
    Copyright Relic Entertainment, Inc.  All rights reserved.
=============================================================================*/

#include <stdio.h>
#include "types.h"
#include "memory.h"
#include "debug.h"
#include "statscript.h"
#include "spaceobj.h"
#include "universe.h"
#include "teams.h"
#include "vector.h"
#include "tactical.h"
#include "ships.h"
#include "main.h"
#include "utility.h"
#include "render.h"
#include "alliance.h"
#include "sensors.h"
#include "blobs.h"

extern fonthandle selGroupFont2;

//located in mainrgn.c
void toMoveLineDraw(ShipPtr ship, real32 scale);
void toFieldSphereDraw(ShipPtr ship,real32 radius, real32 scale,color passedColour);
void toDrawRadialIndicator1(ShipPtr ship, real32 radius, real32 scale,color passedColour);
void toDrawRadialIndicator2(ShipPtr ship, real32 radius, real32 scale,color passedColour);
void toDrawRadialIndicator3(ShipPtr ship, real32 radius, real32 scale,color passedColour);
void toDrawRadialIndicator4(ShipPtr ship, real32 radius, real32 scale,color passedColour);

real32 mineMineTOSize;
real32 toMinimumSize;

/*=============================================================================
    Data:
=============================================================================*/
//basic, default TO icon
toicon toDefaultIcon =
{
    0, {0.0f ,0.0f}
};

//to icons for all classes of ships + 1 for mines
toicon *toClassIcon[NUM_CLASSES+1];

//colors for tactical overlay of various players
color toPlayerColor[TO_NumPlayers];

//for legend: track if a particular class is currently displayed (for each player)
bool8 toClassUsed[NUM_CLASSES+1][TO_NumPlayers];

void toNewClassSet(char *directory,char *field,void *dataToFillIn);
void toNumberPointsSet(char *directory,char *field,void *dataToFillIn);
void toVertexAdd(char *directory,char *field,void *dataToFillIn);
scriptEntry toIconTweaks[] =
{
    { "IconClass",      toNewClassSet,  NULL},
    { "nPoints",        toNumberPointsSet, NULL},
    { "vertex",         toVertexAdd,    NULL},
    { "playerColor0",   scriptSetRGBCB, &toPlayerColor[0]},
    { "playerColor1",   scriptSetRGBCB, &toPlayerColor[1]},
    { "playerColor2",   scriptSetRGBCB, &toPlayerColor[2]},
    { "playerColor3",   scriptSetRGBCB, &toPlayerColor[3]},
    { "playerColor4",   scriptSetRGBCB, &toPlayerColor[4]},
    { "playerColor5",   scriptSetRGBCB, &toPlayerColor[5]},
    { "playerColor6",   scriptSetRGBCB, &toPlayerColor[6]},
    { "playerColor7",   scriptSetRGBCB, &toPlayerColor[7]},
    { NULL, NULL, 0 }
};

//retained integrals used during loading
ShipClass toCurrentClass = CLASS_Mothership;
sdword toCurrentPoint = 0;

// x,y location for the player list in the main tactical overlay
sdword TO_PLAYERLIST_Y = 480;
sdword TO_PLAYERLIST_X = 10;

/*=============================================================================
    Functions:
=============================================================================*/
/*-----------------------------------------------------------------------------
    Name        : toStartup
    Description : Startup TO module.
    Inputs      : void
    Outputs     : Initializes the toClassIcon array.
    Return      : void
----------------------------------------------------------------------------*/
void toStartup(void)
{
    sdword index;

    mineMineTOSize = primScreenToGLScaleX(TO_MineMinimumScreenSize);
    toMinimumSize  = primScreenToGLScaleX(TO_MinimumScreenSize);

    for (index = 0; index < (NUM_CLASSES+1); index++)
    {                                                       //set all icons to default
        toClassIcon[index] = &toDefaultIcon;
    }
    scriptSet(NULL, "Tactical.script", toIconTweaks);
}

/*-----------------------------------------------------------------------------
    Name        : toShutdown
    Description : Shut down the TO module.
    Inputs      : void
    Outputs     : Frees the toIconTweaks array.
    Return      : void
----------------------------------------------------------------------------*/
void toShutdown(void)
{
    sdword index;

    for (index = 0; index < (NUM_CLASSES+1); index++)
    {                                                       //set all icons to default
        if (toClassIcon[index] != &toDefaultIcon && toClassIcon[index] != NULL)
        {
            memFree(toClassIcon[index]);
        }
    }
}

/*-----------------------------------------------------------------------------
    Script-read functions to load in the TO icons
-----------------------------------------------------------------------------*/
void toNewClassSet(char *directory,char *field,void *dataToFillIn)
{                                                           //set new ship class
    if (_stricmp(field,"CLASS_Mine") == 0)
    {
        toCurrentClass = NUM_CLASSES;   // use NUM_CLASSES to indicate mine
    }
    else
    {
        toCurrentClass = StrToShipClass(field);
    }
    dbgAssert(toCurrentClass < (NUM_CLASSES+1));
}
void toNumberPointsSet(char *directory,char *field,void *dataToFillIn)
{                                                           //set number of points and allocate structure
    sdword nPoints, nScanned;

    nScanned = sscanf(field, "%d", &nPoints);               //parse number of points
    dbgAssert(nScanned == 1);
    dbgAssert(nPoints > 0 && nPoints < 32);                 //!!! 32 is some arbitrary number
    toClassIcon[toCurrentClass] = memAlloc(toIconSize(nPoints),//allocate the icon structure
                                           "Tactical Overlay Icon", NonVolatile);
    toClassIcon[toCurrentClass]->nPoints = nPoints;
    toCurrentPoint = 0;                                     //no points loaded yet
}
void toVertexAdd(char *directory,char *field,void *dataToFillIn)
{
    real32 x, y;
    sdword nScanned;

    dbgAssert(toCurrentPoint < toClassIcon[toCurrentClass]->nPoints);//make sure not too many points
    nScanned = sscanf(field, "%f,%f", &x, &y);              //scan in the x/y
    dbgAssert(nScanned == 2);                               //make sure we got 2 numbers
    toClassIcon[toCurrentClass]->loc[toCurrentPoint].x =    //copy the points over
        x * TO_VertexScanFactorX;                           //multiplied by a scaling factor
    toClassIcon[toCurrentClass]->loc[toCurrentPoint].y =
        y * TO_VertexScanFactorY;
    toCurrentPoint++;                                       //next point
}

void toFieldSphereDrawGeneral(vector position, real32 radius,color passedColour);

/*-----------------------------------------------------------------------------
    Name        : toAllShipsDraw
    Description : Draw tactical overlays for all ships.
    Inputs      :
    Outputs     : Draw a tactical display icon overlay for all on-screen ships
    Return      : void
----------------------------------------------------------------------------*/
void toAllShipsDraw(void)
{
    Node *objnode = universe.RenderList.head;
    Ship *ship;
    real32 radius, scale;
    sdword index, player;
    toicon *icon;
    vector zero = {0,0,0};
    color c;
    udword intScale;

     // reset usage of classes (for legend)
    for (index = 0; index < (NUM_CLASSES+1); index++)
    {
          for (player = 0; player < TO_NumPlayers; ++player)
          {
               toClassUsed[index][player] = 0;
          }
    }

    rndTextureEnable(FALSE);
    rndLightingEnable(FALSE);

    while (objnode != NULL)
    {
          ship = (Ship *) listGetStructOfNode(objnode);

        if (ship->flags & SOF_Dead)
        {
            goto nextnode;
        }

        if (ship->collInfo.selCircleRadius <= 0.0f)
        {                                                   //if behind camera
            goto nextnode;                                  //don't draw it
        }

        if(ship->flags & SOF_Slaveable)
        {
            if(ship->slaveinfo->flags & SF_SLAVE)
            {
                goto nextnode;          //don't draw TO for SLAVES of some master ship
            }
        }

        if (ship->objtype == OBJ_MissileType)
        {
#define mine ((Missile *)ship)
            if (mine->missileType != MISSILE_Mine)
            {
                goto nextnode;
            }

            if (mine->playerowner != universe.curPlayerPtr)
            {
                goto nextnode;      // only show player's own mines
            }

            radius = mine->collInfo.selCircleRadius;

            if (radius < TO_MaxFadeSize)
            {

                //Get an index of the player to get the color.
#if TO_STANDARD_COLORS
                if (mine->playerowner == universe.curPlayerPtr)
                {
                    c = teFriendlyColor;
                }
                else if (allianceArePlayersAllied(mine->playerowner, universe.curPlayerPtr))
                {
                    c = teAlliedColor;
                }
                else
                {
                    c = teHostileColor;
                }
#else
                c = teColorSchemes[mine->colorScheme].tacticalColor;
#endif

                if (radius > TO_FadeStart)
                {
                    // scale the color - start fading out the overlay
                    scale = (radius - TO_MinFadeSize) * TO_FadeRate;
                    intScale = (udword)scale;
                    c = colRGB(colRed(c) / intScale, colGreen(c) / intScale, colBlue(c) / intScale);
                }

                icon = toClassIcon[NUM_CLASSES];    // NUM_CLASSES indicates Mine

                primLineLoopStart2(1, c);

                radius = max(mineMineTOSize, radius);   //ensure radius not too small

                for (index = icon->nPoints - 1; index >= 0; index--)
                {
                    primLineLoopPoint3F(mine->collInfo.selCircleX + icon->loc[index].x * radius,
                                        mine->collInfo.selCircleY + icon->loc[index].y * radius);
                }

                primLineLoopEnd2();
            }

        }
        else if (ship->objtype == OBJ_ShipType)
        {
            if (bitTest(ship->flags,SOF_Cloaked) && ship->playerowner != universe.curPlayerPtr)
            {                                                   //if cloaked, and doesn't belong to current player
                if(!proximityCanPlayerSeeShip(universe.curPlayerPtr,ship))
                {
                    goto nextnode;                                  //don't draw it
                }
            }

            if (ship->shiptype == Drone)
            {
                goto nextnode;
            }

            // tactical overlay (fades out when the ship gets large enough)
            radius = ship->collInfo.selCircleRadius * ship->staticinfo->tacticalSelectionScale;

            if (radius < TO_MaxFadeSize)
            {
                //Get an index of the player to get the color.
#if TO_STANDARD_COLORS
                if (ship->playerowner == universe.curPlayerPtr)
                {
                    c = teFriendlyColor;
                }
                else if (allianceIsShipAlly(ship, universe.curPlayerPtr))
                {
                    c = teAlliedColor;
                }
                else
                {
                    c = teHostileColor;
                }
#else
                c = teColorSchemes[ship->colorScheme].tacticalColor;
#endif

                if (radius > TO_FadeStart)
                {
                    // scale the color - start fading out the overlay
                    scale = 255.0f - ((radius - TO_MinFadeSize) * TO_FadeRate);
                    intScale = (udword)scale;
                    c = colRGB((colRed(c) * intScale) >> 8, (colGreen(c) * intScale) >> 8, (colBlue(c) * intScale) >> 8);
                }

                icon = toClassIcon[((ShipStaticInfo *)ship->staticinfo)->shipclass];
                primLineLoopStart2(1, c);

                radius = max(toMinimumSize, radius);   //ensure radius not too small

                for (index = icon->nPoints - 1; index >= 0; index--)
                {
                    primLineLoopPoint3F(ship->collInfo.selCircleX + icon->loc[index].x * radius,
                                        ship->collInfo.selCircleY + icon->loc[index].y * radius);
                }

                primLineLoopEnd2();

                     // mark class as used for this player (for legend)
                     toClassUsed[((ShipStaticInfo *)ship->staticinfo)->shipclass][ship->playerowner->playerIndex] = 1;
            }

            //for moving ships that belong to the current player, draw the moveline
            if ((!vecAreEqual(ship->moveTo, zero)) && (ship->playerowner == universe.curPlayerPtr))
            {
                toMoveLineDraw(ship, scale);
            }

            //Draw Special TO's for Special Ships
            if(ship->playerowner == universe.curPlayerPtr)
            {
                switch(ship->shiptype)
                {
                case CloakGenerator:
                    if( ((CloakGeneratorSpec *)ship->ShipSpecifics)->CloakOn)
                    {
                        toFieldSphereDraw(ship,((CloakGeneratorStatics *) ((ShipStaticInfo *)(ship->staticinfo))->custstatinfo)->CloakingRadius, scale,TW_CLOAKGENERATOR_SPHERE_COLOUR);
                    }
                    break;
                case DFGFrigate:
                    toFieldSphereDraw(ship,((DFGFrigateStatics *) ((ShipStaticInfo *)(ship->staticinfo))->custstatinfo)->DFGFrigateFieldRadius, scale,TW_DFGF_SPHERE_COLOUR);
                    break;
                case GravWellGenerator:
                    if (((GravWellGeneratorSpec *)ship->ShipSpecifics)->GravFieldOn)
                    {
                        toFieldSphereDraw(ship,((GravWellGeneratorStatics *) ((ShipStaticInfo *)(ship->staticinfo))->custstatinfo)->GravWellRadius, scale,TW_GRAVWELL_SPHERE_COLOUR);
                    }
                    break;
                case ProximitySensor:
                    //toFieldSphereDraw(ship,((ProximitySensorStatics *) ((ShipStaticInfo *)(ship->staticinfo))->custstatinfo)->SearchRadius, scale);
                   if( ((ProximitySensorSpec *)ship->ShipSpecifics)->sensorState == SENSOR_SENSED
                        || ((ProximitySensorSpec *)ship->ShipSpecifics)->sensorState == SENSOR_SENSED2)
                   {
                       toDrawRadialIndicator4(ship,((ProximitySensorStatics *) ((ShipStaticInfo *)(ship->staticinfo))->custstatinfo)->SearchRadius, scale,TW_PROXIMITY_RING_COLOUR);
                       toFieldSphereDraw(ship,((ProximitySensorStatics *) ((ShipStaticInfo *)(ship->staticinfo))->custstatinfo)->SearchRadius, scale,TW_PROXIMITY_SPHERE_COLOUR_FOUND);
                   }
                   else
                   {
                       toFieldSphereDraw(ship,((ProximitySensorStatics *) ((ShipStaticInfo *)(ship->staticinfo))->custstatinfo)->SearchRadius, scale,TW_PROXIMITY_SPHERE_COLOUR);
                   }
                }
            }
        }

nextnode:
        objnode = objnode->next;
    }

#define DEBUG_DRAW_BLOBS 0

#if DEBUG_DRAW_BLOBS


    {
        blob *Blob;

        objnode = universe.collBlobList.head;
        while (objnode != NULL)
        {
            Blob = (blob *)listGetStructOfNode(objnode);

            toFieldSphereDrawGeneral(Blob->centre,Blob->radius,colReddish);

            objnode = objnode->next;
        }
    }
#endif
}

/*-----------------------------------------------------------------------------
    Name        : toLegendDraw
    Description : Draw tactical overlay legend.
    Inputs      : toClassUsed is set with a previous call to toAllShipsDraw()
    Outputs     : Draw a legend for the tactical display icons.
    Return      : void
----------------------------------------------------------------------------*/
void toLegendDraw(void)
{
    sdword rowHeight, shipClass, pl, y, index, x;
    toicon *icon;
    color col;
    real32 radius;
    fonthandle fhSave;
    rectangle playerColorRect;

    fhSave = fontCurrentGet();                  //save the current font
    fontMakeCurrent(selGroupFont2);  // use a common, fairly small font

    rowHeight = fontHeight(" "); // used to space the legend
    y = rowHeight;
    radius = primScreenToGLScaleX(rowHeight)/2;
    x = (sdword)(rowHeight * 2.5);

#if TO_STANDARD_COLORS
    col = teNeutralColor;
#else
    col = teColorSchemes[universe.curPlayerIndex].tacticalColor;
#endif
    col = colRGB(colRed(col)/TO_IconColorFade, colGreen(col)/TO_IconColorFade, colBlue(col)/TO_IconColorFade);

    // draw legend entries for any classes currently displayed
    for (shipClass = CLASS_Mothership+1; shipClass <= CLASS_NonCombat; ++shipClass)
    {
        for (pl = 0; pl < TO_NumPlayers; ++pl)
        {
            if (toClassUsed[shipClass][pl])
                break;
        }
        if (pl < TO_NumPlayers)
        {
            // icon
            icon = toClassIcon[shipClass];
            primLineLoopStart2(1, col);

            for (index = icon->nPoints - 1; index >= 0; index--)
            {
               primLineLoopPoint3F(primScreenToGLX(rowHeight*1.5) + icon->loc[index].x * radius,
                                          primScreenToGLY(y + rowHeight/2) + icon->loc[index].y * radius);
            }
            primLineLoopEnd2();

            // text
            fontPrint(x, y,
                   TO_TextColor,
                   ShipClassToNiceStr((ShipClass)shipClass));
            y += rowHeight + 1;
        }
    }

    // draw player names in the bottom left of the screen.

    if (!singlePlayerGame)
    {
        playerColorRect.x0 = TO_PLAYERLIST_X;
        playerColorRect.x1 = MAIN_WindowWidth;
        playerColorRect.y0 = 0;
        playerColorRect.y1 = MAIN_WindowHeight-TO_PLAYERLIST_Y;

        smPlayerNamesDraw(&playerColorRect);
    }

    fontMakeCurrent(fhSave);
}
