/* -*-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_DROAM_ENGINE_DIAMOND_H
#define OSGEARTH_DROAM_ENGINE_DIAMOND_H 1

#include "Common"
#include "AMRGeometry"
#include <osgEarth/Revisioning>
#include <osgEarth/TileKey>
#include <osgEarth/TaskService>
#include <osg/Geometry>

class MeshManager;

// ancestor incidies (counter-clockwise from the bottom)
enum DiamondAncestor {
    QUADTREE = 0, // bottom of the diamond (also the quadtree ancestor)
    PARENT_R = 1, // right parent
    GDPARENT = 2, // top of the diamond
    PARENT_L = 3  // left parent
};

enum DiamondStatus {
    ACTIVE   = 0, // diamond is active in the mesh
    INACTIVE = 1  // diamond is idle or has been flagged for deletion
};

// diamond's orientation. 0 is "up".
typedef unsigned short Orientation;

struct RevisionedStateSet : public osgEarth::Revisioned<osg::StateSet>
{
};

/** 
* A diamond is the basic unit object for the terrain mesh. There are two types of
* diamonds: Geometry diamonds reside at odd levels and contain actual geometry.
* Intermediate diamonds reside at even levels and serve to connect the continuous mesh
* between geometry levels, but contain no visible geometry themselves.
*/
struct Diamond : osg::Referenced
{
    Diamond( MeshManager* mesh, osgEarth::TileKey* key, Level level, const std::string& name ="" );
    ~Diamond();

    void activate();                    // call after the diamond is entirely initialized.

    osg::ref_ptr<osgEarth::TileKey> _key;         // tile key corresponding to this diamond (geom diamonds only)
    std::string _name;                  // name of this diamond (for debugging only-- remove later)
    Level _level;                       // subdivision level
    Orientation _orientation;           // orientation of the diamond
    DiamondStatus _status;              // whether this diamond is active or has been removed
    MeshManager* _mesh;                 // the mesh under which this diamond exists
    osg::ref_ptr<Diamond> _a[4];        // 4 ancestors (use Ancestor enum as indicies)
    osg::ref_ptr<Diamond> _c[4];        // immediate children (clockwise from bottom-right)
    unsigned short _childValence;       // number of slots for children (always 4, except 3 at level 0)
    bool _isSplit;                      // is this diamond split? (i.e. can it create children?)
    bool _hasGeometry;                  // whether this is a geometry diamond (i.e. odd-numbered level)
    NodeIndex _vi;                      // index of this diamond's vertex/normal in the master vbos
    TravNumber _lastCullFrame;          // frame number of last cull pass
    bool _drawableDirty;                // marks this diamond's primitive set as needing a refresh.
    bool _queuedForSplit;               // whether this diamond is in the split queue.
    bool _queuedForMerge;               // whether this diamond is in the merge queue.
    bool _queuedForImage;               // whether this diamond is in the image queue.
    bool _bsComputed;                   // whether _bs has been computed yet
    bool _imageRequested;               // whether the texture image has been submitted to the task service
    osg::ref_ptr<RevisionedStateSet> _stateSet; // stateset for this diamond level
    osg::ref_ptr<AMRDrawable> _amrDrawable;     // primitive draw list
    Diamond* _targetStateSetOwner;      // Diamond that owns the stateset we ultimately want to use
    Diamond* _currentStateSetOwner;     // Diamond that owns the stateset we are currently using
    osgEarth::Revision _targetStateSetRevision;   // Revision of the stateset we are rendering with
    bool _hasFinalImage;                // whether the final texture has been loaded into the stateset.
    
    osg::BoundingSphere _visibleBound;  // this diamond's visible extent (for drawing)
    const osg::BoundingSphere& visibleBound() const;

    osg::BoundingSphere _extendedBound; // this diamond's extended extent (for split/merge determination)  
    const osg::BoundingSphere& extendedBound() const;

    osg::ref_ptr<osgEarth::TaskRequest> _imageRequest;



    /** print debugging info */
    void dump();

    /** Sets the diamond centroid coordinate and adds it to the mesh. */
    void setCoord( double x, double y, double z ) { setCoord( osg::Vec3d(x,y,z) ); }
    void setCoord( const osg::Vec3d& coord );

    /** Gets this diamond's neibghor across child-c boundary, creating it if it does not exist */
    Diamond* getOrCreateNeighbor( ChildIndex c );

    /** Gets this diamond's neighbor across child-c boundary, returning NULL if it does not exist. */
    Diamond* getNeighbor( ChildIndex c );

    /** Gets this diamond's child at index c, creating it if it does not exist. */
    Diamond* getOrCreateChild( ChildIndex c );

    /** Removes the child at the specified index. */
    void removeChild( ChildIndex c );

    /** gets the index of a child diamond in teh children array, or return
    _numChildren if not found */
    ChildIndex getIndexOfChild( Diamond* child );

    /** Gets the child index of the diamond edge that begins at the specified vertex. */
    ChildIndex getIndexOfChildEdgeStartingAt( NodeIndex vi );

    /** recursively populate the diamond bintree up to a maximum depth level. */
    void seed( Level maxLevel );

    /** splits the diamond (making children possible). */
    void split();

    /** merges a split diamond (and removes all children). */
    void merge();

    /** returns true is this diamond has at least one child. */
    bool hasChildren() const;

    /** returns true if this diamond is "split", i.e. it has all possible children. */
    bool hasAllChildren() const;

    /** recalcuates the squared radius of thid diamond. */
    void computeBound();

    /** update the culling status of the diamond and its children based on a new eyepoint. */
    unsigned int cull( osgUtil::CullVisitor* cv );

    /** assigns a new child to this diamond and invalidates the primitive set. */
    void setChild( ChildIndex index, Diamond* child );

    /** rebuilds this diamond's draw list if necessary (and updates its precense in the mesh) */ 
    void refreshDrawable();

    /** marks this diamond's primitive set as needing a refresh. */
    void dirty();

    /** access a quadtree decendant */
    inline Diamond* q(ChildIndex c) {
        ChildIndex c2 = c == 0 || c == 2 ? 1 : 3;
        return _c[c].valid() ? _c[c]->_c[c2].get() : 0L;
    }

    //const osg::Vec3f& vert() const; // { return _mesh->v(_vi); }
    //const osg::Vec3d& coord() const;
    //const osg::Vec3f& normal() const;
    //const osg::Vec3d& geoCoord() const;
    const MeshNode& node() const;

    //void releaseGLObjects();
};

#endif // OSGEARTH_DROAM_ENGINE_DIAMOND_H
