/* -*-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.
*/

#ifdef WIN32

/////////////////////////////////////////////////////////////////////////////
// Disable unavoidable warning messages:
//
// C4503 - decorated name length exceeded, name was truncated
//
#pragma warning(disable : 4503)

#endif // WIN32

#ifndef DATASET_H
#define DATASET_H 1

#include <osg/BoundingSphere>
#include <osg/Node>
#include <osg/Matrixd>
#include <osg/Image>
#include <osg/Shape>
#include <osg/CoordinateSystemNode>
#include <osg/State>

#include <osgDB/Archive>

#include <set>

#include <vpb/Export>

// forward declare so we can avoid tieing vpb to GDAL.
class GDALDataset;
class GDALRasterBand;

namespace vpb
{

#define MAXIMUM_NUMBER_OF_LEVELS 30

class GeospatialExtents
{
public:

    osg::Vec2d  _min;
    osg::Vec2d  _max;
    bool        _isGeographic;
    
    inline GeospatialExtents() : 
        _min(DBL_MAX,DBL_MAX),
        _max(-DBL_MAX,-DBL_MAX),
        _isGeographic(false) {}

    inline GeospatialExtents(double xmin, double ymin, double xmax, double ymax, bool isGeographic) :
        _min(xmin, ymin),
        _max(xmax, ymax),
        _isGeographic(isGeographic) {}

    inline double& xMin() { return _min.x(); }
    inline double xMin() const { return _min.x(); }

    inline double& yMin() { return _min.y(); }
    inline double yMin() const { return _min.y(); }

    inline double& xMax() { return _max.x(); }
    inline double xMax() const { return _max.x(); }

    inline double& yMax() { return _max.y(); }
    inline double yMax() const { return _max.y(); }

    inline void init()
    {
        _min.set(DBL_MAX,DBL_MAX);
        _max.set(-DBL_MAX,-DBL_MAX);
    }

    inline bool valid() const
    {
        return _max.x()>=_min.x() &&  _max.y()>=_min.y();
    }

    inline double radius() const
    {
        return sqrt((radius2()));
    }

    inline double radius2() const
    {
        return 0.25f*((_max-_min).length2());
    }

    GeospatialExtents intersection(const GeospatialExtents& e, double xoffset) const
    {    
        return GeospatialExtents(osg::maximum(xMin(),e.xMin()+xoffset),osg::maximum(yMin(),e.yMin()),
                                 osg::minimum(xMax(),e.xMax()+xoffset),osg::minimum(yMax(),e.yMax()),_isGeographic);
    }

    /** Return true if this bounding box intersects the specified bounding box. */
    bool intersects(const GeospatialExtents& bb) const
    {
        if (_isGeographic)
        {
            // first check vertical axis overlap
            if (osg::maximum(yMin(),bb.yMin()) > osg::minimum(yMax(),bb.yMax())) return false;
            
            // next check if overlaps directly without any 360 degree horizontal shifts.
            if (osg::maximum(xMin(),bb.xMin()) <= osg::minimum(xMax(),bb.xMax())) return true;
            
            // next check if a 360 rotation will produce an overlap
            float rotationAngle = (xMin() > bb.xMin()) ? 360.0 : -360;
            return (osg::maximum(xMin(),bb.xMin()+rotationAngle) <= osg::minimum(xMax(),bb.xMax()+rotationAngle));
        }
        else
        {
            return (osg::maximum(xMin(),bb.xMin()) <= osg::minimum(xMax(),bb.xMax()) &&
                    osg::maximum(yMin(),bb.yMin()) <= osg::minimum(yMax(),bb.yMax()));
        }
    }

 
    void expandBy(const osg::BoundingSphere& sh)
    {
        if (!sh.valid()) return;

        if(sh._center.x()-sh._radius<_min.x()) _min.x() = sh._center.x()-sh._radius;
        if(sh._center.x()+sh._radius>_max.x()) _max.x() = sh._center.x()+sh._radius;

        if(sh._center.y()-sh._radius<_min.y()) _min.y() = sh._center.y()-sh._radius;
        if(sh._center.y()+sh._radius>_max.y()) _max.y() = sh._center.y()+sh._radius;
    }

    inline void expandBy(const osg::Vec3& v)
    {
        if(v.x()<_min.x()) _min.x() = v.x();
        if(v.x()>_max.x()) _max.x() = v.x();

        if(v.y()<_min.y()) _min.y() = v.y();
        if(v.y()>_max.y()) _max.y() = v.y();
    }

    void expandBy(const GeospatialExtents& e)
    {
        if (!e.valid()) return;

        if(e._min.x()<_min.x()) _min.x() = e._min.x();
        if(e._max.x()>_max.x()) _max.x() = e._max.x();

        if(e._min.y()<_min.y()) _min.y() = e._min.y();
        if(e._max.y()>_max.y()) _max.y() = e._max.y();
    }
};



class VPB_EXPORT DataSet : public osg::Referenced
{
    public:

        static std::string coordinateSystemStringToWTK(const std::string& coordinateSystem);

        class Source;

        struct VPB_EXPORT SpatialProperties
        {
            enum DataType
            {
                NONE,
                RASTER,
                VECTOR
            };

