/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
 * Copyright 2008-2010 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 OSGEARTHSYMBOLOGY_SYMBOLIZER_H
#define OSGEARTHSYMBOLOGY_SYMBOLIZER_H 1

#include <osgEarthSymbology/Common>
#include <osgEarthSymbology/Content>
#include <osgEarthSymbology/Style>
#include <osgEarth/Revisioning>
#include <osg/Group>

namespace osgEarth { namespace Symbology
{
    /**
     * A shared context that is available to each symbolizer when it compiles
     * data. It may contain information about a terrain, srs, or other global
     * information.
     */
    // NOTE: header-only; no export macro
    class SymbolizerContext : public osg::Referenced
    {
    };

    /**
     * A symbolizer state object template. In order to share a single Symbolizer instance across
     * multiple SymbolicNodes, each SymbolicNode maintains a State object that is passes along
     * to the Symbolizer during update. The State holds the Style and Content objects that the
     * Symbolizer will render, and it tracks whether each SymbolicNode is up to date with the
     * latest revisions of the Style and Content.
     *
     * The State object gets passed to Symbolizer::update. You can customize it by supplying your
     * own Content type (which must inherit from Symbology::Content) and you can extend it to
     * store custom stateful variables and objects.
     *
     * For now, the State also includes the SymbolizerContext object, primarily because there 
     * was no other obvious place to house it. We'll see if this sticks.
     */
    template<typename CONTENT_TYPE>
    class State : public osg::Object // header-only; no export
    {
    public:
        /**
         * Sets a new Content object and automatically marks the State as dirty.
         */
        void setContent( const CONTENT_TYPE* value ) {
            _content = value;
            if (_content.valid())
            {
                _content->sync(_contentRevision);
            }
            --_contentRevision; // force a sync
        }

        /**
         * The Content associated with this state. This is const-only 
         * becase a Symbolizer should not be allowed to alter the Content.
         */
        const CONTENT_TYPE* getContent() const {
            return _content.get(); }

        /**
         * Sets a new Content object and automatically marks the State as dirty.
         */
        void setStyle( const Style* style ) {
            _style = style;
            if (_style.valid())
            {
              _style->sync(_styleRevision);
            }
            --_styleRevision; // force a sync
        }

        /**
         * Accesses the Style associated with this State object. This is const-only
         * because a Symbolizer should not be allowed to alter the Style.
         */
        const Style* getStyle() const {
            return _style.get(); }


        /** Sets the common symbolization context. */
        void setContext( SymbolizerContext* value ) {
            _context = value;
        }

        /** Accesses the shared symbolizer context. */
        SymbolizerContext* getContext() const {
            return _context.get();
        }

    public:
        /** Constructs a new symbolizer state object. */
        State() { }

        /** Copy constructor */
        State( const State& rhs, const osg::CopyOp& op =osg::CopyOp::DEEP_COPY_ALL ) :
            _style( rhs._style.get() ),
            _content( rhs._content.get() ),
            _context( rhs._context.get() )
        {
            // note: leave revisions uncopied intentionally
        }

        /** OSG object methods (clone, etc) */
        META_Object(osgEarthSymbology,State);

        /** Returns true if the Style has changed since the last time this State was compiled. */
        bool styleDirty() const {
            return _style.valid() && _style->outOfSyncWith( _styleRevision );
        }

        /** Returns true if the Content has changed since the last time this State was compiled. */
        bool contentDirty() const {
            return _content.valid() && _content->outOfSyncWith( _contentRevision );
        }

    public:
        Revision _styleRevision;
        Revision _contentRevision;

    protected:
        osg::ref_ptr<const Style>        _style;
        osg::ref_ptr<const CONTENT_TYPE> _content;
        osg::ref_ptr<SymbolizerContext>  _context;
    };

    /**
     * A Symbolizer compiles parametric content into an OSG subgraph. It uses a Style
     * to affect how that compilation takes place. Both the Content and the Style information
     * are communicated to the Symbolizer via a State object in the update() call.
     */
    template<typename STATE_TYPE>
    class Symbolizer : public osg::Referenced // header-only
    {
    public:
        /**
         * Compiles content into an OSG subgraph. A SymbolicNode to which this Symbolizer is
         * assigned calls this method (during the UPDATE traversal) whenever the State is
         * determined to be "dirty". The implementation should attach the compiled subgraph
         * to the "attachPoint" group.
         */
        virtual bool compile(
            STATE_TYPE* state,
            osg::Group* attachPoint ) =0;

        /**
         * If true, the SymbolicNode will call update() every frame regardless of whether
         * the State is dirty.
         */
        bool getAlwaysCompile() const { 
            return _alwaysCompile; }

    protected:
        Symbolizer() : _alwaysCompile(false) { }

        void setAlwaysCompile( bool value ) {
            _alwaysCompile = value; }

    private:
        bool _alwaysCompile;
    };

} } // namespace osgEarth::Symbology

#endif // OSGEARTHSYMBOLOGY_SYMBOLIZER_H
