/* -*- c++ -*- */
#include "serializer.h"
#include "formatter.h"
#include "classes/member_cache.h"
#include "classes/any.h"

namespace aka2 {

serializer::serializer(int tab, const std::string &encoding)
  : indent_str_(tab, ' '), encoding_(encoding), formatter_(0) {}

serializer::~serializer() {}

void serializer::serialize(const document &doc, std::ostream &ostm) {
  do_serialize(doc.get_root().ptr(), doc.get_props(), doc.get_name(), ostm);
}


void serializer::do_serialize(const void *e, const element_props &props, const qname &name, std::ostream &ostm) {
  create_formatter(ostm);
  serialize_internal(e, props, name);
  formatter_.reset();
}


void serializer::create_formatter(std::ostream& ostm) {
#ifdef USE_XERCESC
  if (formatter::use_xerces_transcoder_)
    formatter_.reset(new xerces_formatter(ostm));
  else
#endif
    formatter_.reset(new babel_formatter(ostm));

  try {
    formatter_->prepare(encoding_);
  }
  catch (...) {
    formatter_.reset();
    throw internal_error();
  }
}

void serializer::new_line() {
  std::string line = "\n";
  for (int i = indent_; i != 0; --i)
    line += indent_str_;
  formatter_->write(line);
}

bool serializer::is_sequence_empty(const void *e, const member_types &mtypes) {
  for (member_types::const_iterator it = mtypes.begin();
       it != mtypes.end(); ++it) {
    if (!is_member_empty(e, *it))
      return false;
  }
  return true;
}

bool serializer::is_all_empty(const void *e, const member_map &mmap) {
  for (member_map::const_iterator it = mmap.begin();
       it != mmap.end(); ++it) {
    if (!is_member_empty(e, it->second))
      return false;
  }
  return true;
}

bool serializer::is_member_empty(const void *e, const member_type &mtype) {

  const_member_cache cache(e, mtype.op(), mtype.getter());
  cache.prepare();
  
  switch (mtype.get_schematype()) {
  case simpletype_id:
  case simplecontent_id:
    return false;
  case array_id: {
    const array_op &aop = static_cast<const array_op&>(mtype.op());
    if (aop.size(cache.value()) != 0)
      return false;
    break;
  }
  case sequence_id: {
    if (mtype.is_element())
      return false;
    const sequence_op &sop = static_cast<const sequence_op&>(mtype.op());
    if (!is_sequence_empty(cache.value(), sop.get_member_types()))
      return false;
    break;
  }
  case choice_id: {
    if (mtype.is_element())
      return false;
    const choice_op &cop = static_cast<const choice_op&>(mtype.op());
    if (cop.size(cache.value()) != 0)
      return false;
    break;
  }
  case all_id: {
    if (mtype.is_element())
      return false;
    const all_op &aop = static_cast<const all_op&>(mtype.op());
    if (!is_all_empty(cache.value(), aop.get_member_map()))
      return false;
    break;
  }
  case ptrmember_id: {
    const ptrmember_op &pop = static_cast<const ptrmember_op&>(mtype.op());
    if (!pop.is_null(cache.value()))
      return false;
    break;
  }
  case any_array_id: {
    const any_array &ar = *static_cast<const any_array*>(cache.value());
    return ar.empty();
  }
  default:
    assert(!"Must not reach here.");
  }
  return true;
}

} // namespace aka2