            SpatialProperties():
                _dataType(RASTER),
                _numValuesX(0),
                _numValuesY(0),
                _numValuesZ(0) {}
        
            SpatialProperties(const SpatialProperties& sp):
                _cs(sp._cs),
                _geoTransform(sp._geoTransform),
                _extents(sp._extents),
                _dataType(sp._dataType),
                _numValuesX(sp._numValuesX),
                _numValuesY(sp._numValuesY),
                _numValuesZ(sp._numValuesZ) {}

             SpatialProperties(osg::CoordinateSystemNode* cs, const GeospatialExtents& extents):
                _cs(cs),
                _extents(extents),
                _dataType(RASTER),
                _numValuesX(0),
                _numValuesY(0),
                _numValuesZ(0) {}
        
            inline SpatialProperties& assignSpatialProperties(const SpatialProperties& sp)
            {
                if (&sp==this) return *this;
                
                _cs = sp._cs;
                _geoTransform = sp._geoTransform;
                _extents = sp._extents;
                _dataType = sp._dataType;
                _numValuesX = sp._numValuesX;
                _numValuesY = sp._numValuesY;
                _numValuesZ = sp._numValuesZ;
                
                return *this;
            }
            
            void computeExtents();

            osg::ref_ptr<osg::CoordinateSystemNode>     _cs;
            osg::Matrixd                                _geoTransform;
            GeospatialExtents                           _extents;
            DataType                                    _dataType;
            unsigned int                                _numValuesX;
            unsigned int                                _numValuesY;
            unsigned int                                _numValuesZ;
        };

        struct DestinationData : public osg::Referenced, SpatialProperties
        {
        
            DestinationData(DataSet* dataSet):
                _dataSet(dataSet),
                _minDistance(0.0),
                _maxDistance(FLT_MAX) {}
        
            
            typedef std::vector< osg::ref_ptr<osg::Image> > ImageList;
            typedef std::vector< osg::ref_ptr<osg::Node> > ModelList;
            
            DataSet*                                    _dataSet;

            float                                       _minDistance;
            float                                       _maxDistance;

            
            osg::ref_ptr<osg::Image>                    _image;
            osg::ref_ptr<osg::HeightField>              _heightField;
            ModelList                                   _models;
        };

        struct VPB_EXPORT SourceData : public osg::Referenced, public SpatialProperties
        {
        
            SourceData(Source* source=0):
                _source(source),
                _hasGCPs(false),
                _gdalDataset(0),
                _hfDataset(0) {}
                
            virtual ~SourceData();

            static SourceData* readData(Source* source);
            
            GeospatialExtents getExtents(const osg::CoordinateSystemNode* cs) const;
            
            const SpatialProperties& computeSpatialProperties(const osg::CoordinateSystemNode* cs) const;

            bool intersects(const SpatialProperties& sp) const;

            void read(DestinationData& destination);
            
            virtual void readImage(DestinationData& destination);
            virtual void readHeightField(DestinationData& destination);
            virtual void readModels(DestinationData& destination);

            float getInterpolatedValue(GDALRasterBand *band, double x, double y);
            float getInterpolatedValue(osg::HeightField* hf, double x, double y);

            Source*                                     _source;
            
            bool                                        _hasGCPs;
            
            osg::ref_ptr<osg::Node>                     _model;
            GDALDataset*                                _gdalDataset;
            osg::HeightField*                           _hfDataset;
            
            typedef std::map<const osg::CoordinateSystemNode*,SpatialProperties> SpatialPropertiesMap;
            mutable SpatialPropertiesMap _spatialPropertiesMap;
            
            
        };


        class VPB_EXPORT Source : public osg::Referenced, public SpatialProperties
        {
        public:
        
            enum Type
            {
                IMAGE,
                HEIGHT_FIELD,
                MODEL
            };
            
            enum ParameterPolicy
            {
                PREFER_CONFIG_SETTINGS,
                PREFER_CONFIG_SETTINGS_BUT_SCALE_BY_FILE_RESOLUTION,
                PREFER_FILE_SETTINGS
            };

            Source():
                _type(IMAGE),
                _sortValue(0.0),
                _temporaryFile(false),
                _coordinateSystemPolicy(PREFER_FILE_SETTINGS),
                _geoTransformPolicy(PREFER_FILE_SETTINGS),
                _minLevel(0),
                _maxLevel(MAXIMUM_NUMBER_OF_LEVELS),
                _layer(0),
                _gdalDataset(0),
                _hfDataset(0)
                {}
        
            Source(Type type, const std::string& filename):
                _type(type),
                _sortValue(0.0),
                _filename(filename),
                _temporaryFile(false),
                _coordinateSystemPolicy(PREFER_FILE_SETTINGS),
                _geoTransformPolicy(PREFER_FILE_SETTINGS),
                _minLevel(0),
                _maxLevel(MAXIMUM_NUMBER_OF_LEVELS),
                _layer(0),
                _gdalDataset(0),
                _hfDataset(0)
                {}

            void setSortValue(double s) { _sortValue = s; }
            double getSortValue() const { return _sortValue; }

            void setSortValueFromSourceDataResolution();

            void setType(Type type) { _type = type; }
            Type getType() const { return _type; }

