#include <string>
#include <iostream>
#include <iomanip>
#include <vector>

#include "JLang/JSTDObjectReader.hh"
#include "JLang/JSTDObjectWriter.hh"
#include "JLang/JObjectMultiplexer.hh"
#include "JLang/JObjectDemultiplexer.hh"

#include "Jeep/JParser.hh"
#include "Jeep/JMessage.hh"

namespace {

  struct __A__ {
    /**
     * Virtual destructor.
     */
    virtual ~__A__()
    {}


    /**
     * Constructor.
     *
     * \param  i      value
     */
    __A__(const int i) :
      i(i)
    {}


    /**
     * Get name.
     *
     * \return         name
     */
    virtual const char* getName() const
    {
      return "a";
    }


    /**
     * Equal operator.
     *
     * \param  first   first  object
     * \param  second  second object
     * \return         true if first and second object are equal; else false
     */
    friend inline bool operator==(const __A__& first, const __A__& second)
    {
      return first.i == second.i;
    }


    /**
     * Write object to output stream.
     *
     * \param  out     output stream
     * \param  object  object
     * \return         output stream
     */
    friend inline std::ostream& operator<<(std::ostream& out, const __A__& object)
    {
      return out << object.getName() << object.i; 
    }

    int i;
  };


  struct __B__ :
    public __A__
  {
    /**
     * Constructor.
     *
     * \param  i      value
     */
    __B__(const int i) :
      __A__(i)
    {}


    /**
     * Get name.
     *
     * \return         name
     */
    virtual const char* getName() const
    {
      return "b";
    }
  };
}


/**
 * \file
 *
 * Example program to test JLANG::JObjectMultiplexer and JLANG::JObjectDemultiplexer classes.
 * \author mdejong
 */
int main(int argc, char **argv)
{
  using namespace std;

  int         debug;

  try {

    JParser<> zap("Example program to test object multiplexing and demultiplexing.");

    zap['d'] = make_field(debug)      = 3;

    zap(argc, argv);
  }
  catch(const exception &error) {
    FATAL(error.what() << endl);
  }


  using namespace JPP;

  vector<__A__> a_in;
  vector<__B__> b_in;

  for (int i = 1; i != 4; ++i) {
    a_in.push_back(i);
    b_in.push_back(i);
  }

  if (debug >= debug_t) {
    cout << "a_in  "; copy(a_in.begin(), a_in.end(), ostream_iterator<__A__>(cout, " ")); cout << endl;
    cout << "b_in  "; copy(b_in.begin(), b_in.end(), ostream_iterator<__B__>(cout, " ")); cout << endl;
  }


  typedef JTYPELIST<__B__, __A__>::typelist  typelist;   // make sure base class is at end

  JSTDObjectReader<typelist> in;

  in.set(a_in);
  in.set(b_in);


  JSTDObjectWriter<typelist> out;

  vector<__A__> a_out;
  vector<__B__> b_out;

  out.set(a_out);
  out.set(b_out);


  JObjectMultiplexer<typelist>          multiplexer  (in);

  JObjectDemultiplexer<__A__, typelist> demultiplexer(out);


  multiplexer >> demultiplexer;

  if (debug >= debug_t) {
    cout << "a_out "; copy(a_out.begin(), a_out.end(), ostream_iterator<__A__>(cout, " ")); cout << endl;
    cout << "b_out "; copy(b_out.begin(), b_out.end(), ostream_iterator<__B__>(cout, " ")); cout << endl;
  }

  ASSERT(a_in == a_out);
  ASSERT(b_in == b_out);

  return 0;
}