#ifndef __JLANG__JOBJECTOUTPUT__
#define __JLANG__JOBJECTOUTPUT__

#include "JLang/JTypeList.hh"
#include "JLang/JNullType.hh"
#include "JLang/JObjectIterator.hh"
#include "JLang/JAccessible.hh"
#include "JLang/JSingleton.hh"


/**
 * \author mdejong
 */

namespace JLANG {}
namespace JPP { using namespace JLANG; }

namespace JLANG {

  /**
   * Template interface of object output for single data type.
   */
  template<class T>
  class JObjectOutput {
  protected:
    /**
     * Default constructor.
     */
    JObjectOutput()
    {}


  public:
    /**
     * Virtual destructor.
     */
    virtual ~JObjectOutput()
    {}


    /**
     * Object output.
     *
     * \param  object     object
     * \return            true if OK; else false
     */
    virtual bool put(const T& object) = 0;


    /**
     * Copy from object iterator.
     * 
     * \param  out        object output
     * \param  in         object iterator
     * \return            object output
     */
    friend inline JObjectOutput<T>& operator<<(JObjectOutput  <T>& out, 
					       JObjectIterator<T>& in)
    {
      while (in.hasNext()) {

	const T* p = in.next();
	
	if (p != NULL)
	  out.put(*p);
	else
	  break;
      }
      
      return out;
    }
  };
  

  /**
   * Implementation of object output for multiple data types.
   *
   * This class recursively defines the JObjectOutput interface
   * for all data types by deriving from:
   *  - JObjectOutput<JHead_t>; and 
   *  - JObjectOutput<JTail_t>.
   */
  template<class JHead_t, class JTail_t>
  class JObjectOutput< JTypeList<JHead_t, JTail_t> > :
    public virtual JObjectOutput<JHead_t>,
    public virtual JObjectOutput<JTail_t>
  {
  public:

    typedef JTypeList<JHead_t, JTail_t>  typelist;

    using JObjectOutput<JHead_t>::put;
    using JObjectOutput<JTail_t>::put;


    /**
     * Copy from object iterator.
     *
     * Note that all data types of the output are copied from the input.
     *
     * \param  out        object output
     * \param  in         object iterator
     * \return            object output
     */
    template<class JInputIterator_t>
    friend inline JObjectOutput& operator<<(JObjectOutput<typelist>& out, JInputIterator_t& in)
    {
      static_cast<JObjectOutput<JHead_t>&>(out) << static_cast<JObjectIterator<JHead_t>&>(in);
      static_cast<JObjectOutput<JTail_t>&>(out) << in;
      
      return out;
    }
  };
  
  
  /**
   * Terminator class of recursive JObjectOutput class.
   */
  template<class JHead_t>
  class JObjectOutput< JTypeList<JHead_t, JNullType> > :
    public virtual JObjectOutput<JHead_t>
  {
  public:

    using JObjectOutput<JHead_t>::put;
  };


  /**
   * Interface for object output with named access.
   */
  template<class T>
  class JAccessibleObjectOutput :
    public virtual JObjectOutput<T>,
    public virtual JAccessible
  {};


  /**
   * Implementation of null output for single data type.
   */
  template<class T>
  struct JNullOutput :
    public JSingleton< JNullOutput<T> >,
    public virtual JObjectOutput<T>
  {
    /**
     * Object output.
     *
     * \param  object     object
     * \return            false
     */
    virtual bool put(const T& object) override
    {
      return false;
    }
  };
  

  /**
   * Implemenatation of null output for multiple data types.
   *
   * This class recursively implements the JLANG::JObjectOutput interface
   * for all data types by deriving from:
   *  - JNullOutput<JHead_t>; and 
   *  - JNullOutput<JTail_t>.
   */
  template<class JHead_t, class JTail_t>
  struct JNullOutput< JTypeList<JHead_t, JTail_t> > :
    public JSingleton< JTypeList<JHead_t, JTail_t> >,
    public virtual JNullOutput<JHead_t>,
    public virtual JNullOutput<JTail_t>
  {};


  /**
   * Terminator class of recursive JNullOutput class.
   */
  template<class JHead_t>
  struct JNullOutput< JTypeList<JHead_t, JNullType> > :
    public virtual JNullOutput<JHead_t>
  {};


  /**
   * Implementation for null output with null access.
   */
  template<class T>
  struct JNullAccessibleOutput :
    public virtual JAccessibleObjectOutput<T>,
    public virtual JNullOutput<T>,
    public virtual JNullAccess
  {};
}

#endif