/*=============================================================================
    Name    : ProximitySensor.c
    Purpose : Specifics for the ProximitySensor

    Created 01/06/1998 by bpasechn
    Copyright Relic Entertainment, Inc.  All rights reserved.
=============================================================================*/

#include "types.h"
#include "spaceobj.h"
#include "ProximitySensor.h"
#include "SoundEvent.h"
#include "universe.h"
#include "memory.h"
#include "univupdate.h"
#include "linkedlist.h"
#include "fastmath.h"
#include "vector.h"
#include "ping.h"
#include "blobs.h"
#include "madlinkin.h"
#include "madlinkindefs.h"
#include "battle.h"
#include "Alliance.h"


/*=============================================================================
    Data:
=============================================================================*/
color ProximitySensorBlipColor = colWhite;

ProximitySensorStatics ProximitySensorStatic;

ProximitySensorStatics ProximitySensorStaticRace1;
ProximitySensorStatics ProximitySensorStaticRace2;

scriptStructEntry ProximitySensorStaticScriptTable[] =
{
    { "SearchRate",    scriptSetUdwordCB, (udword) &(ProximitySensorStatic.SearchRate), (udword) &(ProximitySensorStatic) },
    { "SearchRadius",    scriptSetReal32CB, (udword) &(ProximitySensorStatic.SearchRadius), (udword) &(ProximitySensorStatic) },
    { "SensorCircleRadius",    scriptSetSdwordCB, (udword) &(ProximitySensorStatic.SensorCircleRadius), (udword) &(ProximitySensorStatic) },
    { "SensorBlinkRate",    scriptSetReal32CB, (udword) &(ProximitySensorStatic.SensorBlinkRate), (udword) &(ProximitySensorStatic) },
    { "SearchRateAfterFound",    scriptSetUdwordCB, (udword) &(ProximitySensorStatic.SearchRateAfterFound), (udword) &(ProximitySensorStatic) },
    { "TriggerSpeed",    scriptSetReal32CB, (udword) &(ProximitySensorStatic.TriggerSpeed), (udword) &(ProximitySensorStatic) },

    { "blipColor",    scriptSetRGBACB, (udword) &(ProximitySensorStatic.blipColor), (udword) &(ProximitySensorStatic) },
    { "blipThickness",    scriptSetSdwordCB, (udword) &(ProximitySensorStatic.blipThickness), (udword) &(ProximitySensorStatic) },

    { NULL,NULL,0,0 }
};


void ProximitySensorStaticInit(char *directory,char *filename,struct ShipStaticInfo *statinfo)
{
    ProximitySensorStatics *ProximitySensorstat = (statinfo->shiprace == R1) ? &ProximitySensorStaticRace1 : &ProximitySensorStaticRace2;

    statinfo->custstatinfo = ProximitySensorstat;
    scriptSetStruct(directory,filename,ProximitySensorStaticScriptTable,(ubyte *)ProximitySensorstat);

    ProximitySensorstat->SearchRadiusSqr = ProximitySensorstat->SearchRadius*ProximitySensorstat->SearchRadius;
    ProximitySensorstat->TriggerSpeedSqr = ProximitySensorstat->TriggerSpeed*ProximitySensorstat->TriggerSpeed;
}

void ProximitySensorInit(Ship *ship)
{
    ProximitySensorSpec *spec = (ProximitySensorSpec *)ship->ShipSpecifics;
    spec->sensorState = SENSOR_BEGIN;
    spec->blipRadius = 0.0f;
    spec->TAGGED = FALSE;
}

void proxInFocusSelection(SelectCommand *focus)
{
    sdword count;
    for(count = 0; count < focus->numShips; count++)
    {
        if(focus->ShipPtr[count]->shiptype == ProximitySensor)
        {   //proximity sensor is in the selection
            ((ProximitySensorSpec *)((focus->ShipPtr[count])->ShipSpecifics))->TAGGED = TRUE;
        }
    }
}

