#include <iostream>
#include <iomanip>

#include "JLang/JBool.hh"
#include "JLang/JConversion.hh"

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


namespace {

  using namespace JPP;
  
  
  /**
   * Write bool to output.
   *
   * \param  out        output stream
   * \param  object     bool
   * \return            output stream
   */
  template<bool value>
  std::ostream& operator<<(std::ostream& out, const JBool<value>& object)
  {
    return out << object.value;
  }

  
  /**
   * Test overload resolution.
   *
   * \param  option     true
   * \return            true
   */
  inline bool invert(const JBool<true>& option)
  {
    return false;
  }


  /**
   * Test overload resolution.
   *
   * \param  option     false
   * \return            false
   */
  inline bool invert(const JBool<false>& option)
  {
    return true;
  }
}

/**
 * Operand macro.
 *
 * \param  OP         logical operator
 * \param  A          first  value
 * \param  B          second value
 * \return            result
 */
#define C_OPERAND(OP, A, B) OP< JBool<A>, JBool<B> >::value

/**
 * Switch macro.\n
 * The result equals first option if value true; else second option.
 *
 * \param  VALUE      value
 * \param  A          first  option
 * \param  B          second option
 * \return            result
 */
#define C_SWITCH(VALUE, A, B) VALUE.c_switch<A,B>()


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

  int debug;

  try {

    JParser<> zap("Example program to test boolean algebra at compile time.");

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

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

  using namespace JPP;
  
  const bool X = true;
  const bool Y = false;

  ASSERT(JBool<X>::value == true);
  ASSERT(JBool<Y>::value == false);
  
  ASSERT(C_OPERAND(AND, X, X) == true);
  ASSERT(C_OPERAND(AND, X, Y) == false);
  ASSERT(C_OPERAND(OR,  X, X) == true);
  ASSERT(C_OPERAND(OR,  Y, Y) == false);
  ASSERT(C_OPERAND(OR,  X, Y) == true);
  ASSERT(C_OPERAND(XOR, X, X) == false);
  ASSERT(C_OPERAND(XOR, X, Y) == true);
  
  ASSERT(invert(JBool<true>())  == false);
  ASSERT(invert(JBool<false>()) == true);

  JBool<true> option;
  
  ASSERT(C_SWITCH(option,X,Y));
  ASSERT(!C_SWITCH(option,Y,X));

  ASSERT(!C_SWITCH(option.c_not(),X,Y));
  ASSERT(C_SWITCH(option.c_not(),Y,X));

  return 0;
}