/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
 * Copyright 2008-2014 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_REX_TERRAIN_DRAW_TILE_COMMAND_H
#define OSGEARTH_REX_TERRAIN_DRAW_TILE_COMMAND_H 1

#include "TileRenderModel"
#include "DrawState"
#include "GeometryPool"
#include "TileDrawable"
#include <osgEarth/TerrainTileNode>
#include <osgEarth/PatchLayer>
#include <osgEarth/TileKey>
#include <osg/Matrix>
#include <osg/Geometry>
#include <list>

using namespace osgEarth;

namespace osgEarth { namespace REX
{
    /**
     * All data necessary to draw a single terrain tile.
     */
    struct DrawTileCommand : public osgEarth::TileState
    {
        // Tile key
        const TileKey* _key;

        // ModelView matrix to apply before rendering this tile
        osg::ref_ptr<const osg::RefMatrix> _modelViewMatrix;

        osg::ref_ptr<const osg::RefMatrix> _localToWorld;

        // Samplers that are shared between all rendering passes
        const Samplers* _sharedSamplers;

        // Samplers specific to one rendering pass
        const Samplers* _colorSamplers;

        // Tile geometry, if present (ref_ptr necessary?)
        osg::ref_ptr<SharedGeometry> _geom;

        TileDrawable* _tile;

        // Tile key value to push to uniform just before drawing
        osg::Vec4f _keyValue;

        // Data revision # of the tile
        unsigned _tileRevision;

        // Coefficient used for tile vertex morphing
        osg::Vec2f _morphConstants;

        // Custom draw callback to call instead of rendering _geom
        TileRenderer* _drawCallback;

        // When drawing _geom, whether to render as GL_PATCHES 
        // instead of GL_TRIANGLES (for patch layers)
        bool _drawPatch;

        // Distance from camera to center of tile
        float _range;

        // Layer order for this tile. i.e., if this is zero, then this command
        // is drawing a tile for the first LayerDrawable.
        int _layerOrder;

        // Order in which this tile appears within the layer
        int _sequence;

        // True if the tile has a custom constrainted mesh
        bool _hasConstraints;

        // Renders the tile.
        void draw(osg::RenderInfo& ri) const;

        // Less than operator will ensure that tiles are sorted near to far
        // to minimze overdraw.
        bool operator < (const DrawTileCommand& rhs) const
        {
            if (_range > rhs._range) return false;
            if (_range < rhs._range) return true;
            return _geom.get() < rhs._geom.get();
        }

        bool operator == (const DrawTileCommand& rhs) const
        {
            return
                // same tile
                _tile == rhs._tile &&
                // same revision
                _tileRevision == rhs._tileRevision &&
                // same matrix
                *_modelViewMatrix == *rhs._modelViewMatrix &&
                // same underlying geometry pointer - this is important
                // if we're doing bindless NV rendering and have to 
                // use a different GPU buffer address.
                _geom.get() == rhs._geom.get();
        }

        DrawTileCommand() :
            _sharedSamplers(0L),
            _colorSamplers(0L),
            _geom(0L),
            _drawCallback(0L),
            _drawPatch(false),
            _range(0.0f),
            _layerOrder(INT_MAX),
            _sequence(0),
            _hasConstraints(false) { }

        virtual void accept(osg::PrimitiveFunctor& functor) const;
        virtual void accept(osg::PrimitiveIndexFunctor& functor) const;

    public: // TileState

        const TileKey& getKey() const override {
            return *_key;
        }

        int getRevision() const override {
            return _tileRevision;
        }

        int getSequence() const override {
            return _sequence;
        }

        const osg::BoundingBox& getBBox() const override {
            return _tile->getBoundingBox();
        }

        const osg::Matrix& getModelViewMatrix() const override {
            return *_modelViewMatrix.get();
        }

        const osg::RefMatrix* getLocalToWorld() const override {
            return _localToWorld;
            //return *_modelViewMatrix.get();
        }

        //! Apply the GL state for this tile (all samplers and uniforms)
        //! in preparation for rendering or computation
        bool apply(
            osg::RenderInfo& ri,
            void* implData) const override;

        void debug(
            osg::RenderInfo& ri,
            void* implData) const override;
    };

    /**
     * Ordered list of tile drawing commands.
     * List is faster than vector here (benchmarked)
     */
    typedef std::vector<DrawTileCommand> DrawTileCommands;

} } // namespace 

#endif // OSGEARTH_REX_TERRAIN_DRAW_TILE_COMMAND_H
