/*
===========================================================================
Copyright (C) 2000-2009 Darklegion Development

This file is part of Tremulous.

Tremulous 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.

Tremulous 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 Tremulous; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/


#include "cg_local.h"

static entityPos_t entityPositions;

#define HUMAN_SCANNER_UPDATE_PERIOD 700

/*
=============
CG_UpdateEntityPositions

Update this client's perception of entity positions
=============
*/
void CG_UpdateEntityPositions( void )
{
  centity_t *cent = NULL;
  int       i;

  if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_HUMANS )
  {
    if( entityPositions.lastUpdateTime + HUMAN_SCANNER_UPDATE_PERIOD > cg.time )
      return;
  }

  VectorCopy( cg.refdef.vieworg, entityPositions.origin );
  VectorCopy( cg.refdefViewAngles, entityPositions.vangles );
  entityPositions.lastUpdateTime = cg.time;

  entityPositions.numAlienBuildables = 0;
  entityPositions.numHumanBuildables = 0;
  entityPositions.numAlienClients = 0;
  entityPositions.numHumanClients = 0;

  for( i = 0; i < cg.snap->numEntities; i++ )
  {
    cent = &cg_entities[ cg.snap->entities[ i ].number ];

    if( cent->currentState.eType == ET_BUILDABLE &&
     !( cent->currentState.eFlags & EF_DEAD ) )
    {
      if( BG_Buildable(cent->currentState.modelindex,NULL)->cuboid )
        if( !BG_CuboidAttributes(cent->currentState.modelindex)->detectable )
          continue; //forget about undetectable cuboids

      // add to list of item positions (for creep)
      if( cent->currentState.modelindex2 == TEAM_ALIENS )
      {
        VectorCopy( cent->lerpOrigin, entityPositions.alienBuildablePos[
            entityPositions.numAlienBuildables ] );
        entityPositions.alienBuildableTimes[
            entityPositions.numAlienBuildables ] = cent->miscTime;

        if( entityPositions.numAlienBuildables < MAX_GENTITIES )
          entityPositions.numAlienBuildables++;
      }
      else if( cent->currentState.modelindex2 == TEAM_HUMANS )
      {
        VectorCopy( cent->lerpOrigin, entityPositions.humanBuildablePos[
            entityPositions.numHumanBuildables ] );

        if( entityPositions.numHumanBuildables < MAX_GENTITIES )
          entityPositions.numHumanBuildables++;
      }
    }
    else if( cent->currentState.eType == ET_PLAYER )
    {
      int team = cent->currentState.misc & 0x00FF;

      if( team == TEAM_ALIENS )
      {
        VectorCopy( cent->lerpOrigin, entityPositions.alienClientPos[
            entityPositions.numAlienClients ] );

        if( entityPositions.numAlienClients < MAX_CLIENTS )
          entityPositions.numAlienClients++;
      }
      else if( team == TEAM_HUMANS )
      {
        VectorCopy( cent->lerpOrigin, entityPositions.humanClientPos[
            entityPositions.numHumanClients ] );

        if( entityPositions.numHumanClients < MAX_CLIENTS )
          entityPositions.numHumanClients++;
      }
    }
  }
}

#define STALKWIDTH  (2.0f * cgDC.aspectScale)
#define BLIPX       (16.0f * cgDC.aspectScale)
#define BLIPY       8.0f
#define FAR_ALPHA   0.8f
#define NEAR_ALPHA  1.2f

/*
=============
CG_DrawBlips

Draw blips and stalks for the human scanner
=============
*/
static void CG_DrawBlips( rectDef_t *rect, vec3_t origin, vec4_t colour )
{
  vec3_t  drawOrigin;
  vec3_t  up = { 0, 0, 1 };
  float   alphaMod = 1.0f;
  float   timeFractionSinceRefresh = 1.0f -
    ( (float)( cg.time - entityPositions.lastUpdateTime ) /
      (float)HUMAN_SCANNER_UPDATE_PERIOD );
  vec4_t  localColour;

  Vector4Copy( colour, localColour );

  RotatePointAroundVector( drawOrigin, up, origin, -entityPositions.vangles[ 1 ] - 90 );
  drawOrigin[ 0 ] /= ( 2 * HELMET_RANGE / rect->w );
  drawOrigin[ 1 ] /= ( 2 * HELMET_RANGE / rect->h );
  drawOrigin[ 2 ] /= ( 2 * HELMET_RANGE / rect->w );

  alphaMod = FAR_ALPHA +
    ( ( drawOrigin[ 1 ] + ( rect->h / 2.0f ) ) / rect->h ) * ( NEAR_ALPHA - FAR_ALPHA );

  localColour[ 3 ] *= alphaMod;
  localColour[ 3 ] *= ( 0.5f + ( timeFractionSinceRefresh * 0.5f ) );

  if( localColour[ 3 ] > 1.0f )
    localColour[ 3 ] = 1.0f;
  else if( localColour[ 3 ] < 0.0f )
    localColour[ 3 ] = 0.0f;

  trap_R_SetColor( localColour );

  if( drawOrigin[ 2 ] > 0 )
    CG_DrawPic( rect->x + ( rect->w / 2 ) - ( STALKWIDTH / 2 ) - drawOrigin[ 0 ],
                rect->y + ( rect->h / 2 ) + drawOrigin[ 1 ] - drawOrigin[ 2 ],
                STALKWIDTH, drawOrigin[ 2 ], cgs.media.scannerLineShader );
  else
    CG_DrawPic( rect->x + ( rect->w / 2 ) - ( STALKWIDTH / 2 ) - drawOrigin[ 0 ],
                rect->y + ( rect->h / 2 ) + drawOrigin[ 1 ],
                STALKWIDTH, -drawOrigin[ 2 ], cgs.media.scannerLineShader );

  CG_DrawPic( rect->x + ( rect->w / 2 ) - ( BLIPX / 2 ) - drawOrigin[ 0 ],
              rect->y + ( rect->h / 2 ) - ( BLIPY / 2 ) + drawOrigin[ 1 ] - drawOrigin[ 2 ],
              BLIPX, BLIPY, cgs.media.scannerBlipShader );
  trap_R_SetColor( NULL );
}

