#pragma once

#include "ros_bridge/rapidjson/include/rapidjson/document.h"
#include "ros_bridge/include/JsonMethodes.h"
#include "ros_bridge/include/MessageBaseClass.h"
#include "utilities.h"
#include "ros_bridge/include/MessageTraits.h"

#include "ros_bridge/include/MessageGroups.h"

#include "boost/type_traits.hpp"
#include "boost/type_traits/is_base_of.hpp"

namespace ROSBridge {

//!
//! \brief The TypeFactory class is used to create C++ representatives of ROS messages.
//!
//! The TypeFactory class is used to create C++ representatives of ROS messages
//! (classes derived from \class MessageBaseClass) from a \class rapidjson::Document document.
class TypeFactory
{
    typedef MessageBaseClass ROSMsg;
public:

    TypeFactory(){}

    //!
    //! \brief Creates a \class rapidjson::Document document containing a ROS mesage from \p msg.
    //!
    //! Creates a \class rapidjson::Document document containing a ROS message from \p msg.
    //! A compile-time error will occur, if \p msg belongs to the \struct EmptyGroup or is
    //! not derived from \class MessageBaseClass.
    //! \param msg Instance of a \class MessageBaseClass subclass belonging to a ROSMessageGroup.
    //! \return rapidjson::Document document containing a ROS message.
    template <class T>
    bool create(const rapidjson::Document &doc, T &type){
        static_assert(boost::is_base_of<ROSMsg, T>::value, "Type of msg must be derived from MessageBaseClass.");
        static_assert(!::boost::is_same<typename T::Group, MessageGroups::EmptyGroup>::value,
                "msg belongs to group EmptyGroup (not allowed). Please specify Group (typedef MessageGroup Group)");
        return _create(doc, type, Type2Type<typename T::Group>());
    }

private:

    // ===========================================================================
    // EmptyGroup and not implemented Groups
    // ===========================================================================
    template<class U, class V>
    bool _create(const rapidjson::Document &doc, U &type, Type2Type<V>){
        (void)type;
        (void)doc;
        assert(false); // Implementation missing for group U::Group!
        return false;
    }

    // ===========================================================================
    // Point32Group
    // ===========================================================================
    template<class U>
    bool _create(const rapidjson::Document &doc, U &type, Type2Type<MessageGroups::Point32Group>){
        using namespace ROSBridge;
        bool ret = JsonMethodes::GeometryMsgs::Point32::fromJson(doc, type);
        assert(ret);
        return ret;
    }

    // ===========================================================================
    // GeoPointGroup
    // ===========================================================================
    template<class U>
    bool _create(const rapidjson::Document &doc, U &type, Type2Type<MessageGroups::GeoPointGroup>){
        using namespace ROSBridge;
        bool ret = JsonMethodes::GeographicMsgs::GeoPoint::fromJson(doc, type);
        assert(ret);
        return ret;
    }

    // ===========================================================================
    // PolygonGroup
    // ===========================================================================
    template<class U>
    bool _create(const rapidjson::Document &doc, U &type, Type2Type<MessageGroups::PolygonGroup>){
        using namespace ROSBridge;
        bool ret = JsonMethodes::GeometryMsgs::Polygon::fromJson(doc, type);
        assert(ret);
        return ret;
    }

    // ===========================================================================
    // PolygonStampedGroup
    // ===========================================================================
    template<class U>
    bool _create(const rapidjson::Document &doc, U &type, Type2Type<MessageGroups::PolygonStampedGroup>){
        using namespace ROSBridge;
        bool ret = JsonMethodes::GeometryMsgs::PolygonStamped::fromJson(doc, type);
        assert(ret);
        return ret;
    }

    // ===========================================================================
    // PolygonArrayGroup
    // ===========================================================================
    template<class U>
    bool _create(const rapidjson::Document &doc, U &type, Type2Type<MessageGroups::PolygonArrayGroup>){
        using namespace ROSBridge;
        bool ret = JsonMethodes::JSKRecognitionMsgs::PolygonArray::fromJson(doc, type);
        assert(ret);
        return ret;
    }

    // ===========================================================================
    // ProgressGroup
    // ===========================================================================
    template<class U>
    bool _create(const rapidjson::Document &doc, U &type, Type2Type<MessageGroups::ProgressGroup>){
        using namespace ROSBridge;
        bool ret = JsonMethodes::NemoMsgs::Progress::fromJson(doc, type);
        assert(ret);
        return ret;
    }

    // ===========================================================================
    // HeartbeatGroup
    // ===========================================================================
    template<class U>
    bool _create(const rapidjson::Document &doc, U &type, Type2Type<MessageGroups::HeartbeatGroup>){
        using namespace ROSBridge;
        bool ret = JsonMethodes::NemoMsgs::Heartbeat::fromJson(doc, type);
        assert(ret);
        return ret;
    }


};

} // end namespace ros_bridge
