/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library 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
 * OpenSceneGraph Public License for more details.
 *
 * ViewDependentShadow codes Copyright (C) 2008 Wojciech Lewandowski
 * Thanks to to my company http://www.ai.com.pl for allowing me free this work.
*/

#ifndef OSGSHADOW_STANDARDSHADOWMAP
#define OSGSHADOW_STANDARDSHADOWMAP 1

#include <osgShadow/DebugShadowMap>

namespace osgShadow {

class OSGSHADOW_EXPORT StandardShadowMap : public DebugShadowMap
{
    public :
        /** Convenient typedef used in definition of ViewData struct and methods */
        typedef StandardShadowMap ThisClass;
        /** Convenient typedef used in definition of ViewData struct and methods */
        typedef DebugShadowMap    BaseClass;

        /** Classic OSG constructor */
        StandardShadowMap();

        /** Classic OSG cloning constructor */
        StandardShadowMap(const StandardShadowMap& ssm,
                          const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);

        /** Declaration of standard OSG object methods */
        META_Object( osgShadow, StandardShadowMap );

        void setBaseTextureUnit( unsigned int unit )
            { _baseTextureUnit = unit; dirty(); }

        unsigned int getBaseTextureUnit( void ) const
            { return _baseTextureUnit; }

        void setShadowTextureUnit( unsigned int unit )
            { _shadowTextureUnit = unit; dirty(); }

        unsigned int getShadowTextureUnit( void ) const
            { return _shadowTextureUnit; }

        // Texture Indices are changed by search and replace on shader source
        // Carefully order these calls when changing both base and shadow indices
        // In worst case when intend to swap indices
        // one will have to call these  methods more than once
        // with one extra pass to change index to unused value to avoid
        // unwanted superfluous replace:
        //
        // Example: Imagine we want to swap base(0) and shadow(1) indices:
        // We have to do an extra step to make sure both do not end up as 1
        //
        // // initially change base to something else than 1
        // setBaseTextureCoordIndex( 100 );
        // // now search and replace all gl_TexCord[1] to gl_TexCord[0]
        // setShadowTextureCoordIndex( 0 );
        // // finally change base from 100 to 0
        // setBaseTextureCoordIndex( 1 );

        void setBaseTextureCoordIndex( unsigned int index )
            { updateTextureCoordIndices( _baseTextureCoordIndex, index );
              _baseTextureCoordIndex = index; }

        unsigned int getBaseTextureCoordIndex( void ) const
            { return _baseTextureCoordIndex; }

        // Texture Indices are changed by search and replace on shader source
        // Look at the comment above setBaseTextureCoordIndex

        void setShadowTextureCoordIndex( unsigned int index )
            { updateTextureCoordIndices( _shadowTextureCoordIndex, index );
              _shadowTextureCoordIndex = index; }

        unsigned int getShadowTextureCoordIndex( void ) const
            { return _shadowTextureCoordIndex; }

        void setTextureSize( const osg::Vec2s& textureSize )
            { _textureSize = textureSize; dirty(); }

        const osg::Vec2s& getTextureSize() const
            { return _textureSize; }

        void setLight( osg::Light* light )
            { _light = light; }

        osg::Light* getLight( void )
            { return _light.get(); }

        const osg::Light* getLight( void ) const
            { return _light.get(); }

        osg::Shader * getShadowVertexShader()
            { return _shadowVertexShader.get(); }

        osg::Shader * getShadowFragmentShader()
            { return _shadowFragmentShader.get(); }

        osg::Shader * getMainVertexShader( )
            { return _mainVertexShader.get(); }

        osg::Shader * getMainFragmentShader( )
            { return _mainFragmentShader.get(); }

        void setShadowVertexShader( osg::Shader * shader )
            { _shadowVertexShader = shader; }

        void setShadowFragmentShader( osg::Shader * shader )
            { _shadowFragmentShader = shader; }

        void setMainVertexShader( osg::Shader * shader )
            { _mainVertexShader = shader; }

        void setMainFragmentShader( osg::Shader * shader )
            { _mainFragmentShader = shader; }


        /** Resize any per context GLObject buffers to specified size. */
        virtual void resizeGLObjectBuffers(unsigned int maxSize);

        /** If State is non-zero, this function releases any associated OpenGL objects for
           * the specified graphics context. Otherwise, releases OpenGL objects
           * for all graphics contexts. */
        virtual void releaseGLObjects(osg::State* = 0) const;

protected:
        /** Classic protected OSG destructor */
        virtual ~StandardShadowMap(void);

        virtual void updateTextureCoordIndices
                ( unsigned int baseTexCoordIndex, unsigned int shadowTexCoordIndex );

        virtual void searchAndReplaceShaderSource
                ( osg::Shader*, std::string fromString, std::string toString );

        osg::ref_ptr< osg::Shader >   _mainVertexShader;
        osg::ref_ptr< osg::Shader >   _mainFragmentShader;
        osg::ref_ptr< osg::Shader >   _shadowVertexShader;
        osg::ref_ptr< osg::Shader >   _shadowFragmentShader;

        osg::ref_ptr< osg::Light >    _light;
        float                         _polygonOffsetFactor;
        float                         _polygonOffsetUnits;
        osg::Vec2s                      _textureSize;
        unsigned int                  _baseTextureUnit;
        unsigned int                  _shadowTextureUnit;
        unsigned int                  _baseTextureCoordIndex;
        unsigned int                  _shadowTextureCoordIndex;

        struct OSGSHADOW_EXPORT ViewData: public BaseClass::ViewData
        {
            osg::ref_ptr< osg::Light > *  _lightPtr;
            unsigned int               *  _baseTextureUnitPtr;
            unsigned int               *  _shadowTextureUnitPtr;

            // ShadowMap texture is defined by base DebugShadowMap
            // osg::ref_ptr<osg::Texture2D>  _texture;

            // ShadowMap camera is defined by base DebugShadowMap
            // osg::ref_ptr<osg::Camera>     _camera;

            osg::ref_ptr<osg::TexGen>     _texgen;
            osg::ref_ptr<osg::StateSet>   _stateset;

            virtual void init( ThisClass * st, osgUtil::CullVisitor * cv );

            virtual void cull( );

            virtual void aimShadowCastingCamera(
                const osg::BoundingSphere &bounds,
                const osg::Light *light,
                const osg::Vec4 &worldLightPos,
                const osg::Vec3 &worldLightDir,
                const osg::Vec3 &worldLightUp = osg::Vec3(0,1,0) );

            virtual void cullShadowReceivingScene( );

            virtual void cullShadowCastingScene( );

            virtual void addShadowReceivingTexGen( );

            virtual const osg::Light* selectLight( osg::Vec4 &viewLightPos,
                osg::Vec3 &viewLightDir );

            virtual void aimShadowCastingCamera( const osg::Light *light,
                const osg::Vec4 &worldLightPos,
                const osg::Vec3 &worldLightDir,
                const osg::Vec3 &worldLightUp
                = osg::Vec3(0,1,0) );

            virtual void resizeGLObjectBuffers(unsigned int maxSize);
            virtual void releaseGLObjects(osg::State* = 0) const;
        };

        friend struct ViewData;

        META_ViewDependentShadowTechniqueData( ThisClass, ThisClass::ViewData )
};

} // namespace osgShadow

#endif