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


/**
 * These classes assist in dealing with the Tile Map Service specification
 * (http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification)
 */

#ifndef OSGEARTH_UTIL_TMS_H
#define OSGEARTH_UTIL_TMS_H 1

#include <osgEarthUtil/Common>

#include <vector>
#include <iostream>
#include <osgEarth/Profile>
#include <osgEarth/Common>

#include <osg/Referenced>
#include <osgDB/ReaderWriter>

#include <osg/Version>
#if OSG_MIN_VERSION_REQUIRED(2,9,5)
#include <osgDB/Options>
#endif

namespace osgEarth 
{   
    class TileSource;
}

namespace osgEarth { namespace Util { namespace TMS
{
    /**
     * TMS tile format.  Specifies format information of a tile
     */
    class OSGEARTHUTIL_EXPORT TileFormat
    {
    public:
        TileFormat();

        /** dtor */
        virtual ~TileFormat() { }

        /**
        *Gets the width of a tile
        */
        unsigned int getWidth() const {return _width;}

        /**
        *Sets the width of a tile
        */
        void setWidth(unsigned int width) {_width = width;}

        /**
        *Gets the height of a tile
        */
        unsigned int getHeight() const {return _height;}

        /**
        *Sets the height of a tile
        */
        void setHeight(unsigned int height) {_height = height;}

        /**
        *Gets the mime type of a tile
        */
        const std::string& getMimeType() const {return _mimeType;}

        /**
        *Sets the mime type of a tile
        */
        void setMimeType(const std::string& mimeType) {_mimeType = mimeType;}

        /**
        *Gets the extension of a tile
        */
        const std::string& getExtension() const {return _extension;}

        /**
        *Sets the extension of a tile
        */
        void setExtension(const std::string& extension) {_extension = extension;}

    protected:
        unsigned int _width;
        unsigned int _height;
        std::string _mimeType;
        std::string _extension;
    };

    /**
    *TMS tile set.  A collection of tiled images at a given zoom level.
    */
    class OSGEARTHUTIL_EXPORT TileSet
    {
    public:
        TileSet();

        /** dtor */
        virtual ~TileSet() { }

        /**
        *Gets the reference link for this TileSet.
        */
        const std::string& getHref() const {return _href;}

        /**
        *Sets the reference link for this TileSet.
        */
        void setHref( const std::string &href) {_href = href;}

        /**
        *Gets the units per pixel for this TileSet.
        */
        double getUnitsPerPixel() const {return _unitsPerPixel;}

        /**
        *Sets the units per pixel for this TileSet.
        */
        void setUnitsPerPixel(double unitsPerPixel) {_unitsPerPixel = unitsPerPixel;}

        /**
        *Gets the zoom level of this TileSet.
        */
        unsigned int getOrder() const {return _order;}

        /**
        *Sets the zoom level of this TileSet.
        */
        void setOrder( int order ) {_order = order;}

    protected:
        std::string _href;
        double _unitsPerPixel;
        unsigned int _order;
    };

    /**
    *A TMS tile map
    */
    class OSGEARTHUTIL_EXPORT TileMap : public osg::Referenced
    {
    public:
        TileMap();

        /** dtor */
        virtual ~TileMap() { }

        /**
        *Gets the tile map service for this TileMap
        */
        const std::string& getTileMapService() const {return _tileMapService;}

        /**
        *Sets the tile map service for this TileMap
        */
        void setTileMapService(const std::string& tileMapService) {_tileMapService = tileMapService;}

        /**
        *Gets the version of this TileMap
        */
        const std::string& getVersion() const {return _version;}

        /**
        *Sets the version of this TileMap
        */
        void setVersion(const std::string& version) {_version = version;}

        /**
        *Gets the title of this TileMap
        */
        const std::string& getTitle() const {return _title;}

        /**
        *Sets the title of this TileMap
        */
        void setTitle(const std::string& title) {_title = title;}

        /**
        *Gets the abstract of this TileMap
        */
        const std::string& getAbstract() const {return _abstract;}

        /**
        *Sets the abstract of this TileMap
        */
        void setAbstract(const std::string& value) {_abstract = value;}

        /**
        *Gets the spatial reference of this TileMap
        */
        const std::string& getSRS() const {return _srs;}

        /**
        *Sets the spatial reference of this TileMap
        */
        void setSRS(const std::string& srs) {_srs = srs;}

        /**
        *Gets the vertical spatial reference of this TileMap
        */
        const std::string& getVerticalSRS() const { return _vsrs; }

        /**
        *Sets the vertical spatial reference of this TileMap
        */
        void setVerticalSRS(const std::string& vsrs) { _vsrs = vsrs; }

        /**
        *Gets the filename that this TileMap was loaded from
        */
        const std::string& getFilename() const {return _filename;}

        /**
        *Sets the filename that this TileMap was loaded from
        */
        void setFilename(const std::string& filename) {_filename = filename;}

        /**
        *Gets the minimum zoom level that this TileMap has valid data for
        */
        unsigned int getMinLevel() const {return _minLevel;}

        /**
        *Gets the maximum level that this TileMap has valid data for
        */
        unsigned int getMaxLevel() const {return _maxLevel;}

        /**
        *Computes the minimum and maximum levels of valid data for this TileMap
        */
        void computeMinMaxLevel();

