/* -*-c++-*- VirtualPlanetBuilder - Copyright (C) 1998-2007 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.
*/

#ifndef SERIALIZER_H
#define SERIALIZER_H 1

#include <osgDB/Input>
#include <osgDB/Output>
#include <osgDB/ParameterOutput>

namespace osgDB
{

class IntLookup
{
public:

    typedef int Value;

    IntLookup(Value defaultValue):
        _default(defaultValue) {}

    typedef std::map<std::string, Value> StringToValue;
    typedef std::map<Value, std::string> ValueToString;

    StringToValue   _stringToValue;
    ValueToString   _valueToString;
    
    void add(Value value, const char* str)
    {
        _stringToValue[str] = value;
        _valueToString[value] = str;
    }
    
    Value getValue(const char* str)
    {
        StringToValue::iterator itr = _stringToValue.find(str);
        if (itr==_stringToValue.end()) return _default;
        return itr->second;
    }
    
    const std::string& getString(Value value)
    {
        ValueToString::iterator itr = _valueToString.find(value);
        if (itr==_valueToString.end()) return _valueToString[_default];
        return itr->second;
    }
    
    Value _default;

};

class Serializer : public osg::Referenced
{
public:

     Serializer() {}
     
     virtual bool write(osgDB::Output&, const osg::Object&) = 0;
     virtual bool read(osgDB::Input&, osg::Object&, bool&) = 0;
};

template<typename C, typename P>
class EnumSerializer : public Serializer
{
public:

     typedef P (C::*GetterFunctionType)() const;
     typedef void (C::*SetterFunctionType)(P);

     EnumSerializer(const char* fieldName, P defaultValue, GetterFunctionType getter, SetterFunctionType setter):
        _fieldName(fieldName),
        _default(defaultValue),
        _getter(getter),
        _setter(setter),
        _lookup(defaultValue) {}
     
    void add(P value, const char* str)
    {
        _lookup.add(static_cast<IntLookup::Value>(value), str);
    }

    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));
    }

    bool write(osgDB::Output& fw, const osg::Object& obj)
    {
        const C& object = static_cast<const C&>(obj);

        if (fw.getWriteOutDefaultValues() ||
            _default != (object.*_getter)())
        {
            fw.indent()<<_fieldName<<" "<<getString((object.*_getter)())<<std::endl;
        }
        
        return true;
    }

    bool read(osgDB::Input& fr, osg::Object& obj, bool& itrAdvanced)
    {
        C& object = static_cast<C&>(obj);
        if (fr[0].matchWord(_fieldName.c_str()) && fr[1].isWord())
        {
            (object.*_setter)(getValue(fr[1].getStr()));
            fr += 2;
            itrAdvanced = true;
        }
        
        return true;
    }

    std::string        _fieldName;
    P                  _default;
    GetterFunctionType _getter;
    SetterFunctionType _setter;
    IntLookup          _lookup;
};


template<typename C>
class StringSerializer : public Serializer
{
public:

     typedef const std::string& P;
     typedef P (C::*GetterFunctionType)() const;
     typedef void (C::*SetterFunctionType)(P);

     StringSerializer(const char* fieldName, P defaultValue, GetterFunctionType getter, SetterFunctionType setter):
        _fieldName(fieldName),
        _default(defaultValue),
        _getter(getter),
        _setter(setter) {}
     
     bool write(osgDB::Output& fw, const osg::Object& obj)
     {
        const C& object = static_cast<const C&>(obj);
        if (fw.getWriteOutDefaultValues() ||
            _default != (object.*_getter)())
        {
            fw.indent()<<_fieldName<<" "<<fw.wrapString((object.*_getter)())<<std::endl;
        }
        
        return true;
     }

    bool read(osgDB::Input& fr, osg::Object& obj, bool& itrAdvanced)
    {
        C& object = static_cast<C&>(obj);
        if (fr[0].matchWord(_fieldName.c_str()) && (fr[1].isWord() || fr[1].isString()))
        {
            (object.*_setter)(fr[1].getStr());
            fr += 2;
            itrAdvanced = true;
        }
        
        return true;
     }
     
     std::string        _fieldName;
     std::string        _default;
     GetterFunctionType _getter;
     SetterFunctionType _setter;
};


template<typename C, typename P>
class TemplateSerializer : public Serializer
{
public:

     typedef P (C::*GetterFunctionType)() const;
     typedef void (C::*SetterFunctionType)(P);

     TemplateSerializer(const char* fieldName, P defaultValue, GetterFunctionType getter, SetterFunctionType setter):
        _fieldName(fieldName),
        _default(defaultValue),
        _getter(getter),
        _setter(setter) {}
     
     bool write(osgDB::Output& fw, const osg::Object& obj)
     {
        const C& object = static_cast<const C&>(obj);
        if (fw.getWriteOutDefaultValues() ||
            _default != (object.*_getter)())
        {
            fw.indent()<<_fieldName<<" "<<(object.*_getter)()<<std::endl;
        }
        
        return true;
     }

    bool read(osgDB::Input& fr, osg::Object& obj, bool& itrAdvanced)
    {
        C& object = static_cast<C&>(obj);
        P value;
        if (fr.read(_fieldName.c_str(), value))
        {
            (object.*_setter)(value);
            itrAdvanced = true;
        }
        
        return true;
     }
     
