//CUPPA:include=+
//CUPPA:include=-
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestAssert.h>

//CUPPA:namespace=+
//CUPPA:namespace=-

#include <akaxiso/akaxiso.h>
#include <akaxiso/content_model.h>
#include <iostream>

namespace {

  struct SeqSample {
    short test_;
  };

  struct SeqSampleLeaf : public aka2::sequence<SeqSample, SeqSampleLeaf> {
    void model() {
      member("test", &SeqSample::test_);
    }
  };

  struct SeqSample1 {
    short test_;
  };

  struct SeqSample1Leaf : public aka2::sequence<SeqSample1, SeqSample1Leaf> {
    void model() {
      member("test", &SeqSample1::test_);
    }
  };

  typedef std::list<aka2::item> ChoiceSample;

  typedef std::list<std::string> fixedList;

  typedef aka2::fixed_sequential_array<fixedList, xiso::leaf<std::string> > fixedListLeaf;

  struct ChoiceSampleLeaf : public aka2::sequential_choice<ChoiceSample, ChoiceSampleLeaf> {
    void model() {
      occurrence(1, 25);
      item("sample_sequence", SeqSampleLeaf());
      item("&model", SeqSampleLeaf());
      fixed_item("fixed_item", "fixed_string", xiso::leaf<std::string>());
      fixed_array("fixed_list", "fixed_list_string", fixedListLeaf(), 
		  0, aka2::unbounded);
    }
  };
}



class choiceTest : public CppUnit::TestFixture {
  CPPUNIT_TEST_SUITE(choiceTest);
//CUPPA:suite=+
  CPPUNIT_TEST(test_appendElement);
  CPPUNIT_TEST(test_appendWrongElement);
  CPPUNIT_TEST(test_insert_fixed_item);
  CPPUNIT_TEST(test_insert_wrong_fixed_item);
  CPPUNIT_TEST(test_equality);
  CPPUNIT_TEST(test_equality1);
  CPPUNIT_TEST(test_equality2);
  CPPUNIT_TEST(test_equalityNotEqual1);
  CPPUNIT_TEST(test_equalityNotEqual2);
  CPPUNIT_TEST(test_copy);
  CPPUNIT_TEST(test_replicate); 
  CPPUNIT_TEST(test_serialize);
  CPPUNIT_TEST(test_parse);
  CPPUNIT_TEST(test_occurrence);
  CPPUNIT_TEST(test_occurrence1);
  CPPUNIT_TEST(test_occurrence2);
  CPPUNIT_TEST(test_wrongDocument);
//CUPPA:suite=-
  CPPUNIT_TEST_SUITE_END();
private:

  // your stuff...

public:

  virtual void setUp() {
    aka2::initialize();
    aka2::doctype("choice_test", ChoiceSampleLeaf());
  }

  virtual void tearDown(){
    aka2::uninitialize();
  }

  static void setValue(ChoiceSample &cho, int numitems) {
    ChoiceSampleLeaf::moc moc(cho);
    for (int i = 0; i < numitems; ++i) {
      SeqSample seq;
      seq.test_ = 10;
      moc.push_back(seq, "sample_sequence");
      moc.push_back_fixed_item("fixed_item");
    }
  }

  static ChoiceSample createSample(int numitems) {
    ChoiceSample tks;
    setValue(tks, numitems);
    return tks;
  }


//CUPPA:decl=+
  void test_appendElement() {
    ChoiceSample cho = createSample(0);
    SeqSample seq;
    ChoiceSampleLeaf::moc moc(cho);
    moc.push_back(seq, "sample_sequence");
  }

  void test_appendWrongElement() {
    ChoiceSample cho1 = createSample(0);
    SeqSample1 seq;

    bool exception_caught = false;
    try {
      ChoiceSampleLeaf::moc moc(cho1);
      moc.push_back(seq, std::string("sample_sequence__"));
    }
    catch (...) {
      exception_caught = true;
    }
    CPPUNIT_ASSERT_MESSAGE("Append of wrong element unexpectedly-succeeded.", exception_caught);
  }

