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

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

#include <iostream>

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


namespace {

  struct SeqSample {
    std::string string_;
    std::string testattr0_;
    std::string testattr1_;
  };


  struct SeqSampleLeaf : public aka::sequence<SeqSample, SeqSampleLeaf> {
    void model() {
      member("string", &SeqSample::string_);
      attribute testattr0("test0", &SeqSample::testattr0_);
      testattr0.set_default("test0_value");
      testattr0.set_required(true);
      attribute("test1", &SeqSample::testattr1_).set_default("test1_value"); // optional
      fixed_attribute<std::string>("fixed_test", "fixed_value").set_required(true);
      fixed_attribute<std::string>("fixed_test1", "fixed_value1", xiso::leaf<std::string>());
    }
  };

}


class attributeTest : public CppUnit::TestFixture {
  CPPUNIT_TEST_SUITE(attributeTest);
//CUPPA:suite=+
  CPPUNIT_TEST(test_default);
  CPPUNIT_TEST(test_equality0);
  CPPUNIT_TEST(test_equality1);
  CPPUNIT_TEST(test_copy);
  CPPUNIT_TEST(test_replicate);
  CPPUNIT_TEST(test_fixed_attribute);
  CPPUNIT_TEST(test_serialize);
  CPPUNIT_TEST(test_parse);
  CPPUNIT_TEST(test_wrong_fixed_attribute);
  CPPUNIT_TEST(test_lack_of_required_attribute);
  CPPUNIT_TEST(test_optional_attribute);
  CPPUNIT_TEST(test_optional_fixed_attribute);
//CUPPA:suite=-
  CPPUNIT_TEST_SUITE_END();
private:

//    // your stuff...

public:

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

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


  static void setValues(SeqSample &seq){
    seq.string_ = "Test String for a member in Sequence.";
    seq.testattr0_ = "test_attribute00";
    seq.testattr1_ = "test_attribute01";
  }

  void set_required_of_fixed_attribute(bool val) {
    aka::attribute_types &atypes = SeqSampleLeaf::attribute_types_;
    aka::attribute_type &atype = atypes.find(aka::qname("fixed_test"))->second;
    atype.set_required(val);
  }


//CUPPA:decl=+
  void test_default() {
    SeqSample seq;
    aka::construct_element(seq, SeqSampleLeaf());
    CPPUNIT_ASSERT_MESSAGE("Default value not set.", seq.testattr0_ == "test0_value");
    CPPUNIT_ASSERT_MESSAGE("Default value not set.", seq.testattr1_ == "test1_value");
  }

  void test_equality0(){
    SeqSample seq;
    SeqSample seq1;

    setValues(seq);
    setValues(seq1);
    bool isEqual = aka::equals(seq, seq1, SeqSampleLeaf());

    CPPUNIT_ASSERT_MESSAGE("Equality test failed.", isEqual);
  }

