#ifndef __JSUPPORT__JDAQFILEWRITER__
#define __JSUPPORT__JDAQFILEWRITER__

#include <ostream>

#include "JIO/JWriterObjectOutput.hh"
#include "JIO/JStreamIO.hh"
#include "JDAQ/JDAQPreambleIO.hh"
#include "JLang/JAccessibleBinaryStream.hh"
#include "JLang/JObjectOutput.hh"
#include "JLang/JTypeList.hh"
#include "JLang/JNullType.hh"
#include "JLang/JObjectOutput.hh"
#include "JLang/JConversion.hh"


/**
 * \author mdejong
 */

namespace JSUPPORT {}
namespace JPP { using namespace JSUPPORT; }

namespace JSUPPORT {

  using JIO::JWriter;
  using JIO::JWriterObjectOutput;
  using JIO::JStreamWriter;
  using KM3NETDAQ::JDAQPreamble;
  using JLANG::JAccessibleBinaryOutputStream;
  using JLANG::JObjectOutput;
  using JLANG::JAccessibleObjectOutput;
  using JLANG::JNullOutput;
  using JLANG::JTypeList;
  using JLANG::JNullType;


  /**
   * Auxiliary class for DAQ object writing to binary stream.
   */
  template<class T, bool = JLANG::JConversion<T,JDAQPreamble>::is_derived>
  class JDAQWriterObjectOutput;


  /**
   * Implementation of object output for DAQ compatible data types.
   *
   * This class implements the JLANG::JObjectOutput interface.
   */
  template<class T>
  class JDAQWriterObjectOutput<T,true> :
    public JWriterObjectOutput<T>
  {
  protected:
    /**
     * Constructor.
     *
     * \param  writer     JWriter output
     */
    JDAQWriterObjectOutput(JWriter& writer) :
      JWriterObjectOutput<T>(writer)
    {}
  };


  /**
   * Implementation of object output for DAQ incompatible data types.
   *
   * This class implements the JLANG::JObjectOutput interface by doing nothing.
   */
  template<class T>
  class JDAQWriterObjectOutput<T,false> :
    public JNullOutput<T>
  {
  protected:
    /**
     * Constructor.
     *
     * \param  writer     JWriter output
     */
    JDAQWriterObjectOutput(JWriter& writer)
    {}
  };


  /**
   * Implementation of object output to binary file for single data type.
   *
   * This class implements the JLANG::JObjectOutput interface.
   */
  template<class T>
  class JDAQWriter :
    public JDAQWriterObjectOutput<T>
  {
  protected:
    /**
     * Constructor.
     *
     * \param  writer     JWriter output
     */
    JDAQWriter(JWriter& writer) :
      JDAQWriterObjectOutput<T>(writer)
    {}
  };


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

  {
  protected:
    /**
     * Constructor.
     *
     * \param  writer     JWriter output
     */
    JDAQWriter(JWriter& writer) :
      JDAQWriter<JHead_t>(writer),
      JDAQWriter<JTail_t>(writer)
    {}
  };


  /**
   * Terminator class of recursive JDAQWriter class.
   */
  template<class JHead_t>
  class JDAQWriter< JTypeList<JHead_t, JNullType> > :
    public JDAQWriter<JHead_t>
  {
  protected:
    /**
     * Constructor.
     *
     * \param  writer     JWriter output
     */
    JDAQWriter(JWriter& writer) :
      JDAQWriter<JHead_t>(writer)
    {}
  };


  /**
   * Object(s) writing to binary file (i.e.\ .dat).
   *
   * This class implements the JLANG::JAccessibleObjectOutput interface.
   */
  template<class T>
  class JDAQFileWriter :
    public JAccessibleBinaryOutputStream,
    public JStreamWriter,
    public JDAQWriter<T>,
    public JAccessibleObjectOutput<T>
  {
  public:
    /**
     * Default constructor.
     */
    JDAQFileWriter() :
      JAccessibleBinaryOutputStream(),
      JStreamWriter(static_cast<std::ostream&>(*this)),
      JDAQWriter<T>(static_cast<JWriter&>     (*this))
    {}
  };
}

#endif