  void test_insert_fixed_item() {
    ChoiceSample cho;
    ChoiceSampleLeaf::moc moc(cho);
    moc.push_back_fixed_item("fixed_item");
  }

  void test_insert_wrong_fixed_item() {
    ChoiceSample cho;
    ChoiceSampleLeaf::moc moc(cho);
    bool thrown = false;
    try {
      moc.push_back("fixed_item", "wrong_value");
    }
    catch ( ... ) {
      thrown = true;
    }
    CPPUNIT_ASSERT_MESSAGE("Append of wrong fixed element unexpectedly-succeeded.", thrown);
  }


  void test_equality() {
    ChoiceSample cho1 = createSample(10);
    ChoiceSample cho2 = createSample(10);
    bool isEqual = aka2::equals(cho1, cho2, ChoiceSampleLeaf());
    CPPUNIT_ASSERT_MESSAGE("is_equal_to() failed.", isEqual);
  }

  void test_equality1() {
    ChoiceSample cho1 = createSample(10);
    ChoiceSample cho2 = createSample(10);
    ChoiceSampleLeaf::moc moc(cho1);
    moc.push_back(fixedList(), "fixed_list");
    bool isEqual = aka2::equals(cho1, cho2, ChoiceSampleLeaf());
    CPPUNIT_ASSERT_MESSAGE("is_equal_to() failed.", isEqual);
  }

  void test_equality2() {
    ChoiceSample cho1 = createSample(10);
    ChoiceSample cho2 = createSample(10);
    ChoiceSampleLeaf::moc moc(cho1);

    fixedList fl; fl.push_back(std::string("sample"));
    moc.push_back(fl, "fixed_list");

    bool isEqual = aka2::equals(cho1, cho2, ChoiceSampleLeaf());
    CPPUNIT_ASSERT_MESSAGE("is_equal_to() failed.", !isEqual);
  }


  void test_equalityNotEqual1() {
    ChoiceSample cho1 = createSample(10);
    ChoiceSample cho2 = createSample(9);
    bool isEqual = aka2::equals(cho1, cho2, ChoiceSampleLeaf());
    CPPUNIT_ASSERT_MESSAGE("isEqualTo failed.", !isEqual);
  }

  void test_equalityNotEqual2() {
    ChoiceSample cho1 = createSample(1);
    ChoiceSample cho2 = createSample(1);

    ChoiceSample::iterator it = cho1.begin();

    bool res = aka2::item_of(*it, "sample_sequence");
    CPPUNIT_ASSERT_MESSAGE("item_of<> failed.", res);

    SeqSample &seq = aka2::item_cast<SeqSample>(*it);
    seq.test_ = 1000;

    bool is_equal = aka2::equals(cho1, cho2, ChoiceSampleLeaf());

    CPPUNIT_ASSERT_MESSAGE("equals failed.", !is_equal);
  }

  void test_copy() {
    ChoiceSample cho1 = createSample(10);
    ChoiceSample cho2 = createSample(0);
    aka2::copy_element(cho2, cho1, ChoiceSampleLeaf());
    bool is_equal = aka2::equals(cho2, cho1, ChoiceSampleLeaf());
    CPPUNIT_ASSERT_MESSAGE("isEqualTo failed.", is_equal);
  }

  void test_replicate() {
    ChoiceSample cho1 = createSample(10);
    ChoiceSample *cho2 = aka2::replicate_element(cho1, ChoiceSampleLeaf());
    bool is_equal = aka2::equals(cho1, *cho2, ChoiceSampleLeaf());
    CPPUNIT_ASSERT_MESSAGE("isEqualTo failed.", is_equal);
    delete cho2;
  }

