/*
===========================================================================
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
===========================================================================
*/

// cg_attachment.c -- an abstract attachment system

#include "cg_local.h"

/*
===============
CG_AttachmentPoint

Return the attachment point
===============
*/
qboolean CG_AttachmentPoint( attachment_t *a, vec3_t v )
{
  centity_t   *cent;

  if( !a )
    return qfalse;

  // if it all breaks, then use the last point we know was correct
  VectorCopy( a->lastValidAttachmentPoint, v );

  switch( a->type )
  {
    case AT_STATIC:
      if( !a->staticValid )
        return qfalse;

      VectorCopy( a->origin, v );
      break;

    case AT_TAG:
      if( !a->tagValid )
        return qfalse;

      AxisCopy( axisDefault, a->re.axis );
      CG_PositionRotatedEntityOnTag( &a->re, &a->parent,
                                     a->model, a->tagName );
      VectorCopy( a->re.origin, v );
      break;

    case AT_CENT:
      if( !a->centValid )
        return qfalse;

      if( a->centNum == cg.predictedPlayerState.clientNum )
      {
        // this is smoother if it's the local client
        VectorCopy( cg.predictedPlayerState.origin, v );
      }
      else
      {
        cent = &cg_entities[ a->centNum ];
        VectorCopy( cent->lerpOrigin, v );
      }
      break;

    case AT_PARTICLE:
      if( !a->particleValid )
        return qfalse;

      if( !a->particle->valid )
      {
        a->particleValid = qfalse;
        return qfalse;
      }
      else
        VectorCopy( a->particle->origin, v );
      break;

    default:
      CG_Printf( S_COLOR_RED "ERROR: Invalid attachmentType_t in attachment\n" );
      break;
  }

  if( a->hasOffset )
    VectorAdd( v, a->offset, v );

  VectorCopy( v, a->lastValidAttachmentPoint );

  return qtrue;
}

/*
===============
CG_AttachmentDir

Return the attachment direction
===============
*/
qboolean CG_AttachmentDir( attachment_t *a, vec3_t v )
{
  vec3_t      forward;
  centity_t   *cent;

  if( !a )
    return qfalse;

  switch( a->type )
  {
    case AT_STATIC:
      return qfalse;
      break;

    case AT_TAG:
      if( !a->tagValid )
        return qfalse;

      VectorCopy( a->re.axis[ 0 ], v );
      break;

    case AT_CENT:
      if( !a->centValid )
        return qfalse;

      cent = &cg_entities[ a->centNum ];
      AngleVectors( cent->lerpAngles, forward, NULL, NULL );
      VectorCopy( forward, v );
      break;

    case AT_PARTICLE:
      if( !a->particleValid )
        return qfalse;

      if( !a->particle->valid )
      {
        a->particleValid = qfalse;
        return qfalse;
      }
      else
        VectorCopy( a->particle->velocity, v );
      break;

    default:
      CG_Printf( S_COLOR_RED "ERROR: Invalid attachmentType_t in attachment\n" );
      break;
  }

  VectorNormalize( v );
  return qtrue;
}

/*
===============
CG_AttachmentAxis

Return the attachment axis
===============
*/
qboolean CG_AttachmentAxis( attachment_t *a, vec3_t axis[ 3 ] )
{
  centity_t   *cent;

  if( !a )
    return qfalse;

  switch( a->type )
  {
    case AT_STATIC:
      return qfalse;
      break;

    case AT_TAG:
      if( !a->tagValid )
        return qfalse;

      AxisCopy( a->re.axis, axis );
      break;

    case AT_CENT:
      if( !a->centValid )
        return qfalse;

      cent = &cg_entities[ a->centNum ];
      AnglesToAxis( cent->lerpAngles, axis );
      break;

    case AT_PARTICLE:
      return qfalse;
      break;

    default:
      CG_Printf( S_COLOR_RED "ERROR: Invalid attachmentType_t in attachment\n" );
      break;
  }

  return qtrue;
}

/*
===============
CG_AttachmentVelocity

If the attachment can have velocity, return it
===============
*/
qboolean CG_AttachmentVelocity( attachment_t *a, vec3_t v )
{
  if( !a )
    return qfalse;

  if( a->particleValid && a->particle->valid )
  {
    VectorCopy( a->particle->velocity, v );
    return qtrue;
  }
  else if( a->centValid )
  {
    centity_t *cent = &cg_entities[ a->centNum ];

    VectorCopy( cent->currentState.pos.trDelta, v );
    return qtrue;
  }

  return qfalse;
}

/*
===============
CG_AttachmentCentNum

If the attachment has a centNum, return it
===============
*/
int CG_AttachmentCentNum( attachment_t *a )
{
  if( !a || !a->centValid )
    return -1;

  return a->centNum;
}

/*
===============
CG_Attached

If the attachment is valid, return qtrue
===============
*/
qboolean CG_Attached( attachment_t *a )
{
  if( !a )
    return qfalse;

  return a->attached;
}

/*
===============
CG_AttachToPoint

Attach to a point in space
===============
*/
void CG_AttachToPoint( attachment_t *a )
{
  if( !a || !a->staticValid )
    return;

  a->type = AT_STATIC;
  a->attached = qtrue;
}

/*
===============
CG_AttachToCent

Attach to a centity_t
===============
*/
void CG_AttachToCent( attachment_t *a )
{
  if( !a || !a->centValid )
    return;

  a->type = AT_CENT;
  a->attached = qtrue;
}

/*
===============
CG_AttachToTag

Attach to a model tag
===============
*/
void CG_AttachToTag( attachment_t *a )
{
  if( !a || !a->tagValid )
    return;

  a->type = AT_TAG;
  a->attached = qtrue;
}

/*
===============
CG_AttachToParticle

Attach to a particle
===============
*/
void CG_AttachToParticle( attachment_t *a )
{
  if( !a || !a->particleValid )
    return;

  a->type = AT_PARTICLE;
  a->attached = qtrue;
}

/*
===============
CG_SetAttachmentPoint
===============
*/
void CG_SetAttachmentPoint( attachment_t *a, vec3_t v )
{
  if( !a )
    return;

  VectorCopy( v, a->origin );
  a->staticValid = qtrue;
}

/*
===============
CG_SetAttachmentCent
===============
*/
void CG_SetAttachmentCent( attachment_t *a, centity_t *cent )
{
  if( !a || !cent )
    return;

  a->centNum = cent->currentState.number;
  a->centValid = qtrue;
}

/*
===============
CG_SetAttachmentTag
===============
*/
void CG_SetAttachmentTag( attachment_t *a, refEntity_t parent,
    qhandle_t model, char *tagName )
{
  if( !a )
    return;

  a->parent = parent;
  a->model = model;
  strncpy( a->tagName, tagName, MAX_STRING_CHARS );
  a->tagValid = qtrue;
}

/*
===============
CG_SetAttachmentParticle
===============
*/
void CG_SetAttachmentParticle( attachment_t *a, particle_t *p )
{
  if( !a )
    return;

  a->particle = p;
  a->particleValid = qtrue;
}

/*
===============
CG_SetAttachmentOffset
===============
*/
void CG_SetAttachmentOffset( attachment_t *a, vec3_t v )
{
  if( !a )
    return;

  VectorCopy( v, a->offset );
  a->hasOffset = qtrue;
}