/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 * Copyright 2010 Pelican Ventures, Inc.
 * 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 SEAMLESS_QSC
#define SEAMLESS_QSC 1

#include <osgEarth/Common>
#include <osgEarth/Profile>
#include <osgEarth/TileKey>
#include <osgEarth/Locators>

namespace seamless
{
/** The Quadralateralized Spherical Cube.
 *
 * The cube is layed out like a flattened cut-out. the x,y coordinates
 * range from (0,0) to (4, 3). In the equatorial region, x is 0-4 and
 * y is 1-2. North Pole is (0, 2) ->(1,3); South Pole is (0,0)->(1,1).
 *
 * Face 0 is centered on lat lon (0, 0). Faces 1-3 are the equatorial
 * faces to the East. 4 is the North Pole, 5 is the South.
 *
 * Within each face, the conversion formulas assume that coordinate
 * values range from -1 to 1.
 */

namespace qsc
{
extern bool latLonToFaceCoords(
    double lat_deg, double lon_deg, double& out_x, double& out_y, int& out_face,
    int faceHint = -1);

/**
 * Converts face coordinates into lat/long.
 */
extern bool faceCoordsToLatLon(
    double x, double y, int face,
    double& out_lat_deg, double& out_lon_deg);

/**
 * Converts cube coordinates (0,0=>4,3) to face coordinates (-1,-1=>1,1,F).
 * WARNING. If the cube coordinate lies on a face boundary, this method will
 * always return the lower-numbered face. The "extent" version of this
 * method (below) is better b/c it's unambiguous.
 */
extern bool cubeToFace(
    double& in_out_x, double& in_out_y,int& out_face);

/**
 * Converts cube coordinates (0,0=>4,3) to face coordinates (-1,-1=>1,1,F). This
 * version takes an extent, which is better than the non-extent version since it
 * can resolve face-border ambiguity.
 */
extern bool cubeToFace(
    double& in_out_xmin, double& in_out_ymin,
    double& in_out_xmax, double& in_out_ymax,
    int& out_face);

/**
 * Converts face coordinates (-1,-1=>1,1 +F) to cube coordinates (0,0=>4,3).
 */
extern bool faceToCube(
    double& in_out_x, double& in_out_y, int face);
}

/**
 * osgTerrain locator for positioning data on the terrain using a cube-face
 * coordinate system.
 */
class QscFaceLocator : public osgEarth::GeoLocator
{
public:
    QscFaceLocator(unsigned int face)
        : _face(face)
    {
    }

    // This method will generate geocentric vertex coordinates, given local tile
    // coordinates (0=>1).
    bool convertLocalToModel(const osg::Vec3d& local, osg::Vec3d& world) const;

    // This method will generate the texture coordinates for a given location on
    // the globe.
    bool convertModelToLocal(const osg::Vec3d& world, osg::Vec3d& local) const;

private:
    unsigned int _face;
};

/**
 * The QSC SRS represents a 6-face cube, each face being in unit
 * coordinates (-1,-1=>1,1).
 * The cube lays out the 4 equatorial faces in a row. The North Pole
 * face is above face 0 and the South Pole face is below face 0. This
 * results in a space measuring (0,0=>4,3).
 */
class QscSpatialReference : public osgEarth::SpatialReference
{
public:
    QscSpatialReference(void* handle);

    virtual osgEarth::GeoLocator* createLocator(
        double xmin, double ymin, double xmax, double ymax,
        bool plate_carre =false) const;

    // Qsc is a projected coordinate system.
    virtual bool isGeographic() const { return false; }
    virtual bool isProjected() const { return true; }

    // This SRS uses a WGS84 lat/long SRS under the hood for reprojection. So we need the
    // pre/post transforms to move from cube to latlong and back.
    virtual bool preTransform(double& x, double& y, void* context) const;
    virtual bool postTransform(double& x, double& y, void* context) const;

    virtual bool transformExtent(
        const SpatialReference* to_srs,
        double& in_out_xmin,
        double& in_out_ymin,
        double& in_out_xmax,
        double& in_out_ymax,
        void* context) const;

protected: // SpatialReference overrides

    void _init();

};

class QscProfile : public osgEarth::Profile
{
public:
    QscProfile();

public: // utilities

    /**
     * Gets the cube face associated with a tile key (in cube srs).
     */
    static int getFace( const osgEarth::TileKey* key );

public: // Profile

    virtual void getIntersectingTiles(
        const osgEarth::GeoExtent& extent,
        std::vector<osgEarth::TileKey>& out_intersectingKeys ) const;

private:

};
}
#endif
