// Copyright (C) 1999-2000 Id Software, Inc.
//

/*
 *  Portions Copyright (C) 2000-2001 Tim Angus
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under the terms of the OSML - Open Source Modification License v1.0 as
 *  described in the file COPYING which is distributed with this source
 *  code.
 *
 *  This program 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.
 */

#include "g_local.h"

/*
================
G_Bounce

================
*/
static void G_Bounce( gentity_t *ent, trace_t *trace )
{
  vec3_t    velocity;
  float     dot;
  int       hitTime;
  float     minNormal;
  qboolean  invert;

  // reflect the velocity on the trace plane
  hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
  BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
  dot = DotProduct( velocity, trace->plane.normal );
  VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );

  if( ent->s.eType == ET_BUILDABLE )
  {
    minNormal = BG_FindMinNormalForBuildable( ent->s.modelindex );
    invert = BG_FindInvertNormalForBuildable( ent->s.modelindex );
  }
  else
    minNormal = 0.707f;

  // cut the velocity to keep from bouncing forever
  if( ( trace->plane.normal[ 2 ] >= minNormal ||
      ( invert && trace->plane.normal[ 2 ] <= -minNormal ) ) &&
      trace->entityNum == ENTITYNUM_WORLD )
    VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta );
  else
    VectorScale( ent->s.pos.trDelta, 0.3f, ent->s.pos.trDelta );

  if( VectorLength( ent->s.pos.trDelta ) < 10 )
  {
    VectorMA( trace->endpos, 0.5, trace->plane.normal, trace->endpos ); // make sure it is off ground
    SnapVector( trace->endpos );
    G_SetOrigin( ent, trace->endpos );
    ent->s.groundEntityNum = trace->entityNum;
    VectorCopy( trace->plane.normal, ent->s.origin2 );
    VectorSet( ent->s.pos.trDelta, 0.0f, 0.0f, 0.0f );
    return;
  }

  VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
  VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
  ent->s.pos.trTime = level.time;
}


/*
================
G_Physics

================
*/
void G_Physics( gentity_t *ent, int msec )
{
  vec3_t    origin;
  trace_t   tr;
  int     contents;
  int     mask;
  int     bHealth = BG_FindHealthForBuildable( ent->s.modelindex );
  int     bRegen = BG_FindRegenRateForBuildable( ent->s.modelindex );

  //pack health, power and dcc
  if( ent->s.eType == ET_BUILDABLE )
  {
    //toggle spawned flag for buildables
    if( !ent->spawned )
    {
      if( ent->buildTime + BG_FindBuildTimeForBuildable( ent->s.modelindex ) < level.time )
      {
        ent->takedamage = qtrue;
        ent->spawned = qtrue;
      }
    }
    
    ent->s.generic1 = (int)( ( (float)ent->health / (float)bHealth ) * B_HEALTH_SCALE );

    if( ent->s.generic1 < 0 )
      ent->s.generic1 = 0;
    
    if( ent->powered )
      ent->s.generic1 |= B_POWERED_TOGGLEBIT;
    
    if( ent->dcced )
      ent->s.generic1 |= B_DCCED_TOGGLEBIT;

    if( ent->spawned )
      ent->s.generic1 |= B_SPAWNED_TOGGLEBIT;

    ent->time1000 += msec;

    if( ent->time1000 >= 1000 )
    {
      ent->time1000 -= 1000;
      
      //regenerate health
      if( ent->health < bHealth && bRegen )
      {
        ent->health += bRegen;

        if( ent->health > bHealth )
          ent->health = bHealth;
      }
    }
  }
  
  // if groundentity has been set to -1, it may have been pushed off an edge
  if( ent->s.groundEntityNum == -1 )
  {
    if( ent->s.eType == ET_BUILDABLE )
    {
      if( ent->s.pos.trType != BG_FindTrajectoryForBuildable( ent->s.modelindex ) )
      {
        ent->s.pos.trType = BG_FindTrajectoryForBuildable( ent->s.modelindex );
        ent->s.pos.trTime = level.time;
      }
    }
    else if( ent->s.pos.trType != TR_GRAVITY )
    {
      ent->s.pos.trType = TR_GRAVITY;
      ent->s.pos.trTime = level.time;
    }
  }
  
  // trace a line from the previous position to the current position
  if( ent->clipmask )
    mask = ent->clipmask;
  else
    mask = MASK_PLAYERSOLID & ~CONTENTS_BODY;//MASK_SOLID;

  if( ent->s.pos.trType == TR_STATIONARY )
  {
    // check think function
    G_RunThink( ent );

    VectorCopy( ent->r.currentOrigin, origin );

    VectorMA( origin, -2.0f, ent->s.origin2, origin );

    trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->s.number, mask );
    
    if( tr.fraction == 1.0 )
      ent->s.groundEntityNum = -1;

    return;
  }

  // get current position
  BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );

  trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->s.number, mask );

  VectorCopy( tr.endpos, ent->r.currentOrigin );

  if( tr.startsolid )
    tr.fraction = 0;

  trap_LinkEntity( ent ); // FIXME: avoid this for stationary?

  // check think function
  G_RunThink( ent );

  if( tr.fraction == 1 ) 
    return;

  // if it is in a nodrop volume, remove it
  contents = trap_PointContents( ent->r.currentOrigin, -1 );
  if( contents & CONTENTS_NODROP )
  {
    G_FreeEntity( ent );
    return;
  }

  G_Bounce( ent, &tr );
}