#define BLIPX2  (24.0f * cgDC.aspectScale)
#define BLIPY2  24.0f

/*
=============
CG_DrawDir

Draw dot marking the direction to an enemy
=============
*/
static void CG_DrawDir( rectDef_t *rect, vec3_t origin, vec4_t colour )
{
  vec3_t  drawOrigin;
  vec3_t  noZOrigin;
  vec3_t  normal, antinormal, normalDiff;
  vec3_t  view, noZview;
  vec3_t  up  = { 0.0f, 0.0f,   1.0f };
  vec3_t  top = { 0.0f, -1.0f,  0.0f };
  float   angle;
  playerState_t *ps = &cg.snap->ps;

  BG_GetClientNormal( ps, normal );

  AngleVectors( entityPositions.vangles, view, NULL, NULL );

  ProjectPointOnPlane( noZOrigin, origin, normal );
  ProjectPointOnPlane( noZview, view, normal );
  VectorNormalize( noZOrigin );
  VectorNormalize( noZview );

  //calculate the angle between the images of the blip and the view
  angle = RAD2DEG( acos( DotProduct( noZOrigin, noZview ) ) );
  CrossProduct( noZOrigin, noZview, antinormal );
  VectorNormalize( antinormal );

  //decide which way to rotate
  VectorSubtract( normal, antinormal, normalDiff );
  if( VectorLength( normalDiff ) < 1.0f )
    angle = 360.0f - angle;

  RotatePointAroundVector( drawOrigin, up, top, angle );

  trap_R_SetColor( colour );
  CG_DrawPic( rect->x + ( rect->w / 2 ) - ( BLIPX2 / 2 ) - drawOrigin[ 0 ] * ( rect->w / 2 ),
              rect->y + ( rect->h / 2 ) - ( BLIPY2 / 2 ) + drawOrigin[ 1 ] * ( rect->h / 2 ),
              BLIPX2, BLIPY2, cgs.media.scannerBlipShader );
  trap_R_SetColor( NULL );
}

/*
=============
CG_BasivisionBlip
=============
*/
void CG_BasivisionBlip( vec3_t origin, float size, qhandle_t shader, float flareSize, qhandle_t flare )
{
  float x, y;

  if( !CG_WorldToScreen( origin, &x, &y ) )
    return;

  size = size * 1000 / Distance( origin, entityPositions.origin );

  if( shader )
    CG_DrawPic( x - ( size * cgDC.aspectScale ) / 2, y - size / 2, size * cgDC.aspectScale, size,
               cgs.media.basivisionBlipShader );

  if( flare )
    CG_DrawPic( x - ( flareSize * cgDC.aspectScale ) / 2, y - flareSize / 2, flareSize * cgDC.aspectScale, flareSize,
               cgs.media.basivisionFlareShader );
}

