/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 * Copyright 2008-2013 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_TERRAIN_ENGINE_NODE_H
#define OSGEARTH_TERRAIN_ENGINE_NODE_H 1

#include <osgEarth/Map>
#include <osgEarth/MapFrame>
#include <osgEarth/Terrain>
#include <osgEarth/TerrainEffect>
#include <osgEarth/TextureCompositor>
#include <osgEarth/ShaderUtils>
#include <osg/CoordinateSystemNode>
#include <osg/Geode>
#include <osg/NodeCallback>

namespace osgEarth
{
    class TerrainEffect;

    /**
     * TerrainEngineNode is the base class and interface for map engine implementations.
     *
     * A map engine lives under a MapNode and is responsible for generating the
     * actual geometry representing the Earth.
     */
    class OSGEARTH_EXPORT TerrainEngineNode : public osg::CoordinateSystemNode
    {
    public:
        /** Gets the map that this engine is rendering. */
        const Map* getMap() const { return _map.get(); }

        /** Gets the Terrain interface for interacting with the scene graph */
        Terrain* getTerrain() { return _terrainInterface.get(); }
        const Terrain* getTerrain() const { return _terrainInterface.get(); }

        /** Gets the property set in use by this map engine. */
        virtual const TerrainOptions& getTerrainOptions() const =0;

        /** Accesses the compositor that controls the rendering of image layers */
        TextureCompositor* getTextureCompositor() const;

        /** Adds a terrain effect */
        void addEffect( TerrainEffect* effect );

        /** Removes a terrain effect */
        void removeEffect( TerrainEffect* effect );


    public: // Runtime properties

        /** Sets the scale factor to apply to elevation height values. Default is 1.0
          * @deprecated */
        void setVerticalScale( float value );

        /** Gets the scale factor to apply to elevation height values.
          * #deprecated */
        float getVerticalScale() const { return _verticalScale; }

        /** Sets the sampling ratio for elevation grid data. Default is 1.0.
          * @deprecated */
        void setElevationSamplingRatio( float value );

        /** Gets the sampling ratio for elevation grid data.
          * @depreceated */
        float getElevationSamplingRatio() const { return _elevationSamplingRatio; }

    protected:
        TerrainEngineNode();

        //TerrainEngineNode( const TerrainEngineNode& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL );

        virtual ~TerrainEngineNode();

    public: // osg::Node overrides
        virtual osg::BoundingSphere computeBound() const;
        virtual void traverse( osg::NodeVisitor& );

    protected: // implementation events
        virtual void onVerticalScaleChanged() { }
        virtual void onElevationSamplingRatioChanged() { }

    protected:
        friend class MapNode;
        friend class TerrainEngineNodeFactory;

        virtual void validateTerrainOptions( TerrainOptions& options );

        /** Attaches a map to the terrain engine and initialized it.*/
        virtual void preInitialize( const Map* map, const TerrainOptions& options );
        virtual void postInitialize( const Map* map, const TerrainOptions& options );

        // signals that a redraw is needed because something changed.
        virtual void dirty();

        // allow subclasses direct access for convenience.
        osg::ref_ptr<TextureCompositor> _texCompositor;

    public: // utility

        /**
         * Utility function that will return an osg::Node representing the geometry
         * for a tile key. The node is standalone; it has no ability to load children
         * or receive updates.
         */
        virtual osg::Node* createTile( const TileKey& key ) =0;

    private:
        friend struct TerrainEngineNodeCallbackProxy;
        friend struct MapNodeMapLayerController;

        void ctor();
        void onMapInfoEstablished( const MapInfo& mapInfo ); // not virtual!
        void onMapModelChanged( const MapModelChange& change );
        void updateImageUniforms();
        virtual void updateTextureCombining() { }

    private:
        
        struct ImageLayerController : public ImageLayerCallback
        {
            ImageLayerController( const Map* map, TerrainEngineNode* engine );

            void onVisibleChanged( TerrainLayer* layer );
            void onOpacityChanged( ImageLayer* layer );
            void onColorFiltersChanged( ImageLayer* layer );      
            void onVisibleRangeChanged( ImageLayer* layer );

            ArrayUniform _layerOpacityUniform;
            ArrayUniform _layerVisibleUniform;
            ArrayUniform _layerRangeUniform;

        private:
            MapFrame           _mapf;
            TerrainEngineNode* _engine;
            friend class TerrainEngineNode;
        };

        osg::ref_ptr<ImageLayerController> _imageLayerController;
        osg::ref_ptr<const Map>            _map;
        osg::ref_ptr<osg::Uniform>         _startFrameTimeUniform;
        osg::ref_ptr<osg::Uniform>         _cameraElevationUniform;
        bool                               _redrawRequired;
        float                              _verticalScale;
        float                              _elevationSamplingRatio;
        osg::ref_ptr<Terrain>              _terrainInterface;
        unsigned                           _dirtyCount;
        //UpdateLightingUniformsHelper       _updateLightingUniformsHelper;
        
        enum InitStage {
            INIT_NONE,
            INIT_PREINIT_COMPLETE,
            INIT_POSTINIT_COMPLETE
        };
        InitStage _initStage;

        typedef std::vector<osg::ref_ptr<TerrainEffect> > TerrainEffectVector;
        TerrainEffectVector effects_;

    public:

        /** Access a typed effect. */
        template<typename T>
        T* getEffect() {
            for(TerrainEffectVector::iterator i = effects_.begin(); i != effects_.end(); ++i ) {
                T* e = dynamic_cast<T*>(i->get());
                if ( e ) return e;
            }
            return 0L;
        }
    };

    /**
     * Factory class for creating terrain engine instances.
     */
    class TerrainEngineNodeFactory
    {
    public:
        static TerrainEngineNode* create( Map* map, const TerrainOptions& options );
    };

    /**
     * Base class for a node that "decorates" the terrain engine node within a map node.
     */
    class TerrainDecorator : public osg::Group
    {
    public:
        virtual void onInstall( TerrainEngineNode* engine );
        virtual void onUninstall( TerrainEngineNode* engine );

    protected:
        virtual ~TerrainDecorator();
    };

} // namespace osgEarth

#endif // OSGEARTH_TERRAIN_ENGINE_NODE_H
