/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 * Copyright 2008-2010 Pelican Mapping
 * http://osgearth.org
 *
 * osgEarth is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
#ifndef OSGEARTH_UNITS_H
#define OSGEARTH_UNITS_H 1

#include <osgEarth/Common>

namespace osgEarth
{
    class Units
    {
    public:
        // linear
        static const Units MILLIMETERS;
        static const Units CENTIMETERS;
        static const Units FEET_INTERNATIONAL;
        static const Units FEET_US;
        static const Units KILOMETERS;
        static const Units METERS;
        static const Units MILES;
        static const Units NAUTICAL_MILES;

        // angular
        static const Units DEGREES;
        static const Units RADIANS;

        enum Type { TYPE_LINEAR, TYPE_ANGULAR };

    public:

        static bool convert( const Units& from, const Units& to, double input, double& output ) {
            if ( from._type == to._type )  {
                output = input * from._toBase / to._toBase;
                return true;
            }
            return false;
        }

        static bool canConvert( const Units& from, const Units& to ) {
            return from._type == to._type;
        }
        
        const std::string& getName() const { return _name; }

        const std::string& getAbbr() const { return _abbr; }

        bool operator == ( const Units& rhs ) const {
            return _type == rhs._type && _toBase == rhs._toBase; }
        
        bool operator != ( const Units& rhs ) const {
            return _type != rhs._type || _toBase != rhs._toBase; }

    public:

        // Make a new unit definition. if type == TYPE_LINEAR, toBase should convert to meters. If
        // type == TYPE_ANGULAR, toBase should convert to radians.
        Units( const std::string& name, const std::string& abbr, const Type& type, double toBase );

    private:
        std::string _name, _abbr;
        Type _type;
        double _toBase;
    };

#if 0
    class Units
    {
    public:
        static bool convert( const Units& from, const Units& to, double input, double& output ) {
            return from.convertValue( to, input, output ); }

        const std::string& getName() const { return _name; }
        const std::string& getAbbr() const { return _abbr; }

    public:
        virtual bool isLinear() const =0;
        virtual bool isAngular() const =0;
        virtual bool operator == ( const Units& rhs ) const =0;
        bool operator != ( const Units& rhs ) const { return !(operator==(rhs)); }

    protected:
        Units( const std::string& name, const std::string& abbr) 
            : _name(name), _abbr(abbr) { }

        virtual bool convertValue(const Units& to, double input, double& output) const =0;

        std::string _name, _abbr;
    };

    class OSGEARTH_EXPORT LinearUnits : public Units
    {
    public:
        static const LinearUnits MILLIMETERS;
        static const LinearUnits CENTIMETERS;
        static const LinearUnits FEET_INTERNATIONAL;
        static const LinearUnits FEET_US;
        static const LinearUnits KILOMETERS;
        static const LinearUnits METERS;
        static const LinearUnits MILES;
        static const LinearUnits NAUTICAL_MILES;

        bool convertValue( const Units& to, double input, double& output ) const {
            if ( to.isLinear() ) {
                output = input * _toMeters / static_cast<const LinearUnits&>(to)._toMeters;
                return true;
            }
            return false;
        }

        bool operator == ( const Units& rhs ) const {
            return rhs.isLinear() && _toMeters == static_cast<const LinearUnits&>(rhs)._toMeters; }

        bool isLinear() const { return true; }
        bool isAngular() const { return false; }

    public:
        LinearUnits( const std::string& name, const std::string& abbr, double toMeters );

    protected:
        double _toMeters;
    };

    class OSGEARTH_EXPORT AngularUnits : public Units
    {
    public:
        static const AngularUnits DEGREES;
        static const AngularUnits RADIANS;


        bool convertValue( const Units& to, double input, double& output ) const {
            if ( to.isAngular() ) {
                output = input * _toMeters / static_cast<const AngularUnits&>(to)._toMeters;
                return true;
            }
            return false;
        }

        bool operator == ( const Units& rhs ) const {
            return rhs.isAngular() && _toRadians == static_cast<const AngularUnits&>(rhs)._toRadians; }

        bool isLinear() const { return false; }
        bool isAngular() const { return true; }

    public:
        AngularUnits( const std::string& name, const std::string& abbr, double toMeters );

    protected:
        double _toRadians;
    };
#endif
}

#endif // OSGEARTH_UNITS_H