  void test_equality1(){
    SeqSample seq;
    SeqSample seq1;

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

  void test_copy() {
    SeqSample seq;
    SeqSample seq1;

    setValues(seq);
    
    aka::copy_element(seq1, seq, SeqSampleLeaf());
    bool isEqual = aka::equals(seq, seq1, SeqSampleLeaf());
    
    CPPUNIT_ASSERT_MESSAGE("Sequence::CopyTo failed.", isEqual);
  }

  void test_replicate(){
    SeqSample seq;

    setValues(seq);
    
    SeqSample *seq1 = aka::replicate_element(seq, SeqSampleLeaf());
    bool isEqual = aka::equals(seq, *seq1, SeqSampleLeaf());
    
    delete seq1;
    CPPUNIT_ASSERT_MESSAGE("Sequence::CopyTo failed.", isEqual);
  }

  void test_fixed_attribute() {
    const aka::attribute_types &atypes = SeqSampleLeaf::attribute_types_;
    aka::attribute_types::const_iterator it = atypes.find(aka::qname("fixed_test"));
    CPPUNIT_ASSERT_MESSAGE("fixed attribute not registered.", it != atypes.end());

    const aka::attribute_type &atype = it->second;
    CPPUNIT_ASSERT_MESSAGE("Should be required.", atype.is_required());
  }

  void test_serialize() {
    std::ostringstream ostm;
    SeqSample seq;
    setValues(seq);

    aka::xml_serializer ser;
    ser.serialize(seq, "mydoc", ostm);

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

  void test_parse() {
    std::ostringstream ostm;
    aka::xml_serializer ser;
    
    SeqSample seq;
    setValues(seq);
    ser.serialize(seq, "mydoc", ostm);
    
    aka::yggxml_parser parser;
    aka::document parsed = parser.parse(ostm.rdbuf()->str());

    CPPUNIT_ASSERT_MESSAGE("Wrong Document name", aka::document_of(parsed, "mydoc"));

    bool is_equal = aka::equals(*aka::root_cast<SeqSample>(parsed), 
				seq, SeqSampleLeaf());
    if (!is_equal) {
      ser.serialize(parsed, ostm);
      std::cout << ostm.str();
    }

    CPPUNIT_ASSERT_MESSAGE("Parsed document is not the same as original.", is_equal);
  }


  void test_wrong_fixed_attribute() {
    bool parse_error = false;
    try {
      aka::yggxml_parser parser;
      aka::document 
	parsed = parser.parse("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
			      "<mydoc fixed_test=\"changed_fixed_value\" test0=\"test_attribute00\" "
			      "test1=\"test_attribute01\">\n"
			      "<string>Test String for a member in Sequence.</string>\n"
			      "</mydoc>\n");
    }
    catch (const std::exception &) {
      parse_error = true;
    }
    CPPUNIT_ASSERT_MESSAGE("Parse error for fixed attribute not caught.", parse_error);

    set_required_of_fixed_attribute(false);
    parse_error = false;
    try {
      aka::yggxml_parser parser;
      aka::document 
	parsed = parser.parse("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
			      "<mydoc fixed_test=\"changed_fixed_value\" test0=\"test_attribute00\" "
			      "test1=\"test_attribute01\">\n"
			      "<string>Test String for a member in Sequence.</string>\n"
			      "</mydoc>\n");
    }
    catch (const std::exception &) {
      parse_error = true;
    }
    CPPUNIT_ASSERT_MESSAGE("Parse error for fixed attribute not caught.", parse_error);

  }


  void test_lack_of_required_attribute() {
    bool parse_error = false;
    try {
      aka::yggxml_parser parser;
      aka::document 
	parsed = parser.parse("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
			      "<mydoc fixed_test=\"fixed_value\" "
			      "test1=\"test_attribute01\">\n"
			      "<string>Test String for a member in Sequence.</string>\n"
			      "</mydoc>\n");
    }
    catch (const std::exception &) {
      parse_error = true;
    }
    CPPUNIT_ASSERT_MESSAGE("Parse error for fixed attribute not caught.", parse_error);
    
    set_required_of_fixed_attribute(false);
    parse_error = false;
    try {
      aka::yggxml_parser parser;
      aka::document 
	parsed = parser.parse("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
			      "<mydoc fixed_test=\"fixed_value\""
			      "test1=\"test_attribute01\">\n"
			      "<string>Test String for a member in Sequence.</string>\n"
			      "</mydoc>\n");
    }
    catch (const std::exception &) {
      parse_error = true;
    }
    CPPUNIT_ASSERT_MESSAGE("Parse error for fixed attribute not caught.", parse_error);
  }

  void test_optional_attribute() {
    aka::yggxml_parser parser;
    aka::document 
      parsed = parser.parse("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
			    "<mydoc fixed_test=\"fixed_value\" "
			    "test0=\"test_attribute0\">"
			    "<string>Test String for a member in Sequence.</string>\n"
			    "</mydoc>\n");
    SeqSample *root = aka::root_cast<SeqSample>(parsed);
    CPPUNIT_ASSERT_MESSAGE("Optional attribute does not have default value.", 
			   root->testattr1_ == "test1_value");
  }


  void test_optional_fixed_attribute() {
    bool parse_error = false;
    aka::yggxml_parser parser;
    set_required_of_fixed_attribute(false);
    try {
      aka::document 
	parsed = parser.parse("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
			      "<mydoc test0=\"test_attribute0\">"
			      "<string>Test String for a member in Sequence.</string>\n"
			      "</mydoc>\n");
      SeqSample *root = aka::root_cast<SeqSample>(parsed);
      CPPUNIT_ASSERT_MESSAGE("Optional attribute does not have default value.", 
			     root->testattr1_ == "test1_value");
    }
    catch (const std::exception &) {
      parse_error = true;
    }
    CPPUNIT_ASSERT_MESSAGE("failed to handle lack of optional fixed attribute.", !parse_error);

    parse_error = false;
    set_required_of_fixed_attribute(true);
    try {
      aka::document 
	parsed = parser.parse("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
			      "<mydoc test0=\"test_attribute0\">"
			      "<string>Test String for a member in Sequence.</string>\n"
			      "</mydoc>\n");
      SeqSample *root = aka::root_cast<SeqSample>(parsed);
      CPPUNIT_ASSERT_MESSAGE("Optional attribute does not have default value.", 
			     root->testattr1_ == "test1_value");
    }
    catch (const std::exception &) {
      parse_error = true;
    }
    CPPUNIT_ASSERT_MESSAGE("failed to handle lack of optional fixed attribute.", parse_error);
  }
//CUPPA:decl=-
};

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

CPPUNIT_TEST_SUITE_REGISTRATION(attributeTest);