  void test_serialize() {
    ChoiceSample cho;
    SeqSample seq;
    seq.test_ = 0;
    
    ChoiceSampleLeaf::moc moc(cho);
    moc.push_back(seq, "sample_sequence");
    moc.push_back(seq, "&model");

    std::ostringstream ostm;
    aka::xml_serializer ser;
    ser.serialize(cho, "choice_test", ostm);

    //std::cout << ostm.str() << std::endl;

  }

  void test_parse() {
    ChoiceSample cho;
    setValue(cho, 10);
    SeqSample seq;
    seq.test_ = 0;
    ChoiceSampleLeaf::moc moc(cho);
    fixedList fl; fl.push_back(std::string("sample"));
    moc.push_back(fl, "fixed_list");
    moc.push_back(seq, "&model");
    
    std::ostringstream ostm;
    aka2::xml_serializer ser;
    ser.serialize(cho, "choice_test", ostm);

    //std::cout << ostm.rdbuf()->str();

    aka2::yggxml_parser parser;
    aka2::document doc;

    try {
     doc = parser.parse(ostm.rdbuf()->str());
    }
    catch ( const std::exception &e) {
      std::cerr << e.what() << std::endl;
      throw;
    }

    bool res = aka2::document_of(doc, "choice_test");
    CPPUNIT_ASSERT_MESSAGE("Parsed document name is wrong.", res);

    bool is_equal = aka2::equals(*aka2::root_cast<ChoiceSample>(doc),
				 cho, ChoiceSampleLeaf());
    if (!is_equal) {
      ser.serialize(doc, ostm);
      std::cout << ostm.rdbuf()->str();
    }

    CPPUNIT_ASSERT_MESSAGE("isEqualTo Failed.", is_equal);
   }

   void test_occurrence() {
     ChoiceSample cho;
     setValue(cho, 30);

     std::ostringstream ostm;
     aka2::xml_serializer ser;
     ser.serialize(cho, "choice_test", ostm);

     aka2::xml_parser parser;

     bool error_found = false;
     try {
       aka2::document doc = parser.parse(ostm.rdbuf()->str());
     }
     catch ( const aka2::parse_error & ) {
       error_found = true;
     }
     CPPUNIT_ASSERT_MESSAGE("Parse error is not reported.", error_found);
   }


   void test_occurrence1() {
     ChoiceSample cho;
     setValue(cho, 0);

     std::ostringstream ostm;
     aka2::xml_serializer ser;
     ser.serialize(cho, "choice_test", ostm);

     aka2::xml_parser parser;

     bool error_found = false;
     try {
       aka2::document doc = parser.parse(ostm.rdbuf()->str());
     }
     catch ( const aka2::parse_error & ) {
       error_found = true;
     }
     CPPUNIT_ASSERT_MESSAGE("Parse error is not reported.", error_found);
   }


   void test_occurrence2() {
     ChoiceSample cho;
     setValue(cho, 2);

     std::ostringstream ostm;
     aka2::xml_serializer ser;
     ser.serialize(cho, "choice_test", ostm);

     aka2::xml_parser parser;

     bool error_found = false;
     try {
       aka2::document doc = parser.parse(ostm.rdbuf()->str());
     }
     catch ( const aka2::parse_error & ) {
       error_found = true;
     }
     CPPUNIT_ASSERT_MESSAGE("Parse error is reported.", !error_found);
   }


   void test_wrongDocument() {
     std::string docstr = 
       "<?xml version=\"1.0\"?>"
       "<choice_test>"
       "  <dummytoken>token</dummytoken>"
       "</choice_test>";
     bool error_found = false;
     aka2::xml_parser parser;
     try {
       aka2::document doc = parser.parse(docstr);
     }
     catch ( const aka2::parse_error & ) {
       error_found = true;
     }
     CPPUNIT_ASSERT_MESSAGE("Parse error is not reported.", error_found);
   }
//CUPPA:decl=-
};

//CUPPA:impl=+
//CUPPA:impl=-

CPPUNIT_TEST_SUITE_REGISTRATION(choiceTest);
