/*
 * (C) Copyright 1996-2017 ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

// File Reanimator.h
// Baudouin Raoult - ECMWF Jun 96

#ifndef eckit_Reanimator_h
#define eckit_Reanimator_h

#include "eckit/serialisation/Stream.h"


//-----------------------------------------------------------------------------

namespace eckit {

//-----------------------------------------------------------------------------

class Streamable;

// -----------------------------------------------------------------------------
// The class Streamable is a abstract class for encoding/decoding objects to/from
// a Stream.
//
// For a class to be "stream-able", it must:
//
//   1- Inherit from class Streamable
//   2- Have a constructor from a Stream&
//   3- Support a encode(Stream&) const method
//
//   When encoded, it must first encode its superclass, then all its members
//   objects, and write is simple members ...
//
// For a class 'Foo' to be "reanimate-able", i.e. to be recreated from a stream
// as a first class object, it must be "stream-able" and it must have the two following
// private members:
//
//    static  ClassSpec        classSpec_;
//    static  Reanimator<Foo> reanimator_;
//
// and the two public methods to access those members:
//
//	virtual const ReanimatorBase& reanimator() const { return reanimator_; }
//	static  const ClassSpec&  classSpec()        { return classSpec_; }
//
// -----------------------------------------------------------------------------
// A ClassSpec decribes the class:
//

struct ClassSpec {
	const ClassSpec	*superClass_; // Pointer to superclass ClassSpec  (Note that
	                              // multiple inheritance is not supported.)
	const char      *name_;		  // class name
};

// in the file Foo.cc, if Foo is a direct subclass of Bar, its ClassSpec can be
// defined as:
//
// ClassSpec Foo::classSpec_ = { &Bar::ClassSpec(), "Foo", 1, };
//
// and the reanimator as
//
// Reanimator<Foo> Foo::reanimator_;
//
// -----------------------------------------------------------------------------
// A ReanimatorBase is an 'factory' used to create objects from a Stream
//

class ReanimatorBase {

	const ClassSpec&  spec_;

	virtual Streamable*  ressucitate(Stream&) const = 0;

	class NotSubClass: public Exception
    { public: NotSubClass(const std::string&, const std::string&); };

	class UnknowClass: public Exception
    { public: UnknowClass(const std::string&); };

public:

	ReanimatorBase(const ClassSpec&);
	virtual ~ReanimatorBase();
	const ClassSpec& spec() const  { return spec_; }

static Streamable*  reanimate(Stream&,const ClassSpec* = 0);
};


// -----------------------------------------------------------------------------

template<class T> class Reanimator : public ReanimatorBase {
	Streamable*  ressucitate(Stream& s) const;
public:
	Reanimator();
static T*  reanimate(Stream& s);
};


//-----------------------------------------------------------------------------

} // namespace eckit

#include "eckit/serialisation/Reanimator.cc"

#endif