            void setFileName(const std::string& filename) { _filename = filename; }
            const std::string& getFileName() const { return _filename; }

            void setTemporaryFile(bool temporaryFile) { _temporaryFile = temporaryFile; }
            bool getTemporaryFile() const { return _temporaryFile; }

            void setGdalDataset(void* gdalDataset);
            void* getGdalDataset();
            const void* getGdalDataset() const;

            void setHFDataset(void* hfDataset);
            void* getHFDataset();
            const void* getHFDataset() const;

            void setCoordinateSystemPolicy(ParameterPolicy policy) { _coordinateSystemPolicy = policy; }
            ParameterPolicy getCoordinateSystemPolicy() const { return _coordinateSystemPolicy; }

            void setCoordinateSystem(const std::string& wellKnownText) { _cs = new osg::CoordinateSystemNode("WKT",wellKnownText); }
            void setCoordinateSystem(osg::CoordinateSystemNode* cs) { _cs = cs; }
            osg::CoordinateSystemNode* getCoordinateSystem() { return  _cs.get(); }

            
            void setGeoTransformPolicy(ParameterPolicy policy)  { _geoTransformPolicy = policy; }
            ParameterPolicy getGeoTransformPolicy() const { return _geoTransformPolicy; }

            void setGeoTransform(osg::Matrixd& transform) { _geoTransform = transform; }
            osg::Matrixd& getGeoTransform() { return _geoTransform; }
            
            void setGeoTransformFromRange(double xMin, double xMax, double yMin, double yMax)
            {
                _geoTransform.makeIdentity();
                _geoTransform(0,0) = xMax-xMin;
                _geoTransform(3,0) = xMin;

                _geoTransform(1,1) = yMax-yMin;
                _geoTransform(3,1) = yMin;
             }


            void assignCoordinateSystemAndGeoTransformAccordingToParameterPolicy();
            


            void setMinLevel(unsigned int minLevel) { _minLevel = minLevel; }
            void setMaxLevel(unsigned int maxLevel) { _maxLevel = maxLevel; }
            void setMinMaxLevel(unsigned int minLevel, unsigned int maxLevel) { _minLevel = minLevel; _maxLevel = maxLevel; }

            unsigned int getMinLevel() const { return _minLevel; }
            unsigned int getMaxLevel() const { return _maxLevel; }

            void setLayer(unsigned int layer) { _layer = layer; }
            unsigned int getLayer() const { return _layer; }


            void setSourceData(SourceData* data) { _sourceData = data; if (_sourceData.valid()) _sourceData->_source = this; }
            SourceData* getSourceData() { return _sourceData.get(); }
            
            bool intersects(const SpatialProperties& sp) const
            {
                return  _sourceData.valid()?_sourceData->intersects(sp):false;
            }

            void loadSourceData();

            
            bool needReproject(const osg::CoordinateSystemNode* cs) const;

            bool needReproject(const osg::CoordinateSystemNode* cs, double minResolution, double maxResolution) const;
            
            Source* doReproject(const std::string& filename, osg::CoordinateSystemNode* cs, double targetResolution=0.0) const;
           
            void buildOverviews();
            
            
            struct ResolutionPair
            {
                ResolutionPair():
                    _resX(0.0),_resY(0.0) {}

                ResolutionPair(double x,double y):
                    _resX(x),_resY(y) {}
                    
                bool operator < (const ResolutionPair& rhs) const
                {
                    double minLHS = osg::minimum(_resX,_resY);
                    double minRHS = osg::minimum(rhs._resX,rhs._resY);
                    return minLHS<minRHS;
                }
            
                double _resX;
                double _resY;
            };
            
            typedef std::vector<ResolutionPair> ResolutionList;
            
            void addRequiredResolution(double resX, double resY) { _requiredResolutions.push_back(ResolutionPair(resX,resY)); }
            
            void setRequiredResolutions(ResolutionList& resolutions) { _requiredResolutions = resolutions; }
            
            ResolutionList& getRequiredResolutions() { return _requiredResolutions; }
            
            const ResolutionList& getRequiredResolutions() const { return _requiredResolutions; }
            
            void consolodateRequiredResolutions();

        protected:
        

            Type                                        _type;

            double                                      _sortValue;
        
            std::string                                 _filename;
            bool                                        _temporaryFile;
            
            ParameterPolicy                             _coordinateSystemPolicy;
            ParameterPolicy                             _geoTransformPolicy;

            unsigned int                                _minLevel;
            unsigned int                                _maxLevel;
            unsigned int                                _layer;
            
            osg::ref_ptr<SourceData>                    _sourceData;
            
            ResolutionList                              _requiredResolutions;
          
            GDALDataset*                                _gdalDataset;

            osg::HeightField*                           _hfDataset;
        };
        
        enum CompositeType
        {
            GROUP,
            LOD,
            PAGED_LOD
        };
        
        class VPB_EXPORT CompositeSource : public osg::Referenced, public SpatialProperties
        {
        public:
            
            CompositeSource(CompositeType type=GROUP):_type(type) {};
            
            typedef std::vector< osg::ref_ptr<Source> > SourceList;
            typedef std::vector< osg::ref_ptr< CompositeSource> > ChildList;
            