bool proxShouldDrawOverlay(Ship *ship)
{
    ProximitySensorSpec *spec = (ProximitySensorSpec *)ship->ShipSpecifics;
    if(ship->playerowner == universe.curPlayerPtr)      //draw because it is local players...
        if(spec->sensorState == SENSOR_SENSED || spec->sensorState == SENSOR_SENSED2)
            return(TRUE);

    return(FALSE);
}
udword proxBlipColor(Ship *ship)
{
    ProximitySensorStatics *proximitysensorstatics;
    proximitysensorstatics = (ProximitySensorStatics *) ((ShipStaticInfo *)(ship->staticinfo))->custstatinfo;
    ProximitySensorBlipColor = proximitysensorstatics->blipColor;
    return(ProximitySensorBlipColor);
}
sdword proxBlipThickness(Ship *ship)
{
    ProximitySensorStatics *proximitysensorstatics;
    proximitysensorstatics = (ProximitySensorStatics *) ((ShipStaticInfo *)(ship->staticinfo))->custstatinfo;
    return(proximitysensorstatics->blipThickness);
}

//function tags ship so that ai knows player can see the ship.  If ship is cloaked,

/*-----------------------------------------------------------------------------
    Name        : proximityPlayerSeesShip
    Description : Tags 'enemy' ship such that the owner of 'ship'
                  can see the enemy ship
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void proximityPlayerSeesShip(Ship *ship,Ship *enemy)
{
    sdword mask = 0x01;
    if(ship->playerowner == NULL)
        return;
    mask = mask << ship->playerowner->playerIndex;
    bitSet(enemy->visibleToWho,mask);

    if(ship->playerowner->playerIndex == universe.curPlayerIndex)
    {
        //local machines ship
        if(enemy->flags & SOF_Cloaked)
        {
            //enemy is cloaked
            ////////////////////
            //speech event indicating such
            //event num: STAT_F_CloakedShipsDetected
            //battle chatter!
            if (battleCanChatterAtThisTime(BCE_STAT_F_CloakedShipsDetected, ship))
            {
                battleChatterFleetAttempt(SOUND_EVENT_DEFAULT, BCE_STAT_F_CloakedShipsDetected, SOUND_EVENT_DEFAULT, &enemy->posinfo.position);
            }

            ///////////////
        }
    }

}

/*-----------------------------------------------------------------------------
    Name        : proximityPlayerCantSeeShip
    Description : Untags Tags 'enemy' ship such that the owner of 'ship'
                  can't see the enemy ship
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void proximityPlayerCantSeeShip(Ship *ship,Ship *enemy)
{
    sdword mask = 0x01;
    if(ship->playerowner == NULL)
        return;
    mask = mask << ship->playerowner->playerIndex;
    bitClear(enemy->visibleToWho,mask);
}


/*-----------------------------------------------------------------------------
    Name        : proximityCanPlayerSeeShip
    Description : determins if player can see shipInQuestion
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/

bool proximityCanPlayerSeeShip(Player *player,Ship *shipInQuestion)
{
    sdword mask = 0x01;
    if(player == NULL)
        return FALSE;

    mask = mask << player->playerIndex;
    if(bitTest(shipInQuestion->visibleToWho,mask))
    {
        //player can see ship
        return TRUE;
    }
    return FALSE;
}

/*-----------------------------------------------------------------------------
    Name        : DetectShips
    Description : searchs ships blob for enemy and cloaked ships nearby
    Inputs      :
    Outputs     :
    Return      : TRUE if enemy ships nearby
----------------------------------------------------------------------------*/

