/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield 
 *
 * This library is open source and may be redistributed and/or modified under  
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or 
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 * 
 * This library 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 
 * OpenSceneGraph Public License for more details.
*/

#ifndef OSGTERRAIN_LOCATOR
#define OSGTERRAIN_LOCATOR 1

#include <osg/Object>
#include <osg/Vec3d>
#include <osg/CoordinateSystemNode>

#include <osgTerrain/Export>

namespace osgTerrain {

class OSGTERRAIN_EXPORT Locator : public osg::Object
{
    public:

        Locator();

        /** Copy constructor using CopyOp to manage deep vs shallow copy.*/
        Locator(const Locator&,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
        
        META_Object(osgTerrain, Locator);

        /** CoordinateSystemType provides the classification of the type coordinate system represented.*/
        enum CoordinateSystemType
        {
            /** GEOCENTRIC coordinate systems are ones mapped to the around the ellipsoid, i.e. whole earth.*/
            GEOCENTRIC,

            /** GEOGRAPHIC coordinate systems are ones mapped to latitude and longitude.*/
            GEOGRAPHIC,

            /** PROJECTED coordinate systems are ones projected to a local projected coordinate system i.e. UTMs.*/
            PROJECTED
        };

        /** Set the CoordinatesSyetemType.
          * Note, the user must keep the CoordinateSystemString consistent with the type of the CoordinateSystem.*/
        void setCoordinateSystemType(CoordinateSystemType type) { _coordinateSystemType = type; }

        /** Get the CoordinatesSyetemType.*/
        CoordinateSystemType getCoordinateSystemType() const { return _coordinateSystemType; }

        /** Set the coordinate system format string. Typical values would be WKT, PROJ4, USGS etc.*/
        void setFormat(const std::string& format) { _format = format; }
        
        /** Get the coordinate system format string.*/
        const std::string& getFormat() const { return _format; }

        /** Set the CoordinateSystem reference string, should be stored in a form consistent with the Format.*/
        void setCoordinateSystem(const std::string& cs) { _cs = cs; }
        
        /** Get the CoordinateSystem reference string.*/
        const std::string& getCoordinateSystem() const { return _cs; }


        /** Set EllipsoidModel to describe the model used to map lat, long and height into geocentric XYZ and back. */
        void setEllipsoidModel(osg::EllipsoidModel* ellipsode) { _ellipsoidModel = ellipsode; }
        
        /** Get the EllipsoidModel.*/
        osg::EllipsoidModel* getEllipsoidModel() { return _ellipsoidModel.get(); }
        
        /** Get the const EllipsoidModel.*/
        const osg::EllipsoidModel* getEllipsoidModel() const { return _ellipsoidModel.get(); }


        /** Set the transformation from local coordinates to model coordinates.*/
        void setTransform(const osg::Matrixd& transform) { _transform = transform; _inverse.invert(_transform); }

        /** Set the transformation from local coordinates to model coordinates.*/
        const osg::Matrixd& getTransform() const { return _transform; }

        /** Set the extents of the local coords.*/
        void setTransformAsExtents(double minX, double minY, double maxX, double maxY);

        
        virtual bool orientationOpenGL() const;

        virtual bool convertLocalToModel(const osg::Vec3d& local, osg::Vec3d& world) const;

        virtual bool convertModelToLocal(const osg::Vec3d& world, osg::Vec3d& local) const;

        static bool convertLocalCoordBetween(const Locator& source, const osg::Vec3d& sourceNDC, 
                                             const Locator& destination, osg::Vec3d& destinationNDC)
        {
            osg::Vec3d model;
            if (!source.convertLocalToModel(sourceNDC, model)) return false;
            if (!destination.convertModelToLocal(model, destinationNDC)) return false;
            return true;
        }
        
        bool computeLocalBounds(Locator& source, osg::Vec3d& bottomLeft, osg::Vec3d& topRight) const;
        
        void setDefinedInFile(bool flag) { _definedInFile = flag; }
        bool getDefinedInFile() const { return _definedInFile; }
        
        void setTransformScaledByResolution(bool scaledByResolution) { _transformScaledByResolution = scaledByResolution; }
        bool getTransformScaledByResolution() const { return _transformScaledByResolution; }

    protected:

        virtual ~Locator();
        
        CoordinateSystemType                _coordinateSystemType;

        std::string                         _format;
        std::string                         _cs;
        osg::ref_ptr<osg::EllipsoidModel>   _ellipsoidModel;

        osg::Matrixd                        _transform;
        osg::Matrixd                        _inverse;
        
        bool                                _definedInFile;
        bool                                _transformScaledByResolution;

};

}

#endif