            void setType(CompositeType type) { _type = type; }
            CompositeType getType() const { return _type; }

            void setSortValueFromSourceDataResolution();

            void sort();

            class iterator
            {
            public:

                enum IteratorMode
                {
                    ACTIVE,
                    ALL
                };
            

                iterator(CompositeSource* composite=0,IteratorMode mode=ALL):
                    _iteratorMode(mode)
                {
                    if (composite) 
                    {
                        _positionStack.push_back(IteratorPosition(composite));
                    }
                }
                
                iterator(const iterator& rhs):
                    _positionStack(rhs._positionStack) {}

                iterator& operator = (const iterator& rhs)
                {
                    if (&rhs==this) return *this;
                    _positionStack = rhs._positionStack;
                    return *this;
                }
                
                bool operator == (const iterator& rhs) const
                {
                    return _positionStack == rhs._positionStack;
                }

                bool operator != (const iterator& rhs) const
                {
                    return _positionStack != rhs._positionStack;
                }

                bool valid() const
                {
                    return !_positionStack.empty() && _positionStack.back().valid();
                }
                                    
                CompositeSource& operator *()
                {
                    return *(valid()?_positionStack.back().current():0);
                }
                
                CompositeSource* operator ->()
                {
                    return valid()?_positionStack.back().current():0;
                }
                
                const CompositeSource& operator *() const
                {
                    return *(valid()?_positionStack.back().current():0);
                }
                
                const CompositeSource* operator ->() const
                {
                    return valid()?_positionStack.back().current():0;
                }

                iterator& operator++()
                {
                    advance(); 
                    return *this;
                }
                
                iterator operator++(int)
                {
                    iterator tmp=*this; 
                    advance(); 
                    return tmp; 
                }
                
                bool advance()
                {
                    if (_positionStack.empty()) return false;
                    
                    // simple advance to the next source
                    if (_positionStack.back().advance())
                    {
                        if (_positionStack.back().current())
                        {
                            _positionStack.push_back(IteratorPosition(_positionStack.back().current()));
                            return advance();
                        }
                    }
 
                    _positionStack.pop_back();
                    return advance();
                }

                
            protected:
            
                struct IteratorPosition
                {
                
                    IteratorPosition(CompositeSource* composite):
                        _composite(composite),
                        _index(-1) {}

                    IteratorPosition(const IteratorPosition& rhs):
                        _composite(rhs._composite),
                        _index(rhs._index) {}

                    IteratorPosition& operator = (const IteratorPosition& rhs)
                    {
                        _composite = rhs._composite;
                        _index = rhs._index;
                        return *this;
                    }

                    bool operator == (const IteratorPosition& rhs) const
                    {
                        return _composite == rhs._composite && _index == rhs._index;
                    }
                
                    bool operator != (const IteratorPosition& rhs) const
                    {
                        return _composite != rhs._composite || _index != rhs._index;
                    }

                    CompositeSource* current()
                    {
                        if (_index==-1) return _composite;
                        else return  (_index>=0 && _index < (int)_composite->_children.size())?_composite->_children[_index].get():0;
                    }

                    const CompositeSource* current() const
                    {
                        if (_index==-1) return _composite;
                        else return  (_index>=0 && _index < (int)_composite->_children.size())?_composite->_children[_index].get():0;
                    }

                    bool valid() const
                    {
                        return _composite && 
                               _index < (int)_composite->_children.size();
                    }
                    
                    inline bool advance()
                    {
                        return advanceToNextChild(*_composite,_index);
                    }

                    inline bool isActive(const CompositeSource& /*composite*/,int /*index*/)
                    {
                        return true;
                    }

                    inline bool advanceToNextChild(CompositeSource& composite, int& index)
                    {
                        ++index;
                        while (index<(int)composite._children.size()) 
                        {
                            if (isActive(composite,index)) return true;
                            ++index;
                        }
                        return false;
                    }

                    CompositeSource*                _composite;
                    int                             _index;
                };

                typedef std::vector<IteratorPosition> PositionStack;
                IteratorMode                        _iteratorMode;
                PositionStack                       _positionStack;
            };
        

            template<class T>
            class base_source_iterator
            {
            public:
            

                base_source_iterator(CompositeSource* composite=0, T advancer=T()):
                    _advancer(advancer),
                    _compositeIterator(composite),
                    _sourceIndex(-1)
                {
                    advance();
                }
                
                base_source_iterator(const base_source_iterator& rhs):
                    _advancer(rhs._advancer),
                    _compositeIterator(rhs._compositeIterator),
                    _sourceIndex(rhs._sourceIndex) {}

                base_source_iterator& operator = (const base_source_iterator& rhs)
                {
                    if (&rhs==this) return *this;
                    _advancer = rhs._advancer;
                    _compositeIterator = rhs._compositeIterator;
                    _sourceIndex = rhs._sourceIndex;
                }
                
                bool operator == (const base_source_iterator& rhs) const
                {
                    return _compositeIterator == rhs._compositeIterator &&
                           _sourceIndex == rhs._sourceIndex;
                }

                bool operator != (const base_source_iterator& rhs) const
                {
                    return _compositeIterator != rhs._compositeIterator ||
                           _sourceIndex != rhs._sourceIndex;
                }

