/*****************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   State-sorting.
 *//*
 * LEGAL:   COPYRIGHT (C) 2009 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
 *****************************************************************************/

#ifndef GRAPH_NODE_SORT_HH
#define GRAPH_NODE_SORT_HH 1

#ifndef COMPILE_HW_LIGHT
#define COMPILE_HW_LIGHT 1
#endif

#include "base/funcs_stl.hh"
#include "shader/module.hh"
#include "shader/shader_name.hh"
using namespace shader;

namespace graph {

////////////////////////////////////////////////////////////////////////////////
/// @brief NodeSort defines major graphical states for top-level state-sorting.
///
/// NodeSort means which sort of node (how to sort a node).
/// NodeSort is an arg passed to SceneGraph::Attach( Node, NodeSort ).
/// Internally, SceneGraph maps NodeSort to a group node.
///
/// For example, 3D models sharing the same shader can be attached by:
/// NodeSort nodeSort( ShaderName("metallic") );
/// GET_SCENE_GRAPH().AttachNode( ship1, nodeSort );
/// GET_SCENE_GRAPH().AttachNode( ship2, nodeSort );
///
/// The Attribs enums represent common graphical states.
/// By passing a specific StateSet to the NodeSort ctor,
/// other graphical states are supported.
///
/// Atribs::TRANSPARENT is suitable for most transparent objects
/// but some will need Attribs::NO_DEPTH_TEST too.
///
class NodeSort
{
COPYABLE(NodeSort)
friend class SceneGraph;
public:
    /// @brief OpenGL modes.
    ///
    struct Attribs
    {
        typedef uint Int;
        CLASS_CONST Int DEFAULT             = 0;
        CLASS_CONST Int NO_CULL_FACE        = (1 << 0);
        CLASS_CONST Int CULL_FACE_CLOCKWISE = (1 << 1);  // do back-face culling clockwise
        CLASS_CONST Int NO_SHADER           = (1 << 2);  // no shader/lighting
        CLASS_CONST Int NO_FOG              = (1 << 3);
        CLASS_CONST Int NO_DEPTH_TEST       = (1 << 4);
        CLASS_CONST Int BLEND               = (1 << 5);
        CLASS_CONST Int SHADOW_CASTER       = (1 << 6);
        CLASS_CONST Int SHADOW_RECEIVER     = (1 << 7);
        CLASS_CONST Int TRANSPARENT         = BLEND | NO_SHADER | NO_FOG;

        explicit Attribs( const Int val = DEFAULT ) : mVal(val) { }

        Int mVal;
    };

public:
    /// @brief OSG node optimizations.
    /// Part of NodeSort but doesn't affect state-sorting.
    ///
    struct Optimizations
    {
        typedef uint Int;

        explicit Optimizations( const Int val = msDefault ) : mVal(val) { }

        CLASS_VAR Int   msDefault;
        Int             mVal;
    };

public:
    NodeSort( const Attribs         attribs = Attribs(),
              const ShaderName&     shaderName = ShaderName(),
              RefPtr<osg::StateSet> stateSet = msDefaultStateSet,
              const Optimizations   optimizations = Optimizations() )
    :   mAttribs(attribs),
        mShaderName(shaderName),
        mStateSet(stateSet),
        mOptimizations(optimizations)
    { SET_TYPESIG(this,TYPESIG_NODE_SORT); }

    NodeSort( const ShaderName& shaderName, const Attribs attribs = Attribs() )
    :   mAttribs(attribs),
        mShaderName(shaderName),
        mStateSet(msDefaultStateSet),
        mOptimizations()
    { SET_TYPESIG(this,TYPESIG_NODE_SORT); }

    NodeSort( const Optimizations& optimizations, const Attribs attribs = Attribs() )
    :   mAttribs(attribs),
        mShaderName(),
        mStateSet(msDefaultStateSet),
        mOptimizations(optimizations)
    { SET_TYPESIG(this,TYPESIG_NODE_SORT); }

    NodeSort( const Attribs         attribs,
              RefPtr<osg::StateSet> stateSet )
    :   mAttribs(attribs),
        mShaderName(),
        mStateSet(stateSet),
        mOptimizations()
    { SET_TYPESIG(this,TYPESIG_NODE_SORT); }

    ~NodeSort()
    { INVALIDATE_TYPESIG(this,TYPESIG_NODE_SORT); }

    RefPtr<GroupNode>       CreateGroupNode( void ) const;
    bool                    IfDefaultShader( void ) const { CHECK_TYPESIG(this,TYPESIG_NODE_SORT); return mShaderName.IfDefaultShader(); }
    Attribs                 GetAttribs( void ) const { CHECK_TYPESIG(this,TYPESIG_NODE_SORT); return mAttribs; }
    const ShaderName&       GetShaderName( void ) const { CHECK_TYPESIG(this,TYPESIG_NODE_SORT); return mShaderName; }
    RefPtr<osg::StateSet>   GetStateSet( void ) const { CHECK_TYPESIG(this,TYPESIG_NODE_SORT); return mStateSet; }
    Optimizations           GetOptimizations( void ) const { CHECK_TYPESIG(this,TYPESIG_NODE_SORT); return mOptimizations; }

    friend bool operator<( const NodeSort& a, const NodeSort& b )
    {
    CHECK_TYPESIG(&a,TYPESIG_NODE_SORT);
    CHECK_TYPESIG(&b,TYPESIG_NODE_SORT);
        RETURN_LT_TRUE_GT_FALSE( a.mAttribs.mVal,       b.mAttribs.mVal );
        RETURN_LT_TRUE_GT_FALSE( a.mShaderName,         b.mShaderName );
        RETURN_LT_TRUE_GT_FALSE( a.mStateSet.get(),     b.mStateSet.get() );
      //RETURN_LT_TRUE_GT_FALSE( a.mOptimizations.mVal, b.mOptimizations.mVal );  // N/A to state-sorting
        return false;  // equal
    }

#if COMPILE_HW_LIGHT
private:
    RefPtr<GroupNode>   NewLightGroup( void ) const;
#endif

private:
    CLASS_VAR RefPtr<osg::StateSet> msDefaultStateSet;
    const Attribs                   mAttribs;               ///< major graphical states
    const ShaderName                mShaderName;            ///< name of shader
    RefPtr<osg::StateSet>           mStateSet;              ///< user-defined state
    const Optimizations             mOptimizations;         ///< OSG node optimizer
public:
    DECLARE_TYPESIG( TYPESIG_NODE_SORT )
};

} // namespace graph

#endif // GRAPH_NODE_SORT_HH
