/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
* Copyright 2020 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_PROCEDURAL_BIOME
#define OSGEARTH_PROCEDURAL_BIOME 1

#include "Export"
#include <osgEarth/Config>
#include <osgEarth/URI>
#include <osgEarth/Units>
#include <osgEarth/GeoData>
#include <osgEarth/LandCover>
#include <osgEarth/GLUtils>
#include <osg/BoundingBox>

namespace osgEarth
{
    namespace Procedural
    {
        using namespace osgEarth;

        /**
         * Individual artwork asset reference within a biome
         */
        class OSGEARTHPROCEDURAL_EXPORT ModelAsset
        {
        public:
            OE_OPTION(std::string, name);
            OE_OPTION(URI, modelURI);
            OE_OPTION(URI, sideBillboardURI);
            OE_OPTION(URI, topBillboardURI);
            OE_OPTION(float, width);
            OE_OPTION(float, height);
            OE_OPTION(float, sizeVariation);
            OE_OPTION(float, selectionWeight);

            ModelAsset() { }
            ModelAsset(const Config& conf);

            Config getConfig() const;

            //! Get Config from which this asset was originally loaded,
            //! which will give the caller access to user-defined values
            //! if they exist.
            const Config& getSourceConfig() const {
                return _sourceConfig;
            }

        private:
            Config _sourceConfig;
        };

        /**
         * Individual artwork asset reference within a biome
         */
        class OSGEARTHPROCEDURAL_EXPORT LifeMapTextureAsset
        {
        public:
            OE_OPTION(std::string, name);
            OE_OPTION(URI, uri);
            OE_OPTION(float, width);
            OE_OPTION(float, height);
            OE_OPTION(int, code);

        public:
            LifeMapTextureAsset() { }
            LifeMapTextureAsset(const Config& conf);
            Config getConfig() const;
        };

        /**
         * Contains all assets.
         */
        class OSGEARTHPROCEDURAL_EXPORT AssetCatalog
        {
        public:
            AssetCatalog() { }
            AssetCatalog(const Config& conf);

            //! Model asset associated with given name
            //! @param name Name of model asset to return
            //! @return ModelAsset or nullptr if no match
            const ModelAsset* getModel(const std::string& name) const;

            //! Collection of ground textures in the lifemap matrix
            const std::vector<LifeMapTextureAsset>& getLifeMapTextures() const;

            //! Collection of special ground textures
            const std::vector<LifeMapTextureAsset>& getSpecialTextures() const;

            //! Is the catalog empty?
            bool empty() const;

        public:
            Config getConfig() const;

        protected:
            std::vector<LifeMapTextureAsset> _textures;
            std::vector<LifeMapTextureAsset> _specialTextures;
            std::unordered_map<std::string, ModelAsset> _models;
        };

        /**
         * Groups together some ModelAssets for use in a single
         * ground cover layer
         */
        class OSGEARTHPROCEDURAL_EXPORT ModelCategory
        {
        public:
            ModelCategory() { }
            ModelCategory(const Config& conf, AssetCatalog* assets);

            struct Member
            {
                const ModelAsset* asset;
                float weight;
                float fill;
            };

            OE_OPTION(std::string, name);
            OE_OPTION_VECTOR(Member, members);

            Config getConfig() const;
        };

        /** 
         * A subset of graphical assets that will be used
         * in a particular geographic region.
         */
        class OSGEARTHPROCEDURAL_EXPORT Biome
        {
        public:
            Biome() { }
            Biome(const Config& conf, AssetCatalog* assets);
            Config getConfig() const;

            OE_OPTION(std::string, name);
            OE_OPTION(int, id);
            OE_OPTION_VECTOR(ModelCategory, modelCategories);

            //! Find a named model category by name
            //! @param name Name of object to find
            //! @return model category, or nullptr if no match
            const ModelCategory* getModelCategory(const std::string& name) const;
        };


        /**
         * Mappings from land use classification types to lifemap parameters.
         */
        class OSGEARTHPROCEDURAL_EXPORT LifeMapValue
        {
        public:
            LifeMapValue() { }
            LifeMapValue(const Config& conf);
            Config getConfig() const;

            OE_OPTION(std::string, id);
            OE_OPTION(float, dense);
            OE_OPTION(float, lush);
            OE_OPTION(float, rugged);
            OE_OPTION(std::string, special);
        };


        /**
         * Mappings from IDs to lifemap parameters.
         */
        class OSGEARTHPROCEDURAL_EXPORT LifeMapValueTable
        {
        public:
            LifeMapValueTable() { }
            LifeMapValueTable(const Config& conf);
            Config getConfig() const;

            OE_OPTION_VECTOR(LifeMapValue, values);

            const LifeMapValue* getValue(const std::string& id) const;

        private:
            std::unordered_map<std::string, LifeMapValue> _lut;
        };

        /**
         * Defines all biomes, assets, and land use mappings used
         * by the procedural system.
         */
        class OSGEARTHPROCEDURAL_EXPORT BiomeCatalog
        {
        public:
            BiomeCatalog() { }
            BiomeCatalog(const Config& conf);

            //! Fetch a biome by its unique ID
            //! @return nullptr if no match
            const Biome* getBiome(int id) const;

            //! Vector of all biomes in the catalog
            std::vector<const Biome*> getBiomes() const;

            //! Asset catalog
            const AssetCatalog& getAssets() const;

            //! Table that maps land use classes to lifemap values.
            const LifeMapValueTable* getLandUseTable() const;

            //! Talbe that maps land cover classes to lifemap values.
            const LifeMapValueTable* getLandCoverTable() const;

        public:
            Config getConfig() const;

            AssetCatalog _assets;            
            std::shared_ptr<LifeMapValueTable> _landUseTable;
            std::shared_ptr<LifeMapValueTable> _landCoverTable;
            std::unordered_map<int, Biome> _biomes;
        };

} } // namespace osgEarth::Procedural

#endif
