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

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

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


namespace {

  struct SeqSample {
    SeqSample() : ptrlong_(0){}
    double dbl_;
    float float_;
    long long_;
    short short_;
    std::string string_;
    long *ptrlong_;
  };

  class SeqSampleLeaf : public aka::sequence<SeqSample, SeqSampleLeaf> {
  public:
    void model() {
      member("double", &SeqSample::dbl_);
      member("float", &SeqSample::float_);
      member("long", &SeqSample::long_);
      member("short", &SeqSample::short_);
      member("string", &SeqSample::string_);
      ptrmember("ptrlong", &SeqSample::ptrlong_).required(false);
      fixed_member<std::string>("fixed_string", "fixed_value");
      fixed_member<std::string>("fixed_string2", "fixed_value2", xiso::leaf<std::string>());
    }
  };

  struct SeqSample1 {  };
  struct SeqSample1Leaf : public aka::sequence<SeqSample1, SeqSample1Leaf> {
    void model() {
    }
  };

  typedef std::list<long> dummyList;
  struct dummyListLeaf : public aka::sequential_array<dummyList, dummyListLeaf> {  };
  struct EmptySequence {
    dummyList list_;
  };
  struct EmptySequenceLeaf : public aka::sequence<EmptySequence, EmptySequenceLeaf> {
    void model() {
      member("dummy", &EmptySequence::list_, dummyListLeaf()).occurence(0, aka::unbounded);
    }
  };

}


class sequenceTest : public CppUnit::TestFixture {
  CPPUNIT_TEST_SUITE(sequenceTest);
//CUPPA:suite=+
  CPPUNIT_TEST(test_equality0);
  CPPUNIT_TEST(test_equality1);
  CPPUNIT_TEST(test_serialize);
  CPPUNIT_TEST(test_serialize_empty_member_array);
  CPPUNIT_TEST(test_parse);
  CPPUNIT_TEST(test_copy);
  CPPUNIT_TEST(test_replicate);
//    CPPUNIT_TEST_EXCEPTION(test_wrongSequence0, aka::parseexception);
//    CPPUNIT_TEST_EXCEPTION(test_wrongSequence1, aka::parseexception);
//CUPPA:suite=-
  CPPUNIT_TEST_SUITE_END();
private:

//    // your stuff...

public:

  virtual void setUp() { 
//     XMLPlatformUtils::Initialize();
    aka::initialize();
    aka::doctype("sequence_test", SeqSampleLeaf());
    aka::doctype("empty_array", EmptySequenceLeaf());
  }

  virtual void tearDown(){
    aka::uninitialize();
//     XMLPlatformUtils::Terminate();
  }


  static void setValues(SeqSample &seq){
    seq.dbl_ = 0.2253;
    seq.float_ = 0.225f;
    seq.long_ = 1638453;
    seq.short_ = 32600;
    seq.string_ = "Test String for a member in sequence.";
  }

//CUPPA:decl=+
  void test_equality0(){
    SeqSample seq;
    SeqSample seq1;

    setValues(seq);
    setValues(seq1);

    bool equal = aka::equals(seq, seq1, SeqSampleLeaf());
    CPPUNIT_ASSERT_MESSAGE("Equality test failed.", equal);
  }

  void test_equality1(){
    SeqSample seq;
    SeqSample seq1;

    setValues(seq);
    setValues(seq1);
    seq.dbl_ = 0;

    bool equal = aka::equals(seq, seq1, SeqSampleLeaf());
    CPPUNIT_ASSERT_MESSAGE("Equality test failed.", !equal);
  }


  void test_copy() {
    SeqSample seq;
    SeqSample seq1;

    setValues(seq);
    aka::copy_element(seq1, seq, SeqSampleLeaf());
    bool is_equal = aka::equals(seq, seq1, SeqSampleLeaf());
    CPPUNIT_ASSERT_MESSAGE("Copy failed.", is_equal);
  }

  void test_replicate(){
    SeqSample seq;

    setValues(seq);
    SeqSample *seq1 = aka::replicate_element(seq, SeqSampleLeaf());
    bool is_equal = aka::equals(seq, *seq1, SeqSampleLeaf());
    delete seq1;

    CPPUNIT_ASSERT_MESSAGE("Replicate failed.", is_equal);
  }

//   void test_compare_different_type(){
//     SeqSample seq;
//     SeqSample1 seq1;
    
//     bool exception_caught = false;
    
//     try {
//       element_equals(&seq, &seq1, SeqSampleLeaf::dispatcher_);
//     }
//     catch (... ) {
//       exception_caught = true;
//     }
//     CPPUNIT_ASSERT_MESSAGE("Exception 'invalid_argument' not thrown", exception_caught);
//   }


  void test_serialize() {
    std::ostringstream ostm;
    aka::xml_serializer ser;
    
    SeqSample root;
    setValues(root);
    ser.serialize(root, "sequence_test", ostm);
//      std::cout << ostm.rdbuf()->str() << std::endl;
  }


  void test_serialize_empty_member_array() {
    std::ostringstream ostm;
    aka::xml_serializer ser;
    
    EmptySequence root;
    ser.serialize(root, "empty_array", ostm);
//      std::cout << ostm.rdbuf()->str() << std::endl;
  }

  void test_parse() {
    std::ostringstream ostm;
    aka::xml_serializer ser;
    
    SeqSample root;
    setValues(root);
    ser.serialize(root, "sequence_test", ostm);
    //    std::cout << ostm.rdbuf()->str() << std::endl;

    aka::yggxml_parser parser;

    std::string errMsg;
    aka::document parsed;
    try {
      parsed = parser.parse(ostm.rdbuf()->str());
    }
    catch (const std::exception &e) {
      errMsg = e.what();
    }

    CPPUNIT_ASSERT_MESSAGE(errMsg.c_str(), errMsg.empty());

    CPPUNIT_ASSERT_MESSAGE("wrong document name. ", 
			   aka::document_of(parsed, "sequence_test"));


    SeqSample *seq = aka::root_cast<SeqSample>(parsed);
    bool is_equal = aka::equals(*seq, root, SeqSampleLeaf());
    CPPUNIT_ASSERT_MESSAGE("Parsed document is not the same as original.", is_equal);
  }

//    void test_wrongSequence0() {
    
//      adom::string doc = 
//        "<?xml version=\"1.0\"?>"
//        "<MyDoc>"
//        "  <double>0.2</double>"
//        "  <long>2</long>" // Should be <float>
//        "  <float>3.0</float>"
//        "  <short>2</short>"
//        "  <string>Test</string>"
//        "</MyDoc>";


//      adom::XMLParser parser;
//      doc1_ = static_cast<MyDoc*>(parser.parse(doc.c_str()));
//    }


//    void test_wrongSequence1() {
    
//      adom::string doc = 
//        "<?xml version=\"1.0\"?>"
//        "<MyDoc>"
//        "  <double>0.2</double>"
//        "  <float>3.0</float>"
//        "  <long>2</long>"
//        "  <short>2</short>"
//        // Lack of element.
//        //      "  <string>Test</string>"
//        "</MyDoc>";

//      adom::XMLParser parser;
//      doc1_ = static_cast<MyDoc*>(parser.parse(doc.c_str()));
//    }


//CUPPA:decl=-
};

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

CPPUNIT_TEST_SUITE_REGISTRATION(sequenceTest);