                bool valid() const
                {
                    return _compositeIterator.valid() && _sourceIndex < (int)_compositeIterator->_sourceList.size();
                }
                                    
                osg::ref_ptr<Source>& operator *()
                {
                    return valid()?_compositeIterator->_sourceList[_sourceIndex]:_nullSource;
                }
                
                osg::ref_ptr<Source>* operator ->()
                {
                    return &(valid()?_compositeIterator->_sourceList[_sourceIndex]:_nullSource);
                }
                
                base_source_iterator& operator++()
                {
                    advance();
                    return *this;
                }
                
                base_source_iterator operator++(int)
                {
                    base_source_iterator tmp=*this; 
                    advance(); 
                    return tmp; 
                }
                
                bool advance()
                {
                    if (!_compositeIterator.valid()) return false;
                    
                    if (_advancer.advanceToNextSource(*_compositeIterator,_sourceIndex)) return true;

                    // at end of current CompositeSource, so need to advance to new one.
                    _sourceIndex = -1;
                    ++_compositeIterator;
                    return advance();
                }
                               
            protected:
            
                T                       _advancer;
                iterator                _compositeIterator;
                int                     _sourceIndex;
                osg::ref_ptr<Source>    _nullSource;

            };

            struct DefaultSourceAdvancer
            {
                DefaultSourceAdvancer() {}
                
                bool isActive(const CompositeSource& /*composite*/,int /*index*/)
                {
                    return true;
                }

                inline bool advanceToNextSource(const CompositeSource& composite, int& index)
                {
                    return ++index<(int)composite._sourceList.size();
                }
            };
        
            struct LODSourceAdvancer
            {
                LODSourceAdvancer(float targetResolution=0.0f):
                    _targetResolution(targetResolution) {}
                
                inline bool advanceToNextSource(const CompositeSource& composite, int& index)
                {
                    if (composite.getType()==GROUP)
                    {
                        return (++index<(int)composite._sourceList.size());
                    }
                    else
                    {
                        if (composite._sourceList.empty()) return false;
                        if (index!=-1) return false; // we've already traversed this composite, only ever one valid LOD.
                    
                        // find source with resolution closest to target
                        int foundIndex = 0;
                        float closestResolution = fabsf(composite._sourceList[0]->getSortValue()-_targetResolution);
                        for(int i=1;i<(int)composite._sourceList.size();++i)
                        {
                            float delta = fabsf(composite._sourceList[i]->getSortValue()-_targetResolution);
                            if (delta<closestResolution)
                            {
                                foundIndex = i;
                                closestResolution = delta;
                            }
                        }
                        if (foundIndex==index) return false;
                        index = foundIndex;
                        return true;
                    }
                }
                
                float _targetResolution;
            };

            typedef base_source_iterator<DefaultSourceAdvancer> source_iterator;
            typedef base_source_iterator<LODSourceAdvancer>     source_lod_iterator;
           
            CompositeType   _type;
            SourceList      _sourceList;
            ChildList       _children;
        };
        
        
        class VPB_EXPORT DestinationTile : public osg::Referenced, public SpatialProperties
        {
        public:
        
        
            enum Position
            {
                LEFT        = 0,
                LEFT_BELOW  = 1,
                BELOW       = 2,
                BELOW_RIGHT = 3,
                RIGHT       = 4,
                RIGHT_ABOVE = 5,
                ABOVE       = 6,
                ABOVE_LEFT  = 7,
                NUMBER_OF_POSITIONS = 8
            };
        
        
            DestinationTile();
            
            void computeNeighboursFromQuadMap();

            void setNeighbours(DestinationTile* left, DestinationTile* left_below, 
                               DestinationTile* below, DestinationTile* below_right,
                               DestinationTile* right, DestinationTile* right_above,
                               DestinationTile* above, DestinationTile* above_left);
                               
            void checkNeighbouringTiles();
            
            void setMaximumImagerySize(unsigned int maxNumColumns,unsigned int maxNumRows)
            {
                _imagery_maxNumColumns = maxNumColumns;
                _imagery_maxNumRows = maxNumRows;
            }

            void setMaximumTerrainSize(unsigned int maxNumColumns,unsigned int maxNumRows)
            {
                _terrain_maxNumColumns = maxNumColumns;
                _terrain_maxNumRows = maxNumRows;
            }
            
            void computeMaximumSourceResolution(CompositeSource* sourceGraph);

            bool computeImageResolution(unsigned int layer, unsigned int& numColumns, unsigned int& numRows, double& resX, double& resY);
            bool computeTerrainResolution(unsigned int& numColumns, unsigned int& numRows, double& resX, double& resY);
            
            void allocate();
            
            void addRequiredResolutions(CompositeSource* sourceGraph);

            void readFrom(CompositeSource* sourceGraph);

            void allocateEdgeNormals();
            
            void equalizeCorner(Position position);
            void equalizeEdge(Position position);
            
            void equalizeBoundaries();
            
            void setTileComplete(bool complete);
            bool getTileComplete() const { return _complete; }

            void optimizeResolution();

            osg::Node* createScene();

            osg::StateSet* createStateSet();
            osg::Node* createHeightField();
            osg::Node* createPolygonal();

