/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2010 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.
*/
// Written by Wang Rui, (C) 2010

#ifndef OSGDB__SERIALIZER
#define OSGDB__SERIALIZER

#include <osg/ref_ptr>
#include <osg/Notify>
#include <osg/Object>
#include <osgDB/InputStream>
#include <osgDB/OutputStream>

#include <string>
#include <sstream>
#include <limits.h>

namespace osgDB
{

#ifndef OBJECT_CAST
    #define OBJECT_CAST static_cast
#endif

class IntLookup
{
public:
    typedef int Value;
    typedef std::map<std::string, Value> StringToValue;
    typedef std::map<Value, std::string> ValueToString;

    IntLookup() {}
    unsigned int size() const { return static_cast<unsigned int>(_stringToValue.size()); }

    void add( const char* str, Value value )
    {
        if ( _valueToString.find(value)!=_valueToString.end() )
        {
            osg::notify(osg::WARN) << "Duplicate enum value " << value
                                   << " with old string: " << _valueToString[value]
                                   << " and new string: " << str << std::endl;
        }
        _valueToString[value] = str;
        _stringToValue[str] = value;
    }

    Value getValue( const char* str )
    {
        StringToValue::iterator itr = _stringToValue.find(str);
        if ( itr==_stringToValue.end() )
        {
            Value value;
            std::stringstream stream;
            stream << str; stream >> value;
            _stringToValue[str] = value;
            return value;
        }
        return itr->second;
    }

    const std::string& getString( Value value )
    {
        ValueToString::iterator itr = _valueToString.find(value);
        if ( itr==_valueToString.end() )
        {
            std::string str;
            std::stringstream stream;
            stream << value; stream >> str;
            _valueToString[value] = str;
            return _valueToString[value];
        }
        return itr->second;
    }

protected:
    StringToValue _stringToValue;
    ValueToString _valueToString;
};

class UserLookupTableProxy
{
public:
    typedef void (*AddValueFunc)( IntLookup* lookup );
    UserLookupTableProxy( AddValueFunc func ) { if ( func ) (*func)(&_lookup); }