bool DetectShips(Ship *ship)
{
    Ship *enemy;
    vector dist;
    bool returnval;
    ProximitySensorStatics *proximitysensorstatics;
    SelectCommand *selection;
    sdword numShips;
    sdword i;

    proximitysensorstatics = (ProximitySensorStatics *) ((ShipStaticInfo *)(ship->staticinfo))->custstatinfo;

    //Optimize detection...
    //Use collision avoidence to 'tell' proximity sensors of ships near it???

    if(ship->collMyBlob == NULL)
    {
        return FALSE;   //not outside...so don't detect anything
    }

    selection = ship->collMyBlob->blobShips;
    numShips = selection->numShips;

    i=0;

    returnval = FALSE;
    while (i < numShips)
    {
        enemy = selection->ShipPtr[i];

        if(!allianceIsShipAlly(enemy, ship->playerowner))
        {    //Enemy ship...later add check here for ALLIANCE
            vecSub(dist,enemy->posinfo.position,ship->posinfo.position);
            if(vecMagnitudeSquared(dist) < proximitysensorstatics->SearchRadiusSqr)
            {   //enemy is within detection radius
                proximityPlayerSeesShip(ship,enemy);
                returnval = TRUE;
            }
        }
        i++;
    }
    return returnval;
}

sdword proxGetBlipRadius(Ship *ship)
{
    return((sdword) ((ProximitySensorSpec *)ship->ShipSpecifics)->blipRadius);
}

/*-----------------------------------------------------------------------------
    Name        : ProximitySensorPingTimeout
    Description : Ping callback to evaluate if the ping has timed out.
    Inputs      : hellaPing - the ping we're evaluating
                  userID - cast to ship * for pointer to proximity sensor.
                  userData - ignore
                  bRemoveReferneces - if TRUE, remove references to pingDyingObject
    Outputs     :
    Return      : TRUE if the ping times out.
----------------------------------------------------------------------------*/
bool ProximitySensorPingTimeout(struct ping *hellaPing, udword userID, ubyte *userData, bool bRemoveReferences)
{
    ProximitySensorSpec *spec = (ProximitySensorSpec *)((Ship *)userID)->ShipSpecifics;

    if (bRemoveReferences)
    {
        if ((SpaceObj *)userID == pingDyingObject)
        {
            return(TRUE);
        }
        return(FALSE);
    }
    if(!(spec->sensorState == SENSOR_SENSED || spec->sensorState == SENSOR_SENSED2))
    {
        return(TRUE);
    }
    return(FALSE);
}