/*
=============
CG_AlienSense
=============
*/
void CG_AlienSense( rectDef_t *rect )
{
  int     i;
  vec3_t  origin;
  vec3_t  relOrigin;
  vec4_t  buildable = { 1.0f, 0.0f, 0.0f, 0.7f };
  vec4_t  client    = { 0.0f, 0.0f, 1.0f, 0.7f };

  VectorCopy( entityPositions.origin, origin );

  if( BG_UpgradeIsActive( UP_NIGHTVISION, cg.predictedPlayerState.stats ) )
  {
    for( i = 0; i < entityPositions.numHumanBuildables; i++ )
      CG_BasivisionBlip( entityPositions.humanBuildablePos[ i ],
                         0, 0,
                         150, cgs.media.basivisionBlipShader );

    for( i = 0; i < entityPositions.numHumanClients; i++ )
      CG_BasivisionBlip( entityPositions.humanClientPos[ i ],
                         60, cgs.media.basivisionBlipShader,
                         80, cgs.media.basivisionFlareShader );
  }
  else
  {
    //draw human buildables
    for( i = 0; i < entityPositions.numHumanBuildables; i++ )
    {
      VectorClear( relOrigin );
      VectorSubtract( entityPositions.humanBuildablePos[ i ], origin, relOrigin );

      if( VectorLength( relOrigin ) < ALIENSENSE_RANGE )
        CG_DrawDir( rect, relOrigin, buildable );
    }

    //draw human clients
    for( i = 0; i < entityPositions.numHumanClients; i++ )
    {
      VectorClear( relOrigin );
      VectorSubtract( entityPositions.humanClientPos[ i ], origin, relOrigin );

      if( VectorLength( relOrigin ) < ALIENSENSE_RANGE )
        CG_DrawDir( rect, relOrigin, client );
    }
  }
}

/*
=============
CG_Scanner
=============
*/
void CG_Scanner( rectDef_t *rect, qhandle_t shader, vec4_t color )
{
  int     i;
  vec3_t  origin;
  vec3_t  relOrigin;
  vec4_t  hIabove;
  vec4_t  hIbelow;
  vec4_t  aIabove = { 1.0f, 0.0f, 0.0f, 0.75f };
  vec4_t  aIbelow = { 1.0f, 0.0f, 0.0f, 0.5f };

  Vector4Copy( color, hIabove );
  hIabove[ 3 ] *= 1.5f;
  Vector4Copy( color, hIbelow );

  VectorCopy( entityPositions.origin, origin );

  //draw human buildables below scanner plane
  for( i = 0; i < entityPositions.numHumanBuildables; i++ )
  {
    VectorClear( relOrigin );
    VectorSubtract( entityPositions.humanBuildablePos[ i ], origin, relOrigin );

    if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] < 0 ) )
      CG_DrawBlips( rect, relOrigin, hIbelow );
  }

  //draw alien buildables below scanner plane
  for( i = 0; i < entityPositions.numAlienBuildables; i++ )
  {
    VectorClear( relOrigin );
    VectorSubtract( entityPositions.alienBuildablePos[ i ], origin, relOrigin );

    if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] < 0 ) )
      CG_DrawBlips( rect, relOrigin, aIbelow );
  }

  //draw human clients below scanner plane
  for( i = 0; i < entityPositions.numHumanClients; i++ )
  {
    VectorClear( relOrigin );
    VectorSubtract( entityPositions.humanClientPos[ i ], origin, relOrigin );

    if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] < 0 ) )
      CG_DrawBlips( rect, relOrigin, hIbelow );
  }

  //draw alien buildables below scanner plane
  for( i = 0; i < entityPositions.numAlienClients; i++ )
  {
    VectorClear( relOrigin );
    VectorSubtract( entityPositions.alienClientPos[ i ], origin, relOrigin );

    if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] < 0 ) )
      CG_DrawBlips( rect, relOrigin, aIbelow );
  }

  if( !cg_disableScannerPlane.integer )
  {
    trap_R_SetColor( color );
    CG_DrawPic( rect->x, rect->y, rect->w, rect->h, shader );
    trap_R_SetColor( NULL );
  }

  //draw human buildables above scanner plane
  for( i = 0; i < entityPositions.numHumanBuildables; i++ )
  {
    VectorClear( relOrigin );
    VectorSubtract( entityPositions.humanBuildablePos[ i ], origin, relOrigin );

    if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] > 0 ) )
      CG_DrawBlips( rect, relOrigin, hIabove );
  }

  //draw alien buildables above scanner plane
  for( i = 0; i < entityPositions.numAlienBuildables; i++ )
  {
    VectorClear( relOrigin );
    VectorSubtract( entityPositions.alienBuildablePos[ i ], origin, relOrigin );

    if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] > 0 ) )
      CG_DrawBlips( rect, relOrigin, aIabove );
  }

  //draw human clients above scanner plane
  for( i = 0; i < entityPositions.numHumanClients; i++ )
  {
    VectorClear( relOrigin );
    VectorSubtract( entityPositions.humanClientPos[ i ], origin, relOrigin );

    if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] > 0 ) )
      CG_DrawBlips( rect, relOrigin, hIabove );
  }

  //draw alien clients above scanner plane
  for( i = 0; i < entityPositions.numAlienClients; i++ )
  {
    VectorClear( relOrigin );
    VectorSubtract( entityPositions.alienClientPos[ i ], origin, relOrigin );

    if( VectorLength( relOrigin ) < HELMET_RANGE && ( relOrigin[ 2 ] > 0 ) )
      CG_DrawBlips( rect, relOrigin, aIabove );
  }
}
