/* -*-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 OSGEARTHUTIL_OBJECT_PLACER
#define OSGEARTHUTIL_OBJECT_PLACER

#include <osgEarthUtil/Common>
#include <osgEarth/MapNode>
#include <osg/Node>
#include <osg/Matrix>
#include <osgUtil/IntersectionVisitor>

namespace osgEarth { namespace Util
{
    /**
     * Convenience utilities for placing an object on an osgEarth terrain map
     * using latitude/longitude coordinates.
     */
    class OSGEARTHUTIL_EXPORT ObjectPlacer
    {
    public:
        /**
         * Constructs a new placer.
         *
         * @param terrain
         *      The scene graph containing the osgEarth::Map node.
         * @param traversalMask
         *      Mask to use when intersecting the terrain.
         * @param clamp
         *      Whether the class should attempt to calculate the placement 
         *      position so that it sits exactly on the terrain skin.
         *      Warning: this does not yet work properly for maps that don't
         *      report a maximum resolution (like most of the commercial providers).
         * @param maxLevel
         *      Maximum level of detail to which to search for high resolution terrain.
         */
        ObjectPlacer(
            osg::Node* terrain,
            int  traversalMask =~0,
            bool clamp        =false,
            int  maxLevel     =20);

        /**
         * Creates a double-precision matrix that will transform geometry to the
         * specified map location. In a geocentric map, the matrix will also rotate
         * into the tangent plane.
         *
         * @param lat_degrees, lon_degrees
         *      Location on the map for which to generate a matrix
         * @param height
         *      Height above the terrain (in local units)
         * @param out_result
         *      Receives the resulting matrix; only valid if the method returns TRUE.
         * @return
         *      True if the method succesfully created the output matrix; false if not
         *      (e.g., the input location was outside the extents of the terrain map).
         */
        bool createPlacerMatrix(
            double lat_degrees,
            double lon_degrees,
            double height,
            osg::Matrixd& out_result ) const;

        /**
         * Creates a new node graph that positions the input node at a specified map
         * location. The resulting node will contain the input node as a child.
         *
         * @param node
         *      Node to position at the specified location
         * @param lat_degrees, lon_degrees
         *      Position on the map at which to place the node
         * @param height
         *      Height above the terrain (in local units)
         * @return
         *      A node graph representing the newly placed node. The input node will be
         *      a child in the new graph. NULL if the placement failed for some reason.
         */
        osg::Node* placeNode(
            osg::Node* node,
            double lat_degrees,
            double lon_degrees,
            double height ) const;

    private:
        osg::ref_ptr<osgEarth::MapNode> _mapNode;
        osg::ref_ptr<osg::CoordinateSystemNode> _csn;
        osg::ref_ptr<osgUtil::IntersectionVisitor::ReadCallback> _readCallback;
        int _traversalMask;
        bool _clamp;

        bool clampGeocentric(osg::CoordinateSystemNode* csn, double lat_rad, double lon_rad, osg::Vec3d& out) const;
        bool clampProjected(osg::CoordinateSystemNode* csn, double x, double y, osg::Vec3d& out) const;
    };

} } // namespace osgEarth::Util

#endif // OSGEARTHUTIL_OBJECT_PLACER