            void unrefData();
            
            
            DataSet*                                    _dataSet;
            std::string                                 _name;
            unsigned int                                _level;
            unsigned int                                _tileX;
            unsigned int                                _tileY;
            GLenum                                      _pixelFormat;                                    
            
            struct ImageData
            {
                ImageData():
                    _imagery_maxSourceResolutionX(0.0f),
                    _imagery_maxSourceResolutionY(0.0f) {}

            
                float                                  _imagery_maxSourceResolutionX;
                float                                  _imagery_maxSourceResolutionY;

                osg::ref_ptr<DestinationData>          _imagery;
            };

            std::vector<ImageData>                      _imagery;
            
            inline ImageData& getImageData(unsigned int layer)
            {
                if (layer>=_imagery.size()) _imagery.resize(layer+1);
                return _imagery[layer];
            }
            
            osg::ref_ptr<DestinationData>               _terrain;
            osg::ref_ptr<DestinationData>               _models;
            
            DestinationTile*                            _neighbour[NUMBER_OF_POSITIONS];
            bool                                        _equalized[NUMBER_OF_POSITIONS];
            
            
            unsigned int                                _maxSourceLevel;

            unsigned int                                _imagery_maxNumColumns;
            unsigned int                                _imagery_maxNumRows;
                        
            unsigned int                                _terrain_maxNumColumns;
            unsigned int                                _terrain_maxNumRows;
            float                                       _terrain_maxSourceResolutionX;
            float                                       _terrain_maxSourceResolutionY;
            
            bool                                        _complete;
            
            typedef std::vector<osg::Vec2> HeightDeltaList;
            HeightDeltaList                             _heightDeltas[NUMBER_OF_POSITIONS];

        };

        class VPB_EXPORT CompositeDestination : public osg::Referenced, public SpatialProperties
        {
        public:   
        
            CompositeDestination():
                _dataSet(0),
                _parent(0),
                _level(0),
                _tileX(0),
                _tileY(0),
                _type(GROUP),
                _maxVisibleDistance(FLT_MAX),
                _subTileGenerated(false) {}
        
            CompositeDestination(osg::CoordinateSystemNode* cs, const GeospatialExtents& extents):
                SpatialProperties(cs,extents),
                _dataSet(0),
                _parent(0),
                _level(0),
                _tileX(0),
                _tileY(0),
                _type(GROUP),
                _maxVisibleDistance(FLT_MAX),
                _subTileGenerated(false)  {}
          
            void computeNeighboursFromQuadMap();

            void addRequiredResolutions(CompositeSource* sourceGraph);
            
            void readFrom(CompositeSource* sourceGraph);

            void equalizeBoundaries();

            osg::Node* createScene();
            
            bool areSubTilesComplete();
            std::string getSubTileName();
            osg::Node* createPagedLODScene();
            osg::Node* createSubTileScene();

            void unrefSubTileData();
            void unrefLocalData();

            void setSubTilesGenerated(bool generated) { _subTileGenerated=generated; }
            bool getSubTilesGenerated() const { return _subTileGenerated; }
            

            typedef std::vector< osg::ref_ptr<DestinationTile> > TileList;
            typedef std::vector< osg::ref_ptr<CompositeDestination> > ChildList;

            DataSet*                _dataSet;
            CompositeDestination*   _parent;
            std::string             _name;
            unsigned int            _level;
            unsigned int            _tileX;
            unsigned int            _tileY;
            CompositeType           _type;
            TileList                _tiles;
            ChildList               _children;
            float                   _maxVisibleDistance;
            bool                    _subTileGenerated;
            
        };


        typedef std::map<unsigned int,CompositeDestination*> Row;
        typedef std::map<unsigned int,Row> Level;
        typedef std::map<unsigned int,Level> QuadMap;
        
        void insertTileToQuadMap(CompositeDestination* tile)
        {
            _quadMap[tile->_level][tile->_tileY][tile->_tileX] = tile;
        }
        
        DestinationTile* getTile(unsigned int level,unsigned int X, unsigned int Y)
        {
            CompositeDestination* cd = getComposite(level,X,Y);
            if (!cd) return 0;            
            if (cd->_tiles.empty()) return 0; 
            return (cd->_tiles).front().get();
        }

        CompositeDestination* getComposite(unsigned int level,unsigned int X, unsigned int Y)
        {
            QuadMap::iterator levelItr = _quadMap.find(level);
            if (levelItr==_quadMap.end()) return 0;

            Level::iterator rowItr = levelItr->second.find(Y);
            if (rowItr==levelItr->second.end()) return 0;

            Row::iterator columnItr = rowItr->second.find(X);
            if (columnItr==rowItr->second.end()) return 0;
            else return columnItr->second;
        }

        Row& getRow(unsigned int level,unsigned int Y)
        {
            return _quadMap[level][Y];
        }

    public:


        DataSet();
        

        void addSource(Source* source);
        void addSource(CompositeSource* composite);

        void loadSources();
        
        void setMaximumTileImageSize(unsigned int size) { _maximumTileImageSize = size; }
        unsigned int getMaximumTileImageSize() const { return _maximumTileImageSize; }

