/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
 * Copyright 2019 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/>
 */

#include <osg/Referenced>
#include <osg/Vec2d>

#include <osgEarth/Profile>

#include <string>
#include <vector>


/**
 * Classes for utilizing the JPL OnEarth tiled access patterns described at http://onearth.jpl.nasa.gov/tiled.html
 * 
 * Another good discussion on how the format works is described here: http://lists.eogeo.org/pipermail/tiling/2006-March/000017.html
 *
 * The TileService specifies a series of predefined WMS access patterns that will result in very fast access.  The tiling is quadtree based
 * with the lowest level of detail generally consisting of 4 256x256 degree tiles with the upper left corner at -180,90.
 */

/**
 * TilePattern corresponds to the 
 */
class TilePattern
{
public:
    TilePattern(const std::string &pattern);

    const std::string& getPattern() const {return _pattern;}

    const double& getTileWidth() const { return _tileWidth; }
    const double& getTileHeight() const { return _tileHeight; }

    void getTileBounds(const int &x, const int &y, double &minX, double &minY, double &maxX, double &maxY);
    std::string getRequestString(const int &x, const int &y);

    const std::string& getLayers() {return _layers;}
    const std::string& getFormat() {return _format;}
    const std::string& getStyles() {return _styles;}
    const std::string& getSRS() {return _srs;}

    const osg::Vec2d& getTopLeftMin() {return _topLeftMin;}
    const osg::Vec2d& getTopLeftMax() {return _topLeftMax;}

    
    const int& getImageWidth() {return _imageWidth;}
    const int& getImageHeight() {return _imageHeight;}

    const std::string& getPrototype() {return _prototype;}

    const osg::Vec2d &getDataMin() {return _dataMin;}
    void setDataMin(const osg::Vec2d &min) {_dataMin = min;}

    const osg::Vec2d &getDataMax() {return _dataMax;}
    void setDataMax(const osg::Vec2d &max) {_dataMax = max;}

private:
    friend class TileServiceReader;

    void init();

    std::string _layers;
    std::string _format;
    std::string _styles;
    std::string _srs;
    int _imageWidth;
    int _imageHeight;

    osg::Vec2d _topLeftMin;
    osg::Vec2d _topLeftMax;

    double _tileWidth;
    double _tileHeight;

    std::string _prototype;
    std::string _pattern;

    osg::Vec2d _dataMin;
    osg::Vec2d _dataMax;
};

class TileService : public osg::Referenced
{
public:
    TileService();

    /** A list of TilePatterns */
    typedef std::vector<TilePattern> TilePatternList;

    /** Gets the TilePatterns defined in this TileService */
    TilePatternList& getPatterns() {return _patterns;}

    /**
     *Gets the name of this TileService
     */
    const std::string& getName() {return _name;}

    /**
     *Sets the name of this TileService
     */
    void setName(const std::string& name) {_name = name;}

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

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

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

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

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

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

    /**
     *Gets the access constraints of this TileService
     */
    const std::string& getAccessConstraints() {return _accessConstraints;}

    /**
     *Sets the access constraints of this TileService
     */
    void setAccessConstraints(const std::string& accessConstraints) {_accessConstraints = accessConstraints;}

    /**
     *Gets the lower left corner of the data bounding box.
     */
    const osg::Vec2d &getDataMin() {return _dataMin;}

    /**
     *Sets the lower left corner of the data bounding box 
     */
    void setDataMin(const osg::Vec2d &min) {_dataMin = min;}

    /**
     *Gets the upper right corner of the data bounding box.
     */
    const osg::Vec2d &getDataMax() {return _dataMax;}

    /**
    *Sets the upper right corner of the data bounding box.
    */
    void setDataMax(const osg::Vec2d &max) {_dataMax = max;}


    /**
     *Gets the TilePatterns from this TileService with the matching parameters.
     *@param layers
     *       The WMS layers
     *@param format
     *       The WMS format
     *@param styles
     *       The WMS styles
     *@param srs
     *       The spatial reference
     *@param imageWidth
     *       The image width
     *@param imageHeight
     *       The image height
     *@param patterns
     *       The TilePatternList to populate with the matching TilePatterns.
     */
    void getMatchingPatterns(const std::string &layers, const std::string &format,
                             const std::string &styles, const std::string &srs,
                             unsigned int imageWidth, unsigned int imageHeight,
                             TilePatternList &out_patterns);

    /**
     *Given a list of patterns with matching parameters, compute a Profile.
     */
    const osgEarth::Profile* createProfile(TilePatternList &patterns);

private:
    std::string _name;
    std::string _title;
    std::string _abstract;
    std::string _version;
    std::string _accessConstraints;

    osg::Vec2d _dataMin;
    osg::Vec2d _dataMax;

    std::vector<TilePattern> _patterns;
};


/*
 * Reads a TileService from a URL or file
 */
class TileServiceReader
{
public:
    static TileService* read( const std::string &location, const osgDB::ReaderWriter::Options* options);
    static TileService* read( std::istream &in );
private:
    TileServiceReader(){}
    TileServiceReader(const TileServiceReader &tsr){}
};
