#include "xmlserializer.h"
#include "classes/membertype.h"
#include "classes/closure.h"
#include <iostream>
#include <sstream>

using namespace aka2;

///. !!!!! Add support for Array of model groups.


void xml_serializer::escape_attribute_value(const std::string &value, std::ostream &ostm){

  for (std::string::const_iterator it = value.begin(); it != value.end(); ++it)
    if (*it == '\"')
      ostm << "&quot;";
    else if (*it == '<')
      ostm << "&lt;";
    else if (*it == '&')
      ostm << "&amp;";
    else 
      ostm << *it;
}

void xml_serializer::escape_text_value(const std::string &value, std::ostream &ostm){

  for (std::string::const_iterator it = value.begin(); it != value.end(); ++it)
    if (*it == '<')
      ostm << "&lt;";
    else if (*it == '&')
      ostm << "&amp;";
    else 
      ostm << *it;
  
}



void xml_serializer::serialize_internal(const void *root, const qname &name,
					const element_op &op,
					std::ostream &ostm) {
  indent_ = 0;
  ostm << "<?xml version=\"1.0\" encoding=\"" << encoding_ << "\" ?>" << std::endl;
  
//   write_namespace_attributes(ostm); 
  write_element(name, root, op, ostm, true);
  
  ostm << std::endl;
}


void xml_serializer::write_namespace_attributes(std::ostream &ostm) {

  const prefix_map &prefixes = system_global_attributes().get_prefix_map();

  for (prefix_map::const_iterator it = prefixes.begin();
       it != prefixes.end(); ++it) {
    const std::string &prefix = it->second;
    id_type namespace_id = it->first;
    new_line(ostm);
    ostm << " xmlns:" << prefix << "=\"" << namespaces().get_namespace_uri(namespace_id) << "\"";
  }

}

void xml_serializer::write_element(const qname &tagname, 
				  const void *e, const element_op &op, 
				  std::ostream &ostm,
				  bool is_root) {

  switch (op.get_schematype()) {
  case sequence_id: {
    write_sequence(tagname, e, static_cast<const sequence_op&>(op), ostm, is_root);
    break;
  }
  case choice_id: {
    write_choice(tagname, e, static_cast<const choice_op&>(op), ostm, is_root);
    break;
  }
  case all_id: {
    const all_op &aop = static_cast<const all_op&>(op);
    write_all(tagname, e, aop, ostm, is_root);
    break;
  }
  case simplecontent_id: {
    write_simplecontent(tagname, e, static_cast<const simplecontent_op&>(op), ostm, is_root);
    break;
  }
  case array_id: {
    const array_op &aop = static_cast<const array_op&>(op);
    write_array(tagname, e, aop, ostm);
    break;
  }  
  case simpletype_id:
    write_simpletype(tagname, e, static_cast<const simpletype_op&>(op), ostm, is_root);
    break;
  default:
    assert(!"Must not reach here.");
  }

}

void xml_serializer::write_element_entity(const void *e, const element_op &op, 
					 std::ostream &ostm) {

  switch (op.get_schematype()) {
  case sequence_id: {
    write_sequence_entity(e, static_cast<const sequence_op&>(op), ostm);
    break;
  }
  case choice_id: {
    write_choice_entity(e, static_cast<const choice_op&>(op), ostm);
    break;
  }
  case array_id: {
    const array_op &aop = static_cast<const array_op&>(op);
    write_group_array(e, aop, ostm);
    break;
  }  
  case all_id: 
    // Must be under the complexType.  
    // It should not be group or nested particle.
  case simplecontent_id:
  case simpletype_id:
  default:
    assert(!"Must not reach here.");
  }

}



void xml_serializer::write_sequence(const qname &tagname, 
				   const void *e, const sequence_op &sop, 
				   std::ostream &ostm,
				   bool is_root) {

  if (is_sequence_empty(e, sop.get_member_types())) {
    new_line(ostm);
    write_nill_element(tagname, e, sop, ostm, is_root);
  }
  else { 
    new_line(ostm);
    write_beginning_tag(tagname, e, sop, ostm, is_root);
    inc_indent_level();
    
    write_sequence_entity(e, sop, ostm);
    
    dec_indent_level();
    new_line(ostm);
    write_ending_tag(tagname, ostm);
  }
}


