#ifndef __JLANG__JOBJECTDEMULTIPLEXER__
#define __JLANG__JOBJECTDEMULTIPLEXER__

#include "JLang/JObjectIterator.hh"
#include "JLang/JObjectOutput.hh"
#include "JLang/JTypeList.hh"
#include "JLang/JNullType.hh"


/**
 * \author mdejong
 */

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

namespace JLANG {

  /**
   * Auxiliary class for demultiplexing object outputs.
   *
   * The template argument <tt>JDerived_t</tt> refers to a list of 
   * multiple object outputs which are processed in parallel.\n
   * The template argument <tt>JBase_t</tt> refers to a single object output 
   * which provides for a common interface to the multiple object outputs.
   *
   * This class implements the JLANG::JObjectOutput interface.
   */
  template<class JBase_t, class JDerived_t>
  struct JObjectDemultiplexer :
    public virtual JObjectOutput<JBase_t>
  {
    /**
     * Constructor.
     *
     * \param  output     object output
     */
    JObjectDemultiplexer(JObjectOutput<JDerived_t>& output) :
      out(output)
    {}


    /**
     * Object output.
     *
     * \param  object     object
     * \return            true if OK; else false
     */
    virtual bool put(const JBase_t& object) override 
    {
      const JDerived_t* p = dynamic_cast<const JDerived_t*>(&object);

      if (p != NULL)
	return out.put(*p);
      else
	return false;
    }


    /**
     * Pipe operator.
     *
     * \param  in         object demultiplexer
     * \param  out        object output
     */
    friend inline void operator|(JObjectIterator<JBase_t>& in, JObjectDemultiplexer& out)
    {
      while (in.hasNext() && out.put(*in.next())) {}
    }

  private:
    JObjectOutput<JDerived_t>& out;
  };

  
  /**
   * Template specialisation of JObjectDemultiplexer for multiple object outputs.
   * Implementation of object demultiplexing for multiple data types.
   *
   * This class recursively extends the JLANG::JObjectDemultiplexer class 
   * for all data types by deriving from:
   *  - JObjectDemultiplexer<JBase_t, JHead_t>; and
   *  - JObjectDemultiplexer<JBase_t, JTail_t>.
   *
   * This class implements the JLANG::JObjectOutput interface.
   */
  template<class JBase_t, class JHead_t, class JTail_t>
  struct JObjectDemultiplexer<JBase_t, JTypeList<JHead_t, JTail_t> > :
    public JObjectDemultiplexer<JBase_t, JHead_t>,
    public JObjectDemultiplexer<JBase_t, JTail_t>,
    public virtual JObjectOutput<JBase_t>
  {
    /**
     * Constructor.
     *
     * \param  output     object output
     */
    template<class T>
    JObjectDemultiplexer(T& output) :
      JObjectDemultiplexer<JBase_t, JHead_t>(output),
      JObjectDemultiplexer<JBase_t, JTail_t>(output)
    {}


    /**
     * Object output.
     *
     * \param  object     object
     * \return            true if OK; else false
     */
    virtual bool put(const JBase_t& object) override 
    {
      return (static_cast< JObjectDemultiplexer<JBase_t, JHead_t> >(*this).put(object) ||
	      static_cast< JObjectDemultiplexer<JBase_t, JTail_t> >(*this).put(object));
    }


    /**
     * Pipe operator.
     *
     * \param  in         object demultiplexer
     * \param  out        object output
     */ 
    friend inline void operator|(JObjectIterator<JBase_t>& in, JObjectDemultiplexer& out)
    {
      while (in.hasNext() && out.put(*in.next())) {}
    }
  };

    
  /**
   * Terminator class of recursive JObjectDemultiplexer class.
   */
  template<class JBase_t, class JHead_t>
  struct JObjectDemultiplexer<JBase_t, JTypeList<JHead_t, JNullType> > :
    public JObjectDemultiplexer<JBase_t, JHead_t>
  {
    /**
     * Constructor.
     *
     * \param  output     object output
     */
    JObjectDemultiplexer(JObjectOutput<JHead_t>& output) :
      JObjectDemultiplexer<JBase_t, JHead_t>(output)
    {}
  };
}

#endif