        void setMaximumTileTerrainSize(unsigned int size) { _maximumTileTerrainSize = size; }
        unsigned int getMaximumTileTerrainSize() const { return _maximumTileTerrainSize; }

        void setMaximumVisibleDistanceOfTopLevel(float d) { _maximumVisiableDistanceOfTopLevel = d; }
        float getMaximumVisibleDistanceOfTopLevel() const { return _maximumVisiableDistanceOfTopLevel; }

        void setRadiusToMaxVisibleDistanceRatio(float ratio) { _radiusToMaxVisibleDistanceRatio = ratio; }
        float getRadiusToMaxVisibleDistanceRatio() const { return _radiusToMaxVisibleDistanceRatio; }
    
        void setVerticalScale(float verticalScale) { _verticalScale = verticalScale; }
        float getVerticalScale() const { return _verticalScale; }
               
        void setSkirtRatio(float skirtRatio) { _skirtRatio = skirtRatio; }
        float getSkirtRatio() const { return _skirtRatio; }

        void setDefaultColor(const osg::Vec4& defaultColor) { _defaultColor = defaultColor; }
        const osg::Vec4& getDefaultColor() const { return _defaultColor; }

        void setDestinationCoordinateSystem(const std::string& wellKnownText) { setDestinationCoordinateSystem(new osg::CoordinateSystemNode("WKT",wellKnownText)); }
        void setDestinationCoordinateSystem(osg::CoordinateSystemNode* cs) { _destinationCoordinateSystem = cs; }
        osg::CoordinateSystemNode* getDestinationCoordinateSystem() { return _destinationCoordinateSystem .get(); }
        
        void setIntermediateCoordinateSystem(const std::string& wellKnownText) { setIntermediateCoordinateSystem(new osg::CoordinateSystemNode("WKT",wellKnownText)); }
        void setIntermediateCoordinateSystem(osg::CoordinateSystemNode* cs) { _intermediateCoordinateSystem = cs; }
        osg::CoordinateSystemNode* getIntermediateCoordinateSystem() { return _intermediateCoordinateSystem.get(); }

        void setConvertFromGeographicToGeocentric(bool flag) { _convertFromGeographicToGeocentric = flag; }
        bool getConvertFromGeographicToGeocentric() const { return _convertFromGeographicToGeocentric; }

        void setEllipsoidModel(osg::EllipsoidModel* et) { _ellipsoidModel = et; }
        osg::EllipsoidModel* getEllipsoidModel() { return _ellipsoidModel.get(); }
        const osg::EllipsoidModel* getEllipsoidModel() const { return _ellipsoidModel.get(); }
        
        bool mapLatLongsToXYZ() const { return getConvertFromGeographicToGeocentric() && getEllipsoidModel(); }

        void setDestinationExtents(const GeospatialExtents& extents) { _extents = extents; }
        
        void setDestinationGeoTransform(const osg::Matrixd& geoTransform) { _geoTransform = geoTransform; }

        /** Set the Archive name if one is to be used.*/
        void setArchiveName(const std::string& filename) { _archiveName = filename; }

        /** Get the Archive name.*/
        const std::string& getArchiveName() const { return _archiveName; }

        /** Set the Archive.*/
        void setArchive(osgDB::Archive* archive) { _archive = archive; }

        /** Get the Archive if one is to being used.*/
        osgDB::Archive* getArchive() { return _archive.get(); }

        /** Set the Directory, DestinationTileBaseName and DestinationTileExtension from the passed in filename.*/
        void setDestinationName(const std::string& filename);

        void setDirectory(const std::string& directory);
        const std::string& getDirectory() const { return _directory; }

        void setDestinationTileBaseName(const std::string& basename) { _tileBasename = basename; }
        const std::string& getDestinationTileBaseName() const { return _tileBasename; }
        
        void setDestinationTileExtension(const std::string& extension) { _tileExtension = extension; }
        const std::string& getDestinationTileExtension() const { return _tileExtension; }

        void setDestinationImageExtension(const std::string& extension) { _imageExtension = extension; }
        const std::string& getDestinationImageExtension() const { return _imageExtension; }
        
        enum DatabaseType
        {
            LOD_DATABASE,
            PagedLOD_DATABASE,
        };
        
        void setDatabaseType(DatabaseType type) { _databaseType = type; }
        DatabaseType getDatabaseType() const { return _databaseType; }

        enum GeometryType
        {
            HEIGHT_FIELD,
            POLYGONAL,
        };
        void setGeometryType(GeometryType type) { _geometryType = type; }
        GeometryType getGeometryType() const { return _geometryType; }

        enum TextureType
        {
            RGB_24,
            RGBA,
            RGB_16,
            RGBA_16,
            COMPRESSED_TEXTURE,
            COMPRESSED_RGBA_TEXTURE
        };
        void setTextureType(TextureType type) { _textureType = type; }
        TextureType getTextureType() const { return _textureType; }

        void setMaxAnisotropy(float d) { _maxAnisotropy = d; }
        float getMaxAnisotropy() const { return _maxAnisotropy; }


        enum MipMappingMode
        {
            NO_MIP_MAPPING, /// disable mip mapping - use LINEAR, LINEAR filters.
            MIP_MAPPING_HARDWARE, /// use mip mapping, dynamically compute them in hardware if supported
            MIP_MAPPING_IMAGERY /// use mip mapping, and store imagery along with associated mip maps.
        };

        void setMipMappingMode(MipMappingMode mipMappingMode) { _mipMappingMode = mipMappingMode; }
        MipMappingMode getMipMappingMode() const { return _mipMappingMode; }


        void setUseLocalTileTransform(bool flag) { _useLocalTileTransform = flag; }
        bool getUseLocalTileTransform() const { return _useLocalTileTransform; }
        
        void setSimplifyTerrain(bool flag) { _simplifyTerrain = flag; }
        bool getSimplifyTerrain() const { return _simplifyTerrain; }
        

        void setDecorateGeneratedSceneGraphWithCoordinateSystemNode(bool flag) { _decorateWithCoordinateSystemNode = flag; }
        bool getDecorateGeneratedSceneGraphWithCoordinateSystemNode() const { return _decorateWithCoordinateSystemNode; }


        void setDecorateGeneratedSceneGraphWithMultiTextureControl(bool flag) { _decorateWithMultiTextureControl = flag; }
        bool getDecorateGeneratedSceneGraphWithMultiTextureControl() const { return _decorateWithMultiTextureControl; }

        unsigned int getNumOfTextureLevels() const { return _numTextureLevels; }

        void setCommentString(const std::string& comment) { _comment = comment; }        
        const std::string& getCommentString() const { return _comment; }        


        void setWriteNodeBeforeSimplification(bool flag) { _writeNodeBeforeSimplification = flag; }
        bool getWriteNodeBeforeSimplification() const { return _writeNodeBeforeSimplification; }


        static void setNotifyOffset(int level);
        static int getNotifyOffset();


        CompositeDestination* createDestinationGraph(CompositeDestination* parent,
                                                     osg::CoordinateSystemNode* cs,
                                                     const GeospatialExtents& extents,
                                                     unsigned int maxImageSize,
                                                     unsigned int maxTerrainSize,
                                                     unsigned int currentLevel,
                                                     unsigned int currentX,
                                                     unsigned int currentY,
                                                     unsigned int maxNumLevels);
        
        
        void computeDestinationGraphFromSources(unsigned int numLevels);
        void updateSourcesForDestinationGraphNeeds();
        void populateDestinationGraphFromSources();
        
        void createDestination(unsigned int numLevels);
        
        void buildDestination() { _buildDestination(false); }
        
        void writeDestination() { _buildDestination(true); }
        
        osg::Node* getDestinationRootNode() { return _rootNode.get(); }

        // helper functions for handling optional archive
        void _writeNodeFile(const osg::Node& node,const std::string& filename);
        void _writeImageFile(const osg::Image& image,const std::string& filename);
        
        void setState(osg::State* state) { _state = state; }
        osg::State* getState() { return _state.get(); }

    protected:

        virtual ~DataSet() {}



        void _readRow(Row& row);
        void _equalizeRow(Row& row);
        void _writeRow(Row& row);
        void _buildDestination(bool writeToDisk);
        
        void init();

        osg::Node* decorateWithCoordinateSystemNode(osg::Node* subgraph);
        osg::Node* decorateWithMultiTextureControl(osg::Node* subgraph);


        osg::ref_ptr<CompositeSource>               _sourceGraph;
        
        osg::ref_ptr<CompositeDestination>          _destinationGraph;
        
        QuadMap                                     _quadMap;



        unsigned int                                _maximumTileImageSize;
        unsigned int                                _maximumTileTerrainSize;
        float                                       _maximumVisiableDistanceOfTopLevel;
    float                                       _radiusToMaxVisibleDistanceRatio;
        float                                       _verticalScale;
        float                                       _skirtRatio;
            
        osg::ref_ptr<osg::CoordinateSystemNode>     _destinationCoordinateSystem;
        osg::ref_ptr<osg::CoordinateSystemNode>     _intermediateCoordinateSystem;

        bool                                        _convertFromGeographicToGeocentric;
        osg::ref_ptr<osg::EllipsoidModel>           _ellipsoidModel;
 
        osg::Matrixd                                _geoTransform;
        GeospatialExtents                           _extents;
        std::string                                 _archiveName;
        osg::ref_ptr<osgDB::Archive>                _archive;
        std::string                                 _directory;
        std::string                                 _tileBasename;
        std::string                                 _tileExtension;
        std::string                                 _imageExtension;
        osg::Vec4                                   _defaultColor;
        DatabaseType                                _databaseType;
        GeometryType                                _geometryType;
        TextureType                                 _textureType;
        float                                       _maxAnisotropy;
        MipMappingMode                              _mipMappingMode;
        
        unsigned int                                _numTextureLevels;

        bool                                        _useLocalTileTransform;

        bool                                        _decorateWithCoordinateSystemNode;
        bool                                        _decorateWithMultiTextureControl;
        
        std::string                                 _comment;

        bool                                        _writeNodeBeforeSimplification;
        
        bool                                        _simplifyTerrain;

        osg::ref_ptr<osg::Node>                     _rootNode;
        osg::ref_ptr<osg::State>                    _state;

        
};

}

#endif
