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

#ifndef OSGEARTH_FEATURES_SESSION_H
#define OSGEARTH_FEATURES_SESSION_H 1

#include <osgEarthFeatures/Common>
#include <osgEarthFeatures/ScriptEngine>
#include <osgEarthSymbology/StyleSheet>
#include <osgEarth/StateSetCache>
#include <osgEarth/ThreadingUtils>
#include <osgEarth/MapInfo>
#include <osgEarth/MapFrame>
#include <osgEarth/Map>

namespace osgEarth { namespace Features
{
    using namespace osgEarth;
    using namespace osgEarth::Symbology;

    class FeatureSource;

    /**
     * Session is a state object that exists throughout the life of one or more related
     * feature compilations.
     *
     * A Session holds shared, re-usable data elements that can be accessed through a 
     * FilterContext.
     *
     * Whereas a FilterContext exists thoughout the life of a single compilation, a Session
     * exists one level above this and governs any number of "related" compilations
     * (e.g., the compilation of many grid cells comprising a single feature layer).
     *
     * Session implements the URIResolver interface to resolve relative URIs.
     */
    class OSGEARTHFEATURES_EXPORT Session : public osg::Referenced
    {
    public:
        /**
         * Constructs a new Session that is tied to a map
         */
        Session( const Map* map, StyleSheet* styles =0L, FeatureSource* source =0L, const osgDB::Options* dbOptions =0L );
        virtual ~Session();

        /**
         * URI Context for relative path resolution.
         */
        void setURIContext( const URIContext& value ) { _uriContext = value; }
        const URIContext& uriContext() const { return _uriContext; }

        /**
         * Gets the underlying map (frame) interface in this session
         */
        MapFrame createMapFrame( Map::ModelParts parts =Map::TERRAIN_LAYERS ) const;

        /**
         * Gets the map information backing up this session.
         */
        const MapInfo& getMapInfo() const { return _mapInfo; }

        /** Gets the SRS of the map behind this session */
        const SpatialReference* getMapSRS() const { return _mapInfo.getSRS(); }

        /**
         * Gets the map related to this session. Be carefull, this is held by an osg::observer_ptr and may be NULL
         */
        const Map* getMap() const { return _map.get(); }

        /** The style sheet governing this session. */
        void setStyles( StyleSheet* value );
        StyleSheet* styles() const { return _styles.get(); }

        /** Gets the current feature source */
        FeatureSource* getFeatureSource() const;

        /** The I/O options for operations within this session */
        const osgDB::Options* getDBOptions() const;

    public:
        template<typename T>
        struct CreateFunctor {
            virtual T* operator()() const =0;
        };

        /**
         * Stores an object in the shared Session cache.
         *
         * WARNING! Don't store things like nodes in here unless you plan
         * to clone them. This is a multi-threaded store.
         *
         * Returns the object written, OR the already-existing object if overwrite = false
         * and the key was already taken.
         */
        template<typename T>
        T* putObject( const std::string& key, T* object, bool overwrite =true ) {
            //Threading::ScopedWriteLock lock( _objMapMutex );
            Threading::ScopedMutexLock lock( _objMapMutex );
            ObjectMap::iterator i = _objMap.find(key);
            if ( i != _objMap.end() && !overwrite )
                return dynamic_cast<T*>(i->second.get());
            _objMap[key] = object;
            return object;
        }

        /**
         * Gets an object from the shared Session cache.
         * (returns a ref_ptr so as not to lose its ref in a multi-threaded app)
         */
        template<typename T>
        osg::ref_ptr<T> getObject( const std::string& key ) {
            //Threading::ScopedReadLock lock( _objMapMutex );
            Threading::ScopedMutexLock lock( _objMapMutex );
            ObjectMap::const_iterator i = _objMap.find(key);
            return i != _objMap.end() ? dynamic_cast<T*>( i->second.get() ) : 0L;
        }

        template<typename T>
        bool getOrCreateObject(const std::string& key, osg::ref_ptr<T>& output, const CreateFunctor<T>& create) {
            Threading::ScopedMutexLock lock( _objMapMutex );
            ObjectMap::const_iterator i = _objMap.find(key);
            if ( i != _objMap.end() ) {
                output = dynamic_cast<T*>( i->second.get() );
                return true;
            }
            else {
                T* object = create();
                if ( object ) {
                    _objMap[key] = object;
                    output = object;
                    return true;
                }
                else {
                    return false;
                }
            }
        }

        void removeObject( const std::string& key );

    public:
        /**
         * The cache for optimizing stateset sharing within a session
         */
        StateSetCache* getStateSetCache() { return _stateSetCache.get(); }

    public:
      ScriptEngine* getScriptEngine() const;

    private:
        typedef std::map<std::string, osg::ref_ptr<osg::Referenced> > ObjectMap;
        ObjectMap                    _objMap;
        //Threading::ReadWriteMutex    _objMapMutex;
        Threading::Mutex             _objMapMutex;

        URIContext                         _uriContext;
        osg::observer_ptr<const Map>       _map;
        MapInfo                            _mapInfo;
        osg::ref_ptr<StyleSheet>           _styles;
        osg::ref_ptr<const osgDB::Options> _dbOptions;
        osg::ref_ptr<ScriptEngine>         _styleScriptEngine;
        osg::ref_ptr<FeatureSource>        _featureSource;
        osg::ref_ptr<StateSetCache>        _stateSetCache;
    };

} }

#endif // OSGEARTH_FEATURES_SESSION_H
