/* -*-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_OBJECTWRAPPER
#define OSGDB_OBJECTWRAPPER

#include <osgDB/Serializer>

namespace osgDB
{

typedef std::vector<std::string> StringList;
extern OSGDB_EXPORT void split( const std::string& src, StringList& list, char separator=' ' );

class OSGDB_EXPORT BaseCompressor : public osg::Referenced
{
public:
    BaseCompressor() {}
    void setName( const std::string& name ) { _name = name; }
    const std::string& getName() const { return _name; }

    virtual bool compress( std::ostream&, const std::string& ) = 0;
    virtual bool decompress( std::istream&, std::string& ) = 0;

protected:
    std::string _name;
};

struct FinishedObjectReadCallback : public osg::Referenced
{
    virtual void objectRead(osgDB::InputStream& is, osg::Object& obj) = 0;
};

class OSGDB_EXPORT ObjectWrapper : public osg::Referenced
{
public:
    typedef std::vector< osg::ref_ptr<BaseSerializer> > SerializerList;
    typedef std::vector< osg::ref_ptr<FinishedObjectReadCallback> > FinishedObjectReadCallbackList;

    ObjectWrapper( osg::Object* proto, const std::string& name,
                   const std::string& associates );
    ObjectWrapper( osg::Object* proto, const std::string& domain, const std::string& name,
                   const std::string& associates );

    void setUpdatedVersion( int ver ) { _version = ver; }
    int getUpdatedVersion() const { return _version; }

    const osg::Object* getProto() const { return _proto.get(); }
    const std::string& getName() const { return _name; }
    const StringList& getAssociates() const { return _associates; }

    void addSerializer( BaseSerializer* s, BaseSerializer::Type t=BaseSerializer::RW_UNDEFINED );
    void markSerializerAsRemoved( const std::string& name );
    BaseSerializer* getSerializer( const std::string& name );
    void addFinishedObjectReadCallback ( FinishedObjectReadCallback* forc) { _finishedObjectReadCallbacks.push_back(forc); }

    bool read( InputStream&, osg::Object& );
    bool write( OutputStream&, const osg::Object& );

    bool readSchema( const StringList& properties, const std::vector<int>& types );
    void writeSchema( StringList& properties, std::vector<int>& types );
    void resetSchema()
    { if ( _backupSerializers.size()>0 ) _serializers = _backupSerializers; }

protected:
    ObjectWrapper() : _version(0) {}
    virtual ~ObjectWrapper() {}

    osg::ref_ptr<osg::Object> _proto;
    std::string _domain;
    std::string _name;
    StringList _associates;
    SerializerList _serializers;
    SerializerList _backupSerializers;
    std::vector<int> _typeList;
    FinishedObjectReadCallbackList _finishedObjectReadCallbacks;
    int _version;  // Last updated version of the wrapper
};

struct UpdateWrapperVersionProxy
{
    UpdateWrapperVersionProxy( ObjectWrapper* w, int v ): _wrapper(w)
    {
        _lastVersion = w->getUpdatedVersion();
        w->setUpdatedVersion(v);        
    }
    
    ~UpdateWrapperVersionProxy()
    {
        _wrapper->setUpdatedVersion(_lastVersion);
    }
    
    ObjectWrapper* _wrapper;
    int _lastVersion;
};

class Registry;

class OSGDB_EXPORT ObjectWrapperManager : public osg::Referenced
{
public:

    // Wrapper handlers
    void addWrapper( ObjectWrapper* wrapper );
    void removeWrapper( ObjectWrapper* wrapper );
    ObjectWrapper* findWrapper( const std::string& name );

    typedef std::map< std::string, osg::ref_ptr<ObjectWrapper> > WrapperMap;
    WrapperMap& getWrapperMap() { return _wrappers; }
    const WrapperMap& getWrapperMap() const { return _wrappers; }

    // Compressor handlers
    void addCompressor( BaseCompressor* compressor );
    void removeCompressor( BaseCompressor* compressor );
    BaseCompressor* findCompressor( const std::string& name );

    typedef std::map< std::string, osg::ref_ptr<BaseCompressor> > CompressorMap;
    CompressorMap& getCompressorMap() { return _compressors; }
    const CompressorMap& getCompressorMap() const { return _compressors; }

    typedef std::map<std::string, IntLookup> IntLookupMap;
    IntLookup::Value getValue( const std::string& group, const std::string& str ) { return findLookup(group).getValue(str.c_str()); }
    const std::string& getString( const std::string& group, IntLookup::Value value ) { return findLookup(group).getString(value); }

protected:

    friend class osgDB::Registry;

    ObjectWrapperManager();
    virtual ~ObjectWrapperManager();

    WrapperMap _wrappers;
    CompressorMap _compressors;

    IntLookup& findLookup( const std::string& group )
    {
        IntLookupMap::iterator itr = _globalMap.find(group);
        if ( itr!=_globalMap.end() ) return itr->second;
        else return _globalMap["GL"];
    }

    IntLookupMap _globalMap;
};


class OSGDB_EXPORT RegisterWrapperProxy
{
public:
    typedef void (*AddPropFunc)( ObjectWrapper* );

    RegisterWrapperProxy( osg::Object* proto, const std::string& name,
                          const std::string& associates, AddPropFunc func );

    virtual ~RegisterWrapperProxy();

protected:
    osg::ref_ptr<ObjectWrapper> _wrapper;
};

class OSGDB_EXPORT RegisterCustomWrapperProxy
{
public:
    typedef void (*AddPropFunc)( const char*, ObjectWrapper* );

    RegisterCustomWrapperProxy( osg::Object* proto, const std::string& domain, const std::string& name,
                                const std::string& associates, AddPropFunc func );

    virtual ~RegisterCustomWrapperProxy();

protected:
    osg::ref_ptr<ObjectWrapper> _wrapper;
};

#define REGISTER_OBJECT_WRAPPER(NAME, PROTO, CLASS, ASSOCIATES) \
    extern "C" void wrapper_serializer_##NAME(void) {} \
    extern void wrapper_propfunc_##NAME(osgDB::ObjectWrapper*); \
    static osgDB::RegisterWrapperProxy wrapper_proxy_##NAME( \
        PROTO, #CLASS, ASSOCIATES, &wrapper_propfunc_##NAME); \
    typedef CLASS MyClass; \
    void wrapper_propfunc_##NAME(osgDB::ObjectWrapper* wrapper)

#define REGISTER_OBJECT_WRAPPER2(NAME, PROTO, CLASS, CLASSNAME, ASSOCIATES) \
    extern "C" void wrapper_serializer_##NAME(void) {} \
    extern void wrapper_propfunc_##NAME(osgDB::ObjectWrapper*); \
    static osgDB::RegisterWrapperProxy wrapper_proxy_##NAME( \
        PROTO, CLASSNAME, ASSOCIATES, &wrapper_propfunc_##NAME); \
    typedef CLASS MyClass; \
    void wrapper_propfunc_##NAME(osgDB::ObjectWrapper* wrapper)

#define REGISTER_CUSTOM_OBJECT_WRAPPER(DOMAIN, NAME, PROTO, CLASS, ASSOCIATES) \
    extern "C" void wrapper_serializer_##NAME(void) {} \
    extern void wrapper_propfunc_##NAME(const char*, osgDB::ObjectWrapper*); \
    static osgDB::RegisterCustomWrapperProxy wrapper_proxy_##NAME( \
        PROTO, #DOMAIN, #CLASS, ASSOCIATES, &wrapper_propfunc_##NAME); \
    typedef CLASS MyClass; \
    void wrapper_propfunc_##NAME(const char* domain, osgDB::ObjectWrapper* wrapper)

#define REGISTER_CUSTOM_OBJECT_WRAPPER2(DOMAIN, NAME, PROTO, CLASS, CLASSNAME, ASSOCIATES) \
    extern "C" void wrapper_serializer_##NAME(void) {} \
    extern void wrapper_propfunc_##NAME(const char*, osgDB::ObjectWrapper*); \
    static osgDB::RegisterCustomWrapperProxy wrapper_proxy_##NAME( \
        PROTO, #DOMAIN, CLASSNAME, ASSOCIATES, &wrapper_propfunc_##NAME); \
    typedef CLASS MyClass; \
    void wrapper_propfunc_##NAME(const char* domain, osgDB::ObjectWrapper* wrapper)

class OSGDB_EXPORT RegisterCompressorProxy
{
public:
    RegisterCompressorProxy( const std::string& name, BaseCompressor* compressor );
    ~RegisterCompressorProxy();

protected:
    osg::ref_ptr<BaseCompressor> _compressor;
};

#define REGISTER_COMPRESSOR(NAME, CLASS) \
    extern "C" void wrapper_compressor_##CLASS(void) {} \
    static osgDB::RegisterCompressorProxy compressor_proxy_##CLASS(NAME, new CLASS);

}

#endif