     std::string        _fieldName;
     P                  _default;
     GetterFunctionType _getter;
     SetterFunctionType _setter;
};

template<typename C>
class Vec4Serializer : public Serializer
{
public:

     typedef osg::Vec4 V;
     typedef const V& P;
     typedef P (C::*GetterFunctionType)() const;
     typedef void (C::*SetterFunctionType)(P);

     Vec4Serializer(const char* fieldName, P defaultValue, GetterFunctionType getter, SetterFunctionType setter):
        _fieldName(fieldName),
        _default(defaultValue),
        _getter(getter),
        _setter(setter) {}
     
     bool write(osgDB::Output& fw, const osg::Object& obj)
     {
        const C& object = static_cast<const C&>(obj);
        if (fw.getWriteOutDefaultValues() ||
            _default != (object.*_getter)())
        {
            fw.indent()<<_fieldName<<" "<<(object.*_getter)()<<std::endl;
        }
        
        return true;
     }

    bool read(osgDB::Input& fr, osg::Object& obj, bool& itrAdvanced)
    {
        C& object = static_cast<C&>(obj);
        V value;
        if (fr.read(_fieldName.c_str(), value[0], value[1], value[2], value[3]))
        {
            (object.*_setter)(value);
            fr += 2;
            itrAdvanced = true;
        }
        
        return true;
     }
     
     std::string        _fieldName;
     V                  _default;
     GetterFunctionType _getter;
     SetterFunctionType _setter;
};


template<typename C>
class BoolSerializer : public Serializer
{
public:

     typedef bool P;
     typedef P (C::*GetterFunctionType)() const;
     typedef void (C::*SetterFunctionType)(P);

     BoolSerializer(const char* fieldName, P defaultValue, GetterFunctionType getter, SetterFunctionType setter):
        _fieldName(fieldName),
        _default(defaultValue),
        _getter(getter),
        _setter(setter) {}
     
     bool write(osgDB::Output& fw, const osg::Object& obj)
     {
        const C& object = static_cast<const C&>(obj);
        if (fw.getWriteOutDefaultValues() ||
            _default != (object.*_getter)())
        {
            fw.indent()<<_fieldName<<" "<<((object.*_getter)() ? "TRUE" : "FALSE")<<std::endl;
        }
        
        return true;
     }

    bool read(osgDB::Input& fr, osg::Object& obj, bool& itrAdvanced)
    {
        C& object = static_cast<C&>(obj);
        if (fr[0].matchWord(_fieldName.c_str()) && fr[1].isWord())
        {
            (object.*_setter)(fr[1].matchWord("TRUE") || fr[1].matchWord("True") || fr[1].matchWord("true"));
            fr += 2;
            itrAdvanced = true;
        }
        
        return true;
     }
     
     std::string        _fieldName;
     P                  _default;
     GetterFunctionType _getter;
     SetterFunctionType _setter;
};

#define CREATE_STRING_SERIALIZER(CLASS,PROPERTY,PROTOTYPE) \
    new osgDB::StringSerializer<CLASS>( \
    #PROPERTY, \
    PROTOTYPE.get##PROPERTY(), \
    &CLASS::get##PROPERTY, \
    &CLASS::set##PROPERTY)

#define CREATE_UINT_SERIALIZER(CLASS,PROPERTY,PROTOTYPE) \
    new osgDB::TemplateSerializer<CLASS,unsigned int>( \
    #PROPERTY, \
    PROTOTYPE.get##PROPERTY(), \
    &CLASS::get##PROPERTY, \
    &CLASS::set##PROPERTY)

#define CREATE_INT_SERIALIZER(CLASS,PROPERTY,PROTOTYPE) \
    new osgDB::TemplateSerializer<CLASS, int>( \
    #PROPERTY, \
    PROTOTYPE.get##PROPERTY(), \
    &CLASS::get##PROPERTY, \
    &CLASS::set##PROPERTY)

#define CREATE_FLOAT_SERIALIZER(CLASS,PROPERTY,PROTOTYPE) \
    new osgDB::TemplateSerializer<CLASS,float>( \
    #PROPERTY, \
    PROTOTYPE.get##PROPERTY(), \
    &CLASS::get##PROPERTY, \
    &CLASS::set##PROPERTY)

#define CREATE_DOUBLE_SERIALIZER(CLASS,PROPERTY,PROTOTYPE) \
    new osgDB::TemplateSerializer<CLASS, double>( \
    #PROPERTY, \
    PROTOTYPE.get##PROPERTY(), \
    &CLASS::get##PROPERTY, \
    &CLASS::set##PROPERTY)

#define CREATE_VEC4_SERIALIZER(CLASS,PROPERTY,PROTOTYPE) \
    new osgDB::Vec4Serializer<CLASS>( \
    #PROPERTY, \
    PROTOTYPE.get##PROPERTY(), \
    &CLASS::get##PROPERTY, \
    &CLASS::set##PROPERTY)

#define CREATE_BOOL_SERIALIZER(CLASS,PROPERTY,PROTOTYPE) \
    new osgDB::BoolSerializer<CLASS>( \
    #PROPERTY, \
    PROTOTYPE.get##PROPERTY(), \
    &CLASS::get##PROPERTY, \
    &CLASS::set##PROPERTY)


}

#endif