    IntLookup _lookup;
};

#define BEGIN_USER_TABLE(NAME, CLASS) \
    static void add_user_value_func_##NAME(osgDB::IntLookup*); \
    static osgDB::UserLookupTableProxy s_user_lookup_table_##NAME(&add_user_value_func_##NAME); \
    static void add_user_value_func_##NAME(osgDB::IntLookup* lookup) { typedef CLASS MyClass
#define ADD_USER_VALUE(VALUE) lookup->add(#VALUE, MyClass::VALUE)
#define END_USER_TABLE() }

#define USER_READ_FUNC(NAME, FUNCNAME) \
    static int FUNCNAME(osgDB::InputStream& is) { \
        int value; if (is.isBinary()) is >> value; \
        else { std::string str; is >> str; \
        value = (s_user_lookup_table_##NAME)._lookup.getValue(str.c_str()); } \
        return value; }

#define USER_WRITE_FUNC(NAME, FUNCNAME) \
    static void FUNCNAME(osgDB::OutputStream& os, int value) { \
        if (os.isBinary()) os << value; \
        else os << (s_user_lookup_table_##NAME)._lookup.getString(value); } \

class BaseSerializer : public osg::Referenced
{
    friend class ObjectWrapper;
public:
    enum Type
    {
        RW_UNDEFINED = 0, RW_USER, RW_OBJECT, RW_IMAGE, RW_LIST,
        RW_BOOL, RW_CHAR, RW_UCHAR, RW_SHORT, RW_USHORT, RW_INT, RW_UINT, RW_FLOAT, RW_DOUBLE,
        RW_VEC2F, RW_VEC2D, RW_VEC3F, RW_VEC3D, RW_VEC4F, RW_VEC4D, RW_QUAT, RW_PLANE,
        RW_MATRIXF, RW_MATRIXD, RW_MATRIX, RW_GLENUM, RW_STRING, RW_ENUM,
        RW_VEC2B, RW_VEC2UB, RW_VEC2S, RW_VEC2US, RW_VEC2I, RW_VEC2UI,
        RW_VEC3B, RW_VEC3UB, RW_VEC3S, RW_VEC3US, RW_VEC3I, RW_VEC3UI,
        RW_VEC4B, RW_VEC4UB, RW_VEC4S, RW_VEC4US, RW_VEC4I, RE_VEC4UI
    };

    BaseSerializer() : _firstVersion(0), _lastVersion(INT_MAX) {}

    virtual bool read( InputStream&, osg::Object& ) = 0;
    virtual bool write( OutputStream&, const osg::Object& ) = 0;
    virtual const std::string& getName() const = 0;

protected:
    int _firstVersion;  // Library version when the serializer is first introduced
    int _lastVersion;  // Library version when the serializer is last required.
};

template<typename C>
class UserSerializer : public BaseSerializer
{
public:
    typedef bool (*Checker)( const C& );
    typedef bool (*Reader)( InputStream&, C& );
    typedef bool (*Writer)( OutputStream&, const C& );

    UserSerializer( const char* name, Checker cf, Reader rf, Writer wf )
    : BaseSerializer(), _name(name), _checker(cf), _reader(rf), _writer(wf) {}

    virtual bool read( InputStream& is, osg::Object& obj )
    {
        C& object = OBJECT_CAST<C&>(obj);
        if ( is.isBinary() )
        {
            bool ok = false; is >> ok;
            if ( !ok ) return true;
        }
        else
        {
            if ( !is.matchString(_name) )
                return true;
        }
        return (*_reader)(is, object);
    }

    virtual bool write( OutputStream& os, const osg::Object& obj )
    {
        const C& object = OBJECT_CAST<const C&>(obj);
        bool ok = (*_checker)(object);
        if ( os.isBinary() )
        {
            os << ok;
            if ( !ok ) return true;
        }
        else
        {
            if ( !ok ) return true;
            os << os.PROPERTY(_name.c_str());
        }
        return (*_writer)(os, object);
    }

    virtual const std::string& getName() const { return _name; }

protected:
    std::string _name;
    Checker _checker;

public:
    Reader _reader;
    Writer _writer;
};

template<typename P>
class TemplateSerializer : public BaseSerializer
{
public:

    TemplateSerializer( const char* name, P def)
    : BaseSerializer(), _name(name), _defaultValue(def) {}

    virtual bool read( InputStream& is, osg::Object& obj ) = 0;
    virtual bool write( OutputStream& os, const osg::Object& obj ) = 0;
    virtual const std::string& getName() const { return _name; }

protected:
    std::string _name;
    P _defaultValue;
};

template<typename C, typename P>
class PropByValSerializer : public TemplateSerializer<P>
{
public:
    typedef TemplateSerializer<P> ParentType;
    typedef P (C::*Getter)() const;
    typedef void (C::*Setter)( P );

    PropByValSerializer( const char* name, P def, Getter gf, Setter sf, bool useHex=false )
    : ParentType(name, def), _getter(gf), _setter(sf), _useHex(useHex) {}

    virtual bool read( InputStream& is, osg::Object& obj )
    {
        C& object = OBJECT_CAST<C&>(obj);
        P value;
        if ( is.isBinary() )
        {
            is >> value;
            if ( ParentType::_defaultValue!=value )
                (object.*_setter)( value );
        }
        else if ( is.matchString(ParentType::_name) )
        {
            if ( _useHex ) is >> std::hex;
            is >> value;
            if ( _useHex ) is >> std::dec;
            (object.*_setter)( value );
        }
        return true;
    }

    virtual bool write( OutputStream& os, const osg::Object& obj )
    {
        const C& object = OBJECT_CAST<const C&>(obj);
        P value = (object.*_getter)();
        if ( os.isBinary() )
        {
            os << value;
        }
        else if ( ParentType::_defaultValue!=value )
        {
            os << os.PROPERTY((ParentType::_name).c_str());
            if ( _useHex ) { os << std::hex << std::showbase; }
            os << value;
            if ( _useHex ) os << std::dec << std::noshowbase;
            os << std::endl;
        }
        return true;
    }

public:
    Getter _getter;
    Setter _setter;

protected:
    bool _useHex;
};

template<typename C, typename P>
class PropByRefSerializer : public TemplateSerializer<P>
{
public:
    typedef TemplateSerializer<P> ParentType;
    typedef const P& CP;
    typedef CP (C::*Getter)() const;
    typedef void (C::*Setter)( CP );

    PropByRefSerializer( const char* name, CP def, Getter gf, Setter sf )
    : ParentType(name, def), _getter(gf), _setter(sf) {}

    virtual bool read( InputStream& is, osg::Object& obj )
    {
        C& object = OBJECT_CAST<C&>(obj);
        P value;
        if ( is.isBinary() )
        {
            is >> value;
            if ( ParentType::_defaultValue!=value )
                (object.*_setter)( value );
        }
        else if ( is.matchString(ParentType::_name) )
        {
            is >> value;
            (object.*_setter)( value );
        }
        return true;
    }

    virtual bool write( OutputStream& os, const osg::Object& obj )
    {
        const C& object = OBJECT_CAST<const C&>(obj);
        CP value = (object.*_getter)();
        if ( os.isBinary() )
        {
            os << value;
        }
        else if ( ParentType::_defaultValue!=value )
        {
            os << os.PROPERTY((ParentType::_name).c_str()) << value << std::endl;
        }
        return true;
    }

public:
    Getter _getter;
    Setter _setter;
};

template<typename C>
class MatrixSerializer : public TemplateSerializer<osg::Matrix>
{
public:
    typedef TemplateSerializer<osg::Matrix> ParentType;
    typedef const osg::Matrix& (C::*Getter)() const;
    typedef void (C::*Setter)( const osg::Matrix& );

    MatrixSerializer( const char* name, const osg::Matrix& def, Getter gf, Setter sf )
    : ParentType(name, def), _getter(gf), _setter(sf) {}

    virtual bool read( InputStream& is, osg::Object& obj )
    {
        C& object = OBJECT_CAST<C&>(obj);
        osg::Matrix value;
        if ( is.isBinary() )
        {
            readMatrixImplementation( is, value );
            if ( ParentType::_defaultValue!=value )
                (object.*_setter)( value );
        }
        else if ( is.matchString(ParentType::_name) )
        {
            readMatrixImplementation( is, value );
            (object.*_setter)( value );
        }
        return true;
    }

    virtual bool write( OutputStream& os, const osg::Object& obj )
    {

        const C& object = OBJECT_CAST<const C&>(obj);
        const osg::Matrix& value = (object.*_getter)();
        if ( os.isBinary() )
        {
            os << value;
        }
        else if ( ParentType::_defaultValue!=value )
        {
            os << os.PROPERTY((ParentType::_name).c_str()) << value << std::endl;
        }
        return true;
    }

protected:
    void readMatrixImplementation( InputStream& is, osg::Matrix& matrix )
    {
#if 1
        is >> matrix;
#else
        if ( is.getUseFloatMatrix() )
        {
            osg::Matrixf realValue; is >> realValue;
            matrix = realValue;
        }
        else
        {
            osg::Matrixd realValue; is >> realValue;
            matrix = realValue;
        }
#endif
    }

public:
    Getter _getter;
    Setter _setter;
};

template<typename C, typename P>
class GLenumSerializer : public TemplateSerializer<P>
{
public:
    typedef TemplateSerializer<P> ParentType;
    typedef P (C::*Getter)() const;
    typedef void (C::*Setter)( P );

    GLenumSerializer( const char* name, P def, Getter gf, Setter sf )
    : ParentType(name, def), _getter(gf), _setter(sf) {}

    virtual bool read( InputStream& is, osg::Object& obj )
    {
        C& object = OBJECT_CAST<C&>(obj);
        if ( is.isBinary() )
        {
            GLenum value; is >> value;
            if ( ParentType::_defaultValue!=static_cast<P>(value) )
                (object.*_setter)( static_cast<P>(value) );
        }
        else if ( is.matchString(ParentType::_name) )
        {
            DEF_GLENUM(value); is >> value;
            (object.*_setter)( static_cast<P>(value.get()) );
        }
        return true;
    }

    virtual bool write( OutputStream& os, const osg::Object& obj )
    {
        const C& object = OBJECT_CAST<const C&>(obj);
        const P value = (object.*_getter)();
        if ( os.isBinary() )
        {
            os << static_cast<GLenum>(value);
        }
        else if ( ParentType::_defaultValue!=value )
        {
            os << os.PROPERTY((ParentType::_name).c_str()) << GLENUM(value) << std::endl;
        }
        return true;
    }

public:
    Getter _getter;
    Setter _setter;
};

template<typename C>
class StringSerializer : public TemplateSerializer<std::string>
{
public:
    typedef TemplateSerializer<std::string> ParentType;
    typedef const std::string& (C::*Getter)() const;
    typedef void (C::*Setter)( const std::string& );

    StringSerializer( const char* name, const std::string& def, Getter gf, Setter sf )
    : ParentType(name, def), _getter(gf), _setter(sf) {}

    virtual bool read( InputStream& is, osg::Object& obj )
    {
        C& object = OBJECT_CAST<C&>(obj);
        std::string value;
        if ( is.isBinary() )
        {
            is >> value;
            if ( ParentType::_defaultValue!=value )
                (object.*_setter)( value );
        }
        else if ( is.matchString(ParentType::_name) )
        {
            is.readWrappedString( value );
            if ( !value.empty() )
                (object.*_setter)( value );
        }
        return true;
    }

    virtual bool write( OutputStream& os, const osg::Object& obj )
    {
        const C& object = OBJECT_CAST<const C&>(obj);
        const std::string& value = (object.*_getter)();
        if ( os.isBinary() )
        {
            os << value;
        }
        else if ( ParentType::_defaultValue!=value )
        {
            os << os.PROPERTY((ParentType::_name).c_str());
            os.writeWrappedString( value );
            os << std::endl;
        }
        return true;
    }

public:
    Getter _getter;
    Setter _setter;
};

template<typename C, typename P>
class ObjectSerializer : public TemplateSerializer<P*>
{
public:
    typedef TemplateSerializer<P*> ParentType;
    typedef const P* (C::*Getter)() const;
    typedef void (C::*Setter)( P* );

    ObjectSerializer( const char* name, P* def, Getter gf, Setter sf )
    : ParentType(name, def), _getter(gf), _setter(sf) {}

    virtual bool read( InputStream& is, osg::Object& obj )
    {
        C& object = OBJECT_CAST<C&>(obj);
        bool hasObject = false;
        if ( is.isBinary() )
        {
            is >> hasObject;
            if ( hasObject )
            {
                P* value = dynamic_cast<P*>( is.readObject() );
                if ( ParentType::_defaultValue!=value )
                    (object.*_setter)( value );
            }
        }
        else if ( is.matchString(ParentType::_name) )
        {
            is >> hasObject;
            if ( hasObject )
            {
                is >> is.BEGIN_BRACKET;
                P* value = dynamic_cast<P*>( is.readObject() );
                if ( ParentType::_defaultValue!=value )
                    (object.*_setter)( value );
                is >> is.END_BRACKET;
            }
        }
        return true;
    }

    virtual bool write( OutputStream& os, const osg::Object& obj )
    {
        const C& object = OBJECT_CAST<const C&>(obj);
        const P* value = (object.*_getter)();
        bool hasObject = (value!=NULL);
        if ( os.isBinary() )
        {
            os << hasObject;
            os.writeObject( value );
        }
        else if ( ParentType::_defaultValue!=value )
        {
            os << os.PROPERTY((ParentType::_name).c_str()) << hasObject;
            if ( hasObject )
            {
                os << os.BEGIN_BRACKET << std::endl;
                os.writeObject( value );
                os << os.END_BRACKET;
            }
            os << std::endl;
        }
        return true;
    }

public:
    Getter _getter;
    Setter _setter;
};

template<typename C, typename P>
class ImageSerializer : public TemplateSerializer<P*>
{
public:
    typedef TemplateSerializer<P*> ParentType;
    typedef const P* (C::*Getter)() const;
    typedef void (C::*Setter)( P* );

    ImageSerializer( const char* name, P* def, Getter gf, Setter sf )
    : ParentType(name, def), _getter(gf), _setter(sf) {}

    virtual bool read( InputStream& is, osg::Object& obj )
    {
        C& object = OBJECT_CAST<C&>(obj);
        bool hasObject = false;
        if ( is.isBinary() )
        {
            is >> hasObject;
            if ( hasObject )
            {
                P* value = dynamic_cast<P*>( is.readImage() );
                if ( ParentType::_defaultValue!=value )
                    (object.*_setter)( value );
            }
        }
        else if ( is.matchString(ParentType::_name) )
        {
            is >> hasObject;
            if ( hasObject )
            {
                is >> is.BEGIN_BRACKET;
                P* value = dynamic_cast<P*>( is.readImage() );
                if ( ParentType::_defaultValue!=value )
                    (object.*_setter)( value );
                is >> is.END_BRACKET;
            }
        }
        return true;
    }

    virtual bool write( OutputStream& os, const osg::Object& obj )
    {
        const C& object = OBJECT_CAST<const C&>(obj);
        const P* value = (object.*_getter)();
        bool hasObject = (value!=NULL);
        if ( os.isBinary() )
        {
            os << hasObject;
            os.writeImage( value );
        }
        else if ( ParentType::_defaultValue!=value )
        {
            os << os.PROPERTY((ParentType::_name).c_str()) << hasObject;
            if ( hasObject )
            {
                os << os.BEGIN_BRACKET << std::endl;
                os.writeImage( value );
                os << os.END_BRACKET;
            }
            os << std::endl;
        }
        return true;
    }

public:
    Getter _getter;
    Setter _setter;
};

template<typename C, typename P, typename B>
class EnumSerializer : public TemplateSerializer<P>
{
public:
    typedef TemplateSerializer<P> ParentType;
    typedef P (C::*Getter)() const;
    typedef B (C::*Setter)( P );

    EnumSerializer( const char* name, P def, Getter gf, Setter sf )
    : ParentType(name, def), _getter(gf), _setter(sf) {}

    void add( const char* str, P value )
    { _lookup.add(str, static_cast<IntLookup::Value>(value)); }

    P getValue( const char* str )
    { return static_cast<P>(_lookup.getValue(str)); }

    const std::string& getString( P value )
    { return _lookup.getString(static_cast<IntLookup::Value>(value)); }

    virtual bool read( InputStream& is, osg::Object& obj )
    {
        C& object = OBJECT_CAST<C&>(obj);
        IntLookup::Value value;
        if ( is.isBinary() )
        {
            is >> value;
            if ( ParentType::_defaultValue!=static_cast<P>(value) )
                (object.*_setter)( static_cast<P>(value) );
        }
        else if ( is.matchString(ParentType::_name) )
        {
            std::string str; is >> str;
            (object.*_setter)( getValue(str.c_str()) );
        }
        return true;
    }

    virtual bool write( osgDB::OutputStream& os, const osg::Object& obj )
    {
        const C& object = OBJECT_CAST<const C&>(obj);
        const P value = (object.*_getter)();
        if ( os.isBinary() )
        {
            os << (IntLookup::Value)value;
        }
        else if ( ParentType::_defaultValue!=value )
        {
            os << os.PROPERTY((ParentType::_name).c_str()) << getString(value) << std::endl;
        }
        return true;
    }

public:
    Getter _getter;
    Setter _setter;

protected:
    IntLookup _lookup;
};


template<typename C, typename P>
class ListSerializer : public BaseSerializer
{
public:
    typedef typename P::value_type ValueType;
    typedef typename P::const_iterator ConstIterator;
    typedef const P& (C::*Getter)() const;
    typedef void (C::*Setter)( const P& );

    ListSerializer( const char* name, Getter gf, Setter sf )
    : _name(name), _getter(gf), _setter(sf) {}

    virtual const std::string& getName() const { return _name; }

    virtual bool read( InputStream& is, osg::Object& obj )
    {
        C& object = OBJECT_CAST<C&>(obj);
        unsigned int size = 0;
        P list;
        if ( is.isBinary() )
        {
            is >> size;
            for ( unsigned int i=0; i<size; ++i )
            {
                ValueType value;
                is >> value;
                list.push_back( value );
            }
            if ( size>0 ) (object.*_setter)( list );
        }
        else if ( is.matchString(_name) )
        {
            is >> size;
            if ( size>0 ) is >> is.BEGIN_BRACKET;
            for ( unsigned int i=0; i<size; ++i )
            {
                ValueType value;
                is >> value;
                list.push_back( value );
            }
            if ( size>0 )
            {
                is >> is.END_BRACKET;
                (object.*_setter)( list );
            }
        }
        return true;
    }

    virtual bool write( OutputStream& os, const osg::Object& obj )
    {
        const C& object = OBJECT_CAST<const C&>(obj);
        const P& list = (object.*_getter)();
        unsigned int size = (unsigned int)list.size();
        if ( os.isBinary() )
        {
            os << size;
            for ( ConstIterator itr=list.begin();
                  itr!=list.end(); ++itr )
            {
                os << (*itr);
            }
        }
        else if ( size>0 )
        {
            os << os.PROPERTY((_name).c_str()) << size << os.BEGIN_BRACKET << std::endl;
            for ( ConstIterator itr=list.begin();
                  itr!=list.end(); ++itr )
            {
                os << (*itr);
            }
            os << std::endl;
            os << os.END_BRACKET << std::endl;
        }
        return true;
    }

public:
    std::string _name;
    Getter _getter;
    Setter _setter;
};

// ADDING MANIPULATORS
#define ADD_SERIALIZER(S) \
    wrapper->addSerializer( (S) )

#define ADD_USER_SERIALIZER(PROP) \
    wrapper->addSerializer( new osgDB::UserSerializer<MyClass>( \
        #PROP, &check##PROP, &read##PROP, &write##PROP), osgDB::BaseSerializer::RW_USER )

#define ADD_BOOL_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, bool >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_BOOL )

#define ADD_CHAR_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, char >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_CHAR )

#define ADD_UCHAR_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, unsigned char >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_UCHAR )

#define ADD_SHORT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, short >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_SHORT )

#define ADD_USHORT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, unsigned short >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_USHORT )

#define ADD_HEXSHORT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, unsigned short >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP, true), osgDB::BaseSerializer::RW_USHORT )

#define ADD_INT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, int >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_INT )

#define ADD_UINT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, unsigned int >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_UINT )

#define ADD_GLINT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, GLint >( \
        #PROP, ((int)(DEF)), &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_INT )

#define ADD_HEXINT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, unsigned int >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP, true), osgDB::BaseSerializer::RW_UINT )

#define ADD_FLOAT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, float >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_FLOAT )

#define ADD_DOUBLE_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByValSerializer< MyClass, double >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_DOUBLE )

#define ADD_REF_BOOL_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, bool >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_BOOL )

#define ADD_REF_CHAR_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, char >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_CHAR )

#define ADD_REF_UCHAR_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, unsigned char >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_UCHAR )

#define ADD_REF_SHORT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, short >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_SHORT )

#define ADD_REF_USHORT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, unsigned short >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_USHORT )

#define ADD_REF_HEXSHORT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, unsigned short >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP, true), osgDB::BaseSerializer::RW_USHORT )

#define ADD_REF_INT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, int >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_INT )

#define ADD_REF_UINT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, unsigned int >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_UINT )

#define ADD_REF_GLINT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, GLint >( \
        #PROP, ((int)(DEF)), &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_INT )

#define ADD_REF_HEXINT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, unsigned int >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP, true), osgDB::BaseSerializer::RW_UINT )

#define ADD_REF_FLOAT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, float >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_FLOAT )

#define ADD_REF_DOUBLE_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, double >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_DOUBLE )


#define ADD_VEC2B_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec2b >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC2B )

#define ADD_VEC2UB_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec2ub >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC2UB )

#define ADD_VEC2S_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec2s >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC2S )

#define ADD_VEC2US_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec2us >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC2US )

#define ADD_VEC2I_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec2i >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC2I )

#define ADD_VEC2UI_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec2ui >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC2UI )

#define ADD_VEC2F_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec2f >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC2F )

#define ADD_VEC2D_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec2d >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC2D )

#define ADD_VEC2_SERIALIZER(PROP, DEF) ADD_VEC2F_SERIALIZER(PROP, DEF)


#define ADD_VEC3B_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec3b >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC3B )

#define ADD_VEC3UB_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec3ub >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC3UB )

#define ADD_VEC3S_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec3s >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC3S )

#define ADD_VEC3US_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec3us >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC3US )

#define ADD_VEC3I_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec3i >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC3I )

#define ADD_VEC3UI_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec3ui >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC3UI )

#define ADD_VEC3F_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec3f >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC3F )

#define ADD_VEC3D_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec3d >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC3D )

#define ADD_VEC3_SERIALIZER(PROP, DEF) ADD_VEC3F_SERIALIZER(PROP, DEF)

#define ADD_VEC4B_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec4b >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC4B )

#define ADD_VEC4UB_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec4ub >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC4UB )

#define ADD_VEC4S_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec4s >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC4S )

#define ADD_VEC4US_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec4us >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC4US )

#define ADD_VEC4I_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec4i >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC4I )

#define ADD_VEC4UI_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec4ui >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC4UI )

#define ADD_VEC4F_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec4f >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC4F )

#define ADD_VEC4D_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Vec4d >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_VEC4D )

#define ADD_VEC4_SERIALIZER(PROP, DEF) ADD_VEC4F_SERIALIZER(PROP, DEF)

#define ADD_QUAT_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Quat >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_QUAT )

#define ADD_PLANE_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Plane >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_PLANE )

#define ADD_MATRIXF_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Matrixf >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_MATRIXF )

#define ADD_MATRIXD_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::PropByRefSerializer< MyClass, osg::Matrixd >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_MATRIXD )

#define ADD_MATRIX_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::MatrixSerializer< MyClass >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_MATRIX )

#define ADD_GLENUM_SERIALIZER(PROP, TYPE, DEF) \
    wrapper->addSerializer( new osgDB::GLenumSerializer< MyClass, TYPE >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_GLENUM )

#define ADD_STRING_SERIALIZER(PROP, DEF) \
    wrapper->addSerializer( new osgDB::StringSerializer< MyClass >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_STRING )

#define ADD_OBJECT_SERIALIZER(PROP, TYPE, DEF) \
    wrapper->addSerializer( new osgDB::ObjectSerializer< MyClass, TYPE >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_OBJECT )

#define ADD_IMAGE_SERIALIZER(PROP, TYPE, DEF) \
    wrapper->addSerializer( new osgDB::ImageSerializer< MyClass, TYPE >( \
        #PROP, DEF, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_IMAGE )

#define ADD_LIST_SERIALIZER(PROP, TYPE) \
    wrapper->addSerializer( new osgDB::ListSerializer< MyClass, TYPE >( \
        #PROP, &MyClass::get##PROP, &MyClass::set##PROP), osgDB::BaseSerializer::RW_LIST )

#define BEGIN_ENUM_SERIALIZER(PROP, DEF) \
    { typedef osgDB::EnumSerializer<MyClass, MyClass::PROP, void> MySerializer; \
    osg::ref_ptr<MySerializer> serializer = new MySerializer( \
        #PROP, MyClass::DEF, &MyClass::get##PROP, &MyClass::set##PROP)

#define BEGIN_ENUM_SERIALIZER2(PROP, TYPE, DEF) \
    { typedef osgDB::EnumSerializer<MyClass, TYPE, void> MySerializer; \
    osg::ref_ptr<MySerializer> serializer = new MySerializer( \
        #PROP, MyClass::DEF, &MyClass::get##PROP, &MyClass::set##PROP)

#define BEGIN_ENUM_SERIALIZER3(PROP, DEF) \
    { typedef osgDB::EnumSerializer<MyClass, MyClass::PROP, bool> MySerializer; \
    osg::ref_ptr<MySerializer> serializer = new MySerializer( \
        #PROP, MyClass::DEF, &MyClass::get##PROP, &MyClass::set##PROP)

#define BEGIN_ENUM_SERIALIZER4(PROPERTIES_CLASS, PROP, DEF) \
    { typedef osgDB::EnumSerializer<MyClass, PROPERTIES_CLASS::PROP, void> MySerializer; \
    osg::ref_ptr<MySerializer> serializer = new MySerializer( \
        #PROP, PROPERTIES_CLASS::DEF, &MyClass::get##PROP, &MyClass::set##PROP)

#define ADD_ENUM_VALUE(VALUE) \
    serializer->add(#VALUE, MyClass::VALUE)

#define ADD_ENUM_CLASS_VALUE(CLASS, VALUE) \
    serializer->add(#VALUE, CLASS::VALUE)

#define END_ENUM_SERIALIZER() \
    wrapper->addSerializer(serializer.get(), osgDB::BaseSerializer::RW_ENUM); }

// VERSION CONTROL OPERATORS
#define UPDATE_TO_VERSION(VER) \
    wrapper->setUpdatedVersion( (VER) );

#define UPDATE_TO_VERSION_SCOPED(VER) \
    osgDB::UpdateWrapperVersionProxy uwvp(wrapper, (VER));

#define REMOVE_SERIALIZER(PROP) \
    wrapper->markSerializerAsRemoved( #PROP );

}

#endif
