/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Types for physical metrics.
 * @remarks
 * Overly-specific physics types:
 * As a compromise, some physics types are specific, others are based on general types.
 * The problem with making all physics types be specific is that much more
 * type-casts would be required and computations would be harder-to-read.
 *//*
 * LEGAL:   COPYRIGHT (C) 2007 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#ifndef PHYSICS_TYPES_HH
#define PHYSICS_TYPES_HH 1

#include <ostream>
#include "math/module.hh"
#include "math/types_trig.hh"
#include "math/vertex.hh"
using namespace math;

namespace physics {

/// @class physics::Meter
/// @brief Scalar distance.  Vector distance is GeoVertex (rather than Meter3).
///
DECLARE_DISTINCT_TYPE_STRICT( Meter, fp, FP )

/// @class physics::Speed
/// @brief Speed in meters/second.  Speed measures magnitude, not direction, so it is a scalar.
///
DECLARE_DISTINCT_TYPE_STRICT( Speed, fp, FP )
const Speed SPEED_MINIMAL( 0.01f );  // margin

/// @class physics::Velocity
/// @brief Velocity measures magnitude and direction, so it is a vector.
/// velocity = distance/time
/// meter/second = Speed type = Velocity[Speed,Speed,Speed]
/// Defining Velocity as Vertex3<Speed> proved impractical (required too much casting).
///
typedef Vector3 Velocity;  // meter/second (Speed type)

/// @class physics::Mach
/// @brief Speed in Mach units.
///
DECLARE_DISTINCT_TYPE_STRICT( Mach, fp, FP )

/// @brief Rate of change in velocity.
/// acceleration = (distance/time) / time
/// Acceleration is represented as a Vector3 as distance (direction)
/// is the variable while the time units are constant (1).
///
typedef Vector3 Acceleration;

/// @class physics::Kilogram
/// @brief Measurement of mass/inertia.
/// Mass and weight are different (mass is inertia, weight is a force).
///
DECLARE_DISTINCT_TYPE_STRICT( Kilogram, fp, FP )

/// @class physics::Newton
/// @brief Measurement of force.
/// F = M * A (unit of force needed to accelerate a unit of mass).
/// N = kg * meter/t/t
///
typedef Vector3 Newton;                          // magnitude and direction
DECLARE_DISTINCT_TYPE_STRICT( Newton1, fp, FP )  // magnitude

/// @class physics::SpeedKPH
/// @brief Kilometers-per-hour
///
DECLARE_DISTINCT_TYPE_STRICT( SpeedKPH, fp, FP )
const SpeedKPH SPEED_KPH_MINIMAL( 0.01f );  // margin

/// @class physics::SpeedMPH
/// @brief Miles-per-hour (metric preferred).
///
DECLARE_DISTINCT_TYPE_STRICT( SpeedMPH, fp, FP )

////////////////////////////////////////////////////////////////////////////////
/// @brief 3D vector in METERS in physical space (Meter3).
///
struct GeoVertex
{
    typedef Meter value_type;  // OSG convention
    GeoVertex( void ) : x(0), y(0), z(0) { }
    GeoVertex( Meter X, Meter Y, Meter Z ) : x(X), y(Y), z(Z) { }
                 // Wrong: allows implicit TYPE-conversion without MATHEMATICAL-conversion.
                 //operator osg::Vec3d() const { return osg::Vec3d(x,y,z); }  // wrong: allows implicit TYPE-conversion
    osg::Vec3d   ToVec3d( void )   const { return osg::Vec3d( x.FP(), y.FP(), z.FP() ); }
    Vector3      ToVector3( void ) const { return    Vector3( x.FP(), y.FP(), z.FP() ); }
    Meter&       operator[]( uint i )       { ASSERT(i<3); return reinterpret_cast<      Meter*>(&x)[i]; }
    const Meter& operator[]( uint i ) const { ASSERT(i<3); return reinterpret_cast<const Meter*>(&x)[i]; }

    Meter x, y, z;
};

INLINE std::ostream& operator<<( std::ostream& strm, const GeoVertex& gv )
{
    strm << "GeoVertex: (" << gv.x.FP() << ','
                           << gv.y.FP() << ','
                           << gv.z.FP() << ')';// << std::endl;
    return strm;
}

//------------------------------------------------------------------------------
#if COMPILE_PALOMINO

////////////////////////////////////////////////////////////////////////////////
/// @brief Spherical coordinates ( latitude:Degree, longitude:Degree, altitude:Meter ).
/// This vertex is heterogeneous: lat,lon are in degrees but alt is in meters.
/// Latitude is in range {-90,..+90} degrees.
/// Longitude is in range {-180,..,+180} degrees.
///
struct SphereVertex
{
    SphereVertex( void ) : mLat(0), mLon(0), mAlt(0) { }
    SphereVertex( Degree lat, Degree lon, Meter alt ) : mLat(lat), mLon(lon), mAlt(alt) { }
                // Wrong: allows implicit TYPE-conversion without MATHEMATICAL-conversion.
                //operator osg::Vec3d() const { return osg::Vec3d( mLat, mLon, mAlt ); }  // omit single-precision
    osg::Vec3d  ToVec3d( void ) const { return osg::Vec3d( fp(mLat), fp(mLon), mAlt.FP() ); }
    Vector3     ToVector3( void ) const { return Vector3( fp(mLat), fp(mLon), mAlt.FP() ); }
    // operator[] impossible since element types differ.

    Degree mLat;
    Degree mLon;
    Meter  mAlt;
};

INLINE std::ostream& operator<<( std::ostream& strm, const SphereVertex& sv )
{
    strm << "SphereVertex: (" << sv.mLat << ','
                              << sv.mLon << ','
                              << sv.mAlt.FP() << ')';// << std::endl;
    return strm;
}

#endif // COMPILE_PALOMINO
//------------------------------------------------------------------------------

} // namespace physics

#endif // PHYSICS_TYPES_HH
