/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 * Copyright 2016 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_DRIVER_GDAL_DRIVEROPTIONS
#define OSGEARTH_DRIVER_GDAL_DRIVEROPTIONS 1

#include <osgEarth/Common>
#include <osgEarth/TileSource>
#include <osgEarth/GeoCommon>
#include <osgEarth/URI>

// forward-declare GDAL type
class GDALDataset;

namespace osgEarth { namespace Drivers
{
    using namespace osgEarth;

    class GDALOptions : public TileSourceOptions // NO EXPORT; header only
    {
    public:
        class ExternalDataset : public osg::Referenced // NO EXPORT; header only
        {
        public:
            ExternalDataset() : osg::Referenced(), _dataset(NULL), _ownsDataset(true) {};
            ExternalDataset(GDALDataset* dataset, bool ownsDataset) : osg::Referenced(), _dataset(dataset), _ownsDataset(ownsDataset) {};

        protected:
            virtual ~ExternalDataset() {};

        public:
            GDALDataset* dataset() const { return _dataset; };
            void setDataset(GDALDataset* dataset) { _dataset = dataset; };

            bool ownsDataset() const { return _ownsDataset; };
            void setOwnsDataset(bool ownsDataset) { _ownsDataset = ownsDataset; };

        private:
            GDALDataset* _dataset;
            bool         _ownsDataset;
        };

    public: // properties

        optional<URI>& url() { return _url; }
        const optional<URI>& url() const { return _url; }

        /*
         * If the data source is a database (e.g., PostGIS), the connection
         * string to use to open the database table.
         */
        optional<std::string>& connection() { return _connection; }
        const optional<std::string>& connection() const { return _connection; }

        /*
         * One or more file extensions, separated by semicolons, to load when
         * url points to a folder and you are trying to load multiple files.
         */
        optional<std::string>& extensions() { return _extensions; }
        const optional<std::string>& extensions() const { return _extensions; }
        
        /**
         * Set of file extensions to ignore (opposite of extensions)
         */
        optional<std::string>& blackExtensions() { return _blackExtensions; }
        const optional<std::string>& blackExtensions() const { return _blackExtensions; }
        
        /**
         * Interpolation method to use when resampling source data; options are
         * nearest, average, and bilinear.  Only effects elevation data
         * unless interp_imagery is also set to true.
         */
        optional<ElevationInterpolation>& interpolation() { return _interpolation; }
        const optional<ElevationInterpolation>& interpolation() const { return _interpolation; }

        /**
         * Tells the driver to always generate data to this level even if it means
         * upsampling
         */
        optional<unsigned int>& maxDataLevelOverride() { return _maxDataLevelOverride;}
        const optional<unsigned int>& maxDataLevelOverride() const { return _maxDataLevelOverride;}

        /*
         * Some GDAL-supported formats support sub-datasets; use this property
         * to specify such a data source
         */
        optional<unsigned int>& subDataSet() { return _subDataSet;}
        const optional<unsigned int>& subDataSet() const { return _subDataSet;}

        /**
         * Set to true to also sample imagery using the method specified by "interpolation"
         * By default imagery is sampled using nearest sampling.  This takes advantage of
         * any built in overviews or wavelet compression in the source file but can 
         * cause artifacts on neighboring tiles.  Interpolating the imagery can look nicer
         * but will be much slower.
         */
        optional<bool>& interpolateImagery() { return _interpolateImagery;}
        const optional<bool>& interpolateImagery() const { return _interpolateImagery;}

        /**
         The "warp profile" is a way to tell the GDAL driver to keep the original SRS and geotransform of the source data
         but use a Warped VRT to make the data appear to conform to the given profile.  This is useful for merging multiple 
         files that may be in different projections using the composite driver.
        */
        optional<ProfileOptions>& warpProfile() { return _warpProfile; }
        const optional<ProfileOptions>& warpProfile() const { return _warpProfile; }

        /**
         The "external dataset" is a way to provide your own GDAL dataset to the GDAL driver.
         There are two fields :
          - dataset() : which stores your own GDAL dataset pointer
          - ownsDataset() : if set to true, the GDALTileSource "owns" the external dataset and closes it when deleted.
        */
        osg::ref_ptr<ExternalDataset>& externalDataset() { return _externalDataset; }
        const osg::ref_ptr<ExternalDataset>& externalDataset() const { return _externalDataset; }

    public: // ctors

        GDALOptions( const TileSourceOptions& options =TileSourceOptions() ) :
            TileSourceOptions( options ),
            _interpolation( INTERP_AVERAGE ),
            _interpolateImagery( false )
        {
            setDriver( "gdal" );
            fromConfig( _conf );
        }

        virtual ~GDALOptions() { }

    public:

        Config getConfig() const
        {
            Config conf = TileSourceOptions::getConfig();
            conf.set( "url", _url );
            conf.set( "connection", _connection );
            conf.set( "extensions", _extensions );
            conf.set( "black_extensions", _blackExtensions );

            if ( _interpolation.isSet() ) {
                if ( _interpolation.value() == osgEarth::INTERP_NEAREST ) conf.update( "interpolation", "nearest" );
                else if ( _interpolation.value() == osgEarth::INTERP_AVERAGE ) conf.update( "interpolation", "average" );
                else if ( _interpolation.value() == osgEarth::INTERP_BILINEAR ) conf.update( "interpolation", "bilinear" );
            }

            conf.set( "max_data_level_override", _maxDataLevelOverride);
            conf.set( "subdataset", _subDataSet);

            conf.set( "interp_imagery", _interpolateImagery);

            conf.setObj( "warp_profile", _warpProfile );

            conf.updateNonSerializable( "GDALOptions::ExternalDataset", _externalDataset.get() );

            return conf;
        }

        void mergeConfig( const Config& conf ) {
            TileSourceOptions::mergeConfig( conf );
            fromConfig( conf );
        }

        void fromConfig( const Config& conf ) {
            conf.getIfSet( "url", _url );
            conf.getIfSet( "connection", _connection );
            conf.getIfSet( "extensions", _extensions );
            conf.getIfSet( "black_extensions", _blackExtensions );
            std::string in = conf.value( "interpolation" );
            if ( in == "nearest" ) _interpolation = osgEarth::INTERP_NEAREST;
            else if ( in == "average" ) _interpolation = osgEarth::INTERP_AVERAGE;
            else if ( in == "bilinear" ) _interpolation = osgEarth::INTERP_BILINEAR;
            conf.getIfSet( "max_data_level_override", _maxDataLevelOverride);
            conf.getIfSet( "subdataset", _subDataSet);

            conf.getIfSet("interp_imagery", _interpolateImagery);

            conf.getObjIfSet( "warp_profile", _warpProfile );

            _externalDataset = conf.getNonSerializable<ExternalDataset>( "GDALOptions::ExternalDataset" );
        }

        optional<URI>                    _url;
        optional<std::string>            _connection;
        optional<std::string>            _extensions;
        optional<std::string>            _blackExtensions;
        optional<ElevationInterpolation> _interpolation;
        optional<bool>                   _interpolateImagery;
        optional<unsigned int>           _maxDataLevelOverride;
        optional<unsigned int>           _subDataSet;
        optional<ProfileOptions>         _warpProfile;
        osg::ref_ptr<ExternalDataset>    _externalDataset;
    };

} } // namespace osgEarth::Drivers

#endif // OSGEARTH_DRIVER_GDAL_DRIVEROPTIONS