        /**
        *Computes the number of tiles at level 0 from the existing TileSets
        */
        void computeNumTiles();

        /**
        *Gets the x coordinate of the origin of this TileMap
        */
        double getOriginX() const {return _originX;}

        /**
        *Sets the x coordinate of the origin of this TileMap
        */
        void setOriginX(double x) {_originX = x;}

        /**
        *Gets the y coordinate of the origin of this TileMap
        */
        double getOriginY() const {return _originY;}

        /**
        *Sets the y coordinate of the origin of this TileMap
        */
        void setOriginY(double y) {_originY = y;}

        /**
        *Sets the origin of this TileMap
        *
        *@param x
        *       The origin's x coordinate
        *@param y
        *       The origin's y coordinate
        */
        void setOrigin(double x, double y);

        /**
        *Gets the extents of this TileMap
        *@param minX
        *       The minimum x coordinate of the extents
        *@param minY
        *       The minimum y coordinate of the extents
        *@param maxX
        *       The maximum x coordinate of the extents
        *@param maxY
        *       The maximum y coordinate of the extents
        */
        void getExtents( double &minX, double &minY, double &maxX, double &maxY) const;

        /**
        *Sets the extents of this TileMap
        *@param minX
        *       The minimum x coordinate of the extents
        *@param minY
        *       The minimum y coordinate of the extents
        *@param maxX
        *       The maximum x coordinate of the extents
        *@param maxY
        *       The maximum y coordinate of the extents
        */
        void setExtents( double minX, double minY, double maxX, double maxY);

        /**
        *Gets the number of tiles wide at lod 0
        */
        unsigned int getNumTilesWide() const { return _numTilesWide; }

        /**
        *Sets the number of tiles wide at lod 0
        */
        void setNumTilesWide(unsigned int w) { _numTilesWide = w; }

        /**
        *Gets the number of tiles high at lod 0
        */
        unsigned int getNumTilesHigh() const { return _numTilesHigh; }

        /**
        *Sets the number of tiles high at lod 0
        */
        void setNumTilesHigh(unsigned int h) {_numTilesHigh = h;}

        osgEarth::Profile::ProfileType getProfileType() const {return _profile_type;}
        void setProfileType( osgEarth::Profile::ProfileType type ) {_profile_type = type;}

        const Profile* createProfile() const;

        /** Gets the TileFormat for this TileMap */
        TileFormat& getFormat() {return _format;}
        
        /** Gets the TileFormat for this TileMap */
        const TileFormat& getFormat() const {return _format;}


        /** A list of TileSets */
        typedef std::vector<TileSet> TileSetList;

        /** Gets the TileSets for this TileMap */
        TileSetList& getTileSets() {return _tileSets;}

        /** Gets the TileSets for this TileMap */
        const TileSetList& getTileSets() const {return _tileSets;}

        DataExtentList& getDataExtents() { return _dataExtents;}
        const DataExtentList& getDataExtents() const { return _dataExtents;}

       
        /**
        *Gets a URL string that can be used to retrieve the image for the given TileKey
        *@param tileKey
        *       The TileKey to get the URL for.
        *@param invertY
        *       If false, treat tile 0,0 as the lower left tile in the the map.  If true, treat 0,0 as the top left tile in the map.
        *@returns
        *       The URL if the data intersects the TileKey.
        */
        std::string getURL(const osgEarth::TileKey& tileKey, bool invertY);

        /**
        *Determines whether or not the given TileKey intersects the TileMap
        */
        bool intersectsKey(const osgEarth::TileKey& tileKey);

        /**
        *Automatically generates a number of TileSets
        */
        void generateTileSets(unsigned int numLevels = 20);

        /**
        * Creates a TileMap corresponding to one of osgEarth's built-in profile types
        */
        static TileMap* create(
            const std::string& url,
            const Profile* profile,
            //osgEarth::Profile::ProfileType type,
            const std::string& format,
            int tile_width,
            int tile_height );

        /**
         * Creates a TileMap based on the parameters of a TileSource
         */
        static TileMap* create(
            const TileSource* tileSource,
            const Profile*    profile );

    protected:
        std::string _tileMapService;
        std::string _version;
        std::string _title;
        std::string _abstract;
        std::string _srs;
        std::string _vsrs;


        double _originX, _originY;

        double _minX, _minY, _maxX, _maxY;

        TileSetList _tileSets;

        TileFormat _format;

        std::string _filename;

        unsigned int _minLevel;
        unsigned int _maxLevel;

        unsigned int _numTilesWide;
        unsigned int _numTilesHigh;

        osgEarth::Profile::ProfileType _profile_type;        

        DataExtentList _dataExtents;
    };

    class OSGEARTHUTIL_EXPORT TileMapReaderWriter
    {
    public:
        static TileMap* read( const std::string &location, const osgDB::ReaderWriter::Options* options );
        static TileMap* read( std::istream &in );
        static TileMap* read( const Config& conf );

        static void write(const TileMap* tileMap, const std::string& location);
        static void write(const TileMap* tileMap, std::ostream& output);

    private:
        TileMapReaderWriter();
        TileMapReaderWriter(const TileMapReaderWriter &tmr);

        /** dtor */
        virtual ~TileMapReaderWriter() { }
    };

} } } // namespace osgEarth::Util::TMS

#endif //OSGEARTHUTIL_TMS_H
