/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Aircraft carrier.
 *//*
 * LEGAL:   COPYRIGHT (C) 2008 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#define PROGRAM_CARRIER_CC 1
#include <vector>
#include "base/module.hh"
using namespace base;
#include "glue/module.hh"
#include "glue/conf.hh"
using namespace glue;
#include "gfx/module.hh"
using namespace gfx;
#include "graph/module.hh"
#include "graph/model_cache.hh"
#include "graph/scene_graph.hh"
using namespace graph;
#include "object/module.hh"
#include "object/aircraft.hh"
using namespace object;
#include "world/module.hh"
using namespace world;
#include "program/module.hh"
#include "program/carrier.hh"

#include <osgUtil/Optimizer>

namespace program {

INTERN const Meter CARRIER_RUNWAY_LENGTH( 220.0f );  // slightly more for collision-detection
INTERN const Meter CARRIER_RUNWAY_ALT( 23.43f );
INTERN const Meter CARRIER_RUNWAY_ALT_THRESHOLD( 2.5f );

////////////////////////////////////////////////////////////////////////////////
////////////////////////////////  Carrier  /////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

std::vector<shptr<Carrier> > Carrier::msCarriers;

/*****************************************************************************
 * ctor/dtor.
 *****************************************************************************/
Carrier::Carrier( shptr<Graph> graph, const WorldVertex& pos, const uint carrierNum )
:   Parent(graph,pos)
{
    SET_TYPESIG(this,TYPESIG_CARRIER);

    // Orient the aircraft carrier.
    PROGRAM_CONF.mCarrierRotationList.Apply( *this );

    msCarriers.push_back( this );
}

Carrier::~Carrier()
{
    INVALIDATE_TYPESIG(this,TYPESIG_CARRIER);
}

/*****************************************************************************
 * For state-sorting.
 *****************************************************************************/
NodeSort
Carrier::GetNodeSort( void )
{
    // Fixes missing texture on tower (that has the carrier number).
    NodeSort::Optimizations optimizations;
    optimizations.mVal &= ~osgUtil::Optimizer::SHARE_DUPLICATE_STATE;
    NodeSort::Attribs::Int attribBits = NodeSort::Attribs::SHADOW_RECEIVER;
    return NodeSort( optimizations, NodeSort::Attribs(attribBits) );
}

/*****************************************************************************
 * Place aircraft on carrier.
 *****************************************************************************/
void
Carrier::PlaceAircraft( shptr<Aircraft> aircraft, const GeoVertex& relativeGeoPosition )
{
CHECK_TYPESIG(this,TYPESIG_CARRIER);

    // Compute position of aircraft.
    WorldVertex position = GetPosition();  // start at carrier's position
    position += world::conv::GeoVertex2WorldVertex( relativeGeoPosition );  // add relative position
    aircraft->SetPosition( position );

    // Adjust altitude of aircraft so that wheels sit on runway.
    // mModelOffsetGear could be zero.
    const fp wheelOffset = world::conv::Meters2Sim( aircraft->GetSpecs().mModelOffsetGear );
    aircraft->Translate( YY, wheelOffset );

    // Extend landing gear.
    aircraft->SetLandingGear( true );
}

/*****************************************************************************
 * Higher-level method to make an aircraft carrier.
 *****************************************************************************/
shptr<Carrier>  // CLASS_METHOD
Carrier::MakeCarrier( const WorldVertex& carrierPosition, const uint carrierNum )
{
    shptr<Graph> graph = ModelCache::GetInstance().LoadModel( PROGRAM_CONF.mCarrierModelName );
    return new Carrier( graph, carrierPosition, carrierNum );
}

/*****************************************************************************
 * @return True if a position is approximately on the carrier's flight deck (runway).
 * This is meant for collision-detection.
 *****************************************************************************/
bool
Carrier::IfRunwayAltitude( const WorldVertex& position )
{
CHECK_TYPESIG(this,TYPESIG_CARRIER);

    const SphereVertex posSph = world::conv::WorldVertex2SphereVertex( position );
    const Meter altDelta = CARRIER_RUNWAY_ALT - posSph.mAlt;
    return ABS(altDelta.FP()) < CARRIER_RUNWAY_ALT_THRESHOLD.FP();  // within +-N meters
}

} // namespace program