void xml_serializer::write_sequence_entity(const void *e, const sequence_op &sop, 
					  std::ostream &ostm) {

  const member_types &members = sop.get_member_types();
  for (member_types::const_iterator it = members.begin(); it != members.end(); ++it) {

    const const_memberpair mpair = it->get_member(e);
    
    switch (mpair.op_.get_schematype()) 	{
    case enclose_id: {
      new_line(ostm);
      write_beginning_tag(it->get_name(), 0, enclose_op(), ostm, false);
      inc_indent_level();
      break;
    }
    case disclose_id: {
      dec_indent_level();
      new_line(ostm);
      write_ending_tag(it->get_name(), ostm);
      break;
    }
    case simpletype_id:
    case simplecontent_id:	{
      if (!it->is_element())
	throw internal_error();
      write_element(it->get_name(), 
		    mpair.e_, mpair.op_, ostm,
		    false);
      break;
    }
    case ptrmember_id: {
      const ptrmember_op &pop = static_cast<const ptrmember_op&>(mpair.op_);
      if (!pop.is_null(mpair.e_))
	write_element(it->get_name(), pop.dereference(mpair.e_), pop.get_value_op(), 
		      ostm, false);
      break;
    }
    case fixed_id: {
      new_line(ostm);
      write_beginning_tag(it->get_name(), 0, mpair.op_, ostm, false); 
      it->write_fixed_string(ostm);
      write_ending_tag(it->get_name(), ostm);
      break;
    }
    case sequence_id:
    case choice_id:
    case all_id:
    case array_id: {
      if (it->is_element())
	write_element(it->get_name(), mpair.e_, mpair.op_, ostm, false);
      else 
	write_element_entity(mpair.e_, mpair.op_, ostm);
      break;
    }
    default:
      assert(!"Must not reach here.");
    }
  }

}


void xml_serializer::write_choice(const qname &tagname, 
				 const void *e, const choice_op &cop, 
				 std::ostream &ostm,
				 bool is_root) {

  if (cop.empty(e)) {
    new_line(ostm);
    write_nill_element(tagname, e, cop, ostm, is_root);
  }
  else {
    new_line(ostm);
    write_beginning_tag(tagname, e, cop, ostm, is_root);
    
    inc_indent_level();

    write_choice_entity(e, cop, ostm);

    dec_indent_level();

    new_line(ostm);
    write_ending_tag(tagname, ostm);
  }

}


void xml_serializer::write_choice_entity(const void *e, const choice_op &cop, 
				 std::ostream &ostm) {

    item_iterator *ait = cop.get_iterator(e);
    while (ait->has_next()) {
      const item *i = ait->next();
      if (i->is_element())
	write_element(i->get_name(), i->element(), i->get_op(), ostm, false);
      else
	write_element_entity(i->element(), i->get_op(), ostm);
    }
    delete ait;

}


void xml_serializer::write_all(const qname &tagname, 
			      const void *e, const all_op &aop, 
			      std::ostream &ostm,
			      bool is_root) {
  
  if (is_all_empty(e, aop.get_member_map())) {
    new_line(ostm);
    write_nill_element(tagname, e, aop, ostm, is_root);
  }
  else {
    new_line(ostm);
    write_beginning_tag(tagname, e, aop, ostm, is_root);
    
    inc_indent_level();
    
    write_all_entity(e, aop, ostm);
    
    dec_indent_level();
    
    new_line(ostm);
    write_ending_tag(tagname, ostm);
  }
}


void xml_serializer::write_all_entity(const void *e, const all_op &aop, 
				     std::ostream &ostm) {
  const member_map &mmap = aop.get_member_map();
  for (member_map::const_iterator it = mmap.begin();
       it != mmap.end(); ++it) {
    const member_type &mtype = it->second;
    const const_memberpair mpair = mtype.get_member(e);

    if (!it->second.is_element())
      throw internal_error();
    
    switch (mpair.op_.get_schematype()) 	{
    case simpletype_id:
    case simplecontent_id:	{
      write_element(mtype.get_name(), 
		    mpair.e_, static_cast<const simpletype_op&>(mpair.op_), ostm,
		    false);
      break;
    }
    case sequence_id:
    case choice_id:
    case all_id:
    case array_id: {
      write_element(mtype.get_name(), mpair.e_, mpair.op_, ostm, false);
      break;
    }
    case ptrmember_id: {
      const ptrmember_op &pop = static_cast<const ptrmember_op&>(mpair.op_);
      if (!pop.is_null(mpair.e_))
	write_element(mtype.get_name(), pop.dereference(mpair.e_), pop.get_value_op(), 
		      ostm, false);
      break;
    }
    default:
      assert(!"Must not reach here.");
    }
  }

}


void xml_serializer::write_simplecontent(const qname &tagname,
					const void *e, const simplecontent_op &sop, 
					std::ostream &ostm,
					bool is_root) {


  const member_type &vt = sop.get_valuetype();
  const_memberpair valpair = vt.get_member(e);
  assert(valpair.op_.get_schematype() == simpletype_id);
  const simpletype_op &simop = static_cast<const simpletype_op&>(valpair.op_);
    
  std::ostringstream ostring;
  simop.write_text(e, ostring, system_global_attributes());

  if (ostring.rdbuf()->str().empty()) {
    new_line(ostm);
    write_nill_element(tagname, e, sop, ostm, is_root);
  }
  else {
    new_line(ostm);
    write_beginning_tag(tagname, e, sop, ostm, is_root);
    escape_text_value(ostring.rdbuf()->str(), ostm);
    write_ending_tag(tagname, ostm);
  }
}

void xml_serializer::write_array(const qname &tagname, 
				const void *e, const array_op &aop, 
				std::ostream &ostm) {


  const element_op &itemop = aop.get_item_op();
  array_iterator *ait = aop.get_iterator(e);
    
  while (ait->has_next()) {
    const void *item = ait->next();
    write_element(tagname, item, itemop, ostm, false);
  }
  delete ait;

}


void xml_serializer::write_group_array(const void *e, const array_op &aop, 
				      std::ostream &ostm) {


  const element_op &itemop = aop.get_item_op();
  array_iterator *ait = aop.get_iterator(e);
    
  while (ait->has_next()) {
    const void *item = ait->next();
    write_element_entity(item, itemop, ostm);
  }
  delete ait;

}


void xml_serializer::write_simpletype(const qname &tagname,
				     const void *e, const simpletype_op &sop, 
				     std::ostream &ostm,
				     bool is_root) {


  std::ostringstream ostring;
  sop.write_text(e, ostring, system_global_attributes());
  if (ostring.rdbuf()->str().empty()) {
    new_line(ostm);
    write_nill_element(tagname, e, sop, ostm, is_root);
  }
  else {
    new_line(ostm);
    write_beginning_tag(tagname, e, sop, ostm, is_root);
    escape_text_value(ostring.rdbuf()->str(), ostm);
    write_ending_tag(tagname, ostm);
  }
}



void xml_serializer::write_beginning_tag(const qname &tagname, 
					const void *e, const element_op &op,
					std::ostream &ostm,
					bool is_root) {

  ostm << '<' << tagname;
  if (is_root)
    write_namespace_attributes(ostm);

  const attribute_types *attrs = op.get_attribute_types();
  if (attrs != 0)
    write_attributes(e, attrs, ostm);
  ostm << '>';

}

void xml_serializer::write_ending_tag(const qname &tagname, std::ostream &ostm) {
  ostm << "</" << tagname << '>';
}

void xml_serializer::write_nill_element(const qname &tagname, 
				       const void *e, const element_op &op,
				       std::ostream &ostm, bool is_root) {
  ostm << '<' << tagname;
  if (is_root)
    write_namespace_attributes(ostm);

  const attribute_types *attrs = op.get_attribute_types();
  if (attrs != 0)
    write_attributes(e, attrs, ostm);
  ostm << "/>";
}



void xml_serializer::write_attributes(const void *elm, 
				     const attribute_types *attrptr, std::ostream &ostm){

  assert(attrptr != 0);

  const attribute_types &attrs = *attrptr;
  for (attribute_types::const_iterator it = attrs.begin(); it != attrs.end(); ++it) {
    const attribute_type &attr = it->second;

    assert((attr.get_schematype() == fixed_id) || 
	   (attr.get_schematype() == simpletype_id));

    if (attr.get_schematype() == fixed_id) {
      if (attr.is_required()) {
	ostm << ' ' << it->first.local() << "=\"";
	std::ostringstream ostring;
	it->second.write_fixed_string(ostring);
	escape_text_value(ostring.rdbuf()->str(), ostm);
	ostm << '\"';
      }
    }
    else if (attr.should_write(elm)) {
      ostm << ' ' << it->first.local() << "=\"";

      const_memberpair mpair = attr.get_member(elm);
      std::ostringstream ostring;
      static_cast<const simpletype_op&>(mpair.op_).write_text(mpair.e_, ostring, 
							      system_global_attributes());
      escape_text_value(ostring.rdbuf()->str(), ostm);
      ostm << '\"';
    }

  }
}