/*-----------------------------------------------------------------------------
    Name        : ProximitySensorHouseKeep
    Description : Housekeeping function for proximity sensor
    Inputs      :
    Outputs     :
    Return      :
----------------------------------------------------------------------------*/
void ProximitySensorHouseKeep(Ship *ship)
{
    ping *newPing;

    ProximitySensorStatics *proximitysensorstatics;
    ProximitySensorSpec *spec = (ProximitySensorSpec *)ship->ShipSpecifics;
    proximitysensorstatics = (ProximitySensorStatics *) ((ShipStaticInfo *)(ship->staticinfo))->custstatinfo;

    //HERE WE SHOULD PROBABLY EXTEND THE STUPID WINGS....!  IF INDEED THEY ARE TO BE EXTENDED


    //right now we are returning if this prox isn't the current players so we don't waste time in its loops
    //maybe later only return if it is an enemy current player because proximity sensor info would be allied shared?  Nahh

    //no longer can we make this optimization since it needs to be deterministic

    //if(ship->playerowner != universe.curPlayerPtr)
    //    return;                                         //if this prox sensor isn't the local users
                                                        // don't do anything
    //deal with the mesh animations cleverlike!
    if(ship->posinfo.isMoving & ISMOVING_MOVING)
    {
        //if prox is going above a reasonable speed..and not just being moved a bit!
        if(vecMagnitudeSquared(ship->posinfo.velocity) > 40000.0f)
        {
            if(ship->madSpecialStatus == MAD_STATUS_SPECIAL_OPEN)
            {
                //close the fins...if docking
                madLinkInCloseSpecialShip(ship);
            }
        }
        else
        {
            if(ship->madSpecialStatus != MAD_STATUS_SPECIAL_OPEN &&
               ship->madSpecialStatus != MAD_STATUS_SPECIAL_OPENING &&
               ship->madSpecialStatus != MAD_STATUS_SPECIAL_CLOSING)
            {
               madLinkInOpenSpecialShip(ship);
            }
        }
    }
    else
    {
        if(ship->madSpecialStatus != MAD_STATUS_SPECIAL_OPEN &&
           ship->madSpecialStatus != MAD_STATUS_SPECIAL_OPENING)
        {
           madLinkInOpenSpecialShip(ship);
        }
    }


    switch(spec->sensorState)
    {
    case SENSOR_BEGIN:  //Sensor has just been built
        spec->sensorState = SENSOR_SENSE;
        break;
    case SENSOR_SENSE:
        if((universe.univUpdateCounter & TW_ProximitySensorSearchRate) == TW_ProximitySensorSearchFrame)
        {   //its time to search for ships in vicinity
           if(DetectShips(ship))
           {    //enemy detected in vicinity....notifiy user..later through in # of Enemies?
                //CueUserAudio(ship);                     //turn on the audio        -- one time call
//                soundEvent(ship, Sensor);
                spec->sensorState = SENSOR_SENSED;
                spec->blipRadius = 0.0f;
                spec->TAGGED = FALSE;

                if (ship->playerowner->playerIndex == universe.curPlayerIndex)
                {
                    //only do this stuff for the local player...
                    //speechEventFleetSpec(ship, STAT_F_ProxSensor_Detection, 0, ship->playerowner->playerIndex);
                    if(battleCanChatterAtThisTime(BCE_STAT_F_ProxSensor_Detection, ship))
                    {
                        battleChatterFleetAttempt(SOUND_EVENT_DEFAULT, BCE_STAT_F_ProxSensor_Detection, SOUND_EVENT_DEFAULT, &ship->posinfo.position);
                    }
                    newPing = pingCreate(NULL, (SpaceObj *)ship, ProximitySensorPingTimeout, NULL, 0, (udword)ship);
                    newPing->c = proxBlipColor(ship);
                    newPing->size = proximitysensorstatics->SearchRadius;
                    newPing->minScreenSize = primScreenToGLScaleX(2);
                    newPing->pingDuration = 1.0f / proximitysensorstatics->SensorBlinkRate;
                    newPing->interPingPause = 0.25;
                    newPing->TOMask = PTOM_Proximity;
                }
            }
        }
        break;
    case SENSOR_SENSED:
        if(!spec->TAGGED)
        {   //We have been tagged..so don't annoy user anyfurther
            //CueUserAudio(ship);     //turn off user audio here:
            spec->sensorState = SENSOR_SENSED2;
        }
    case SENSOR_SENSED2:
        //need to determine whether to beep, or to blink here...
        //
        spec->blipRadius+= proximitysensorstatics->SensorBlinkRate;
        if(spec->blipRadius >= (real32) proximitysensorstatics->SensorCircleRadius)
            spec->blipRadius = 0.0f;

        if((universe.univUpdateCounter & TW_ProximitySensorSearchRate) == TW_ProximitySensorSearchFrame)
        {   //its time to search and see if Enemies STILL in vicinity...or if relief has come?
           if(!DetectShips(ship))
           {   //Enemies No longer being Detected
               //de TAG us and go to search mode....
                spec->sensorState = SENSOR_SENSE;
                //turn off sound here too ...
                spec->TAGGED = FALSE;
           }
        }
        break;
    default:
        break;
    }
}

CustShipHeader ProximitySensorHeader =
{
    ProximitySensor,
    sizeof(ProximitySensorSpec),
    ProximitySensorStaticInit,
    NULL,
    ProximitySensorInit,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ProximitySensorHouseKeep,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
};



