#ifndef __JDB__JDATABASEOBJECTITERATOR__
#define __JDB__JDATABASEOBJECTITERATOR__

#include <string>

#include "JLang/JAbstractObjectIterator.hh"
#include "JLang/JTypeList.hh"
#include "JLang/JSingleton.hh"

#include "JDB/JDB.hh"
#include "JDB/JSelector.hh"

#include "dbclient/KM3NeTDBClient.h"


/**
 * \author mdejong
 */

namespace JDATABASE {}
namespace JPP { using namespace JDATABASE; }

namespace JDATABASE {

  using JLANG::JAbstractObjectIterator;
  using JLANG::JObjectIterator;
  using JLANG::JTypeList;
  using JLANG::JNullType;
  using JLANG::JSingleton;
  using KM3NeT::DB::ResultSet;

  /**
   * Auxiliary class for invalid result set.
   */
  struct JNullResultSet :
    public ResultSet,
    public JSingleton<JNullResultSet>
  {
    virtual bool Next() override { return false; }
    virtual unsigned int FieldCount() const override { return 0; }
    virtual std::string& FieldName(unsigned int i) const override { static std::string buffer; return buffer; }
    virtual std::string& GetString(unsigned int i) const override { static std::string buffer; return buffer; }
    virtual void Close() override {}
  };


  /**
   * Object iteration from database.
   * This class implements the JLANG::JObjectIterator interface.
   */
  template<class T>
  class JDatabaseObjectIterator :
    public JAbstractObjectIterator<T>
  {
  public:
    /**
     * Constructor.
     *
     * \param  selection   selection
     */
    JDatabaseObjectIterator(const JSelector& selection) :
      rs(getResultSet(getTable<T>(), selection))
    {
      JDBReader<T>::set(selection);
    }


    /**
     * Constructor.
     *
     * This constructor is used to select a table by name.
     *
     * \param  query       query / table name
     * \param  selection   selection
     */
    JDatabaseObjectIterator(const std::string& query, const JSelector& selection) :
      rs(query == getTable<T>() ? getResultSet(query, selection) : JNullResultSet::getInstance())
    {
      JDBReader<T>::set(selection);
    }


    /**
     * Destructor.
     */
    ~JDatabaseObjectIterator()
    {
      //rs.Close();
    }


    /**
     * Set object.
     *
     * \param  object     reference to object to be set
     * \return            true if set; else false
     */
    virtual bool setObject(T& object) override 
    {
      return (rs >> object);
    }

  private:
    ResultSet& rs;
  };


  /**
   * Implementation of object iterator for multiple data types.
   *
   * This class recursively defines the JLANG::JObjectIterator interface
   * for all data types by deriving from:
   *  - JDatabaseObjectIterator<JHead_t>; and
   *  - JDatabaseObjectIterator<JTail_t>.
   */
  template<class JHead_t, class JTail_t>
  struct JDatabaseObjectIterator< JTypeList<JHead_t, JTail_t> > :
    public JDatabaseObjectIterator<JHead_t>,
    public JDatabaseObjectIterator<JTail_t>,
    public virtual JObjectIterator< JTypeList<JHead_t, JTail_t> >
  {
    /**
     * Constructor.
     *
     * \param  query       query / table name
     * \param  selection   selection
     */
    JDatabaseObjectIterator(const std::string& query, const JSelector& selection) :
      JDatabaseObjectIterator<JHead_t>(query, selection),
      JDatabaseObjectIterator<JTail_t>(query, selection)
    {}
  };


  /**
   * Terminator class of recursive JDatabaseObjectIterator class.
   */
  template<class JHead_t>
  struct JDatabaseObjectIterator< JTypeList<JHead_t, JNullType> > :
    public JDatabaseObjectIterator<JHead_t>
  {
    /**
     * Constructor.
     *
     * \param  query       query / table name
     * \param  selection   selection
     */
    JDatabaseObjectIterator(const std::string& query, const JSelector& selection) :
      JDatabaseObjectIterator<JHead_t>(query, selection)
    {}
  };
}

#endif