/* -*- c++ -*- */
#include "formatter.h"
#include "exception.h"

#ifdef USE_XERCESC
#include <xercesc/util/PlatformUtils.hpp> /* {Xerces-C++2.2} */
#include <xercesc/util/XMLString.hpp>
#endif

namespace aka2 {

namespace {
  struct escape_entry {
    uchar_t uchar_;
    uchar_t escaped_[16];
  };

  const escape_entry escape_table[] = {
    {'"', {'&', 'q', 'u', 'o', 't', ';', 0}},
    {'&', {'&', 'a', 'm', 'p', ';',      0}},
    {'<', {'&', 'l', 't', ';',           0}},
    {0,   {                              0}}
  };

#ifdef __USING_EUC__
  const int default_encoding_ = babel::base_encoding::euc;
#endif

#ifdef __USING_SJIS__
  const int default_encoding_ = babel::base_encoding::sjis;
#endif
} // namespace


bool formatter::use_xerces_transcoder_ = false;

void formatter::write(const std::string &value) {
  ustring unistr = lcp_to_ucs2(value);
  write(unistr);
}

void formatter::write_attribute_entity(const std::string &entity) {
  write_entity(entity, 0);
}

void formatter::write_text_entity(const std::string &entity) {
  write_entity(entity, 1);
}

void formatter::write_entity(const std::string &value, const int escape_index) {
  ustring unistr = lcp_to_ucs2(value);
  ustring escaped;
  for (ustring::const_iterator it = unistr.begin(); it != unistr.end(); ++it) {
    const uchar_t uchar = *it;
    const escape_entry *entry = &escape_table[escape_index];
    while (entry->uchar_ != 0) {
      if (entry->uchar_ == uchar) {
        escaped += entry->escaped_;
        break;
      }
      ++entry;
    }
    if (entry->uchar_ == 0)
      escaped += uchar;
  }
  write(escaped);
}


void babel_formatter::prepare(const std::string &encoding) {

#ifdef __LITTLE_ENDIAN_COMPUTER__
  static const bom utf16_bom = {{ '\xff', '\xfe' }, 2};
  //static const bom utf32_bom = {{ 0xff, 0xfe, 0, 0 }, 4};
#endif

#ifdef __BIG_ENDIAN_COMPUTER__
  static const bom utf16_bom = {{ '\xfe', '\xff' }, 2};
  //static const bom utf32_bom = {{ 0, 0, 0xfe, 0xff }, 4};
#endif

  int enc_out = yggdrasil::get_encoding_from_label(encoding.c_str());
  if (enc_out == babel::base_encoding::unknown)
    throw aka2::internal_error();
  in_translator_ = 
    babel::manual_translate_engine<std::string, ustring>::
    create(default_encoding_, babel::base_encoding::unicode);
  out_translator_ = 
    babel::manual_translate_engine<ustring, std::string>::
    create(babel::base_encoding::unicode, enc_out);

  if ((stricmp("utf16", encoding.c_str()) == 0) ||
      (stricmp("utf-16", encoding.c_str()) == 0)) {
    bom_ = &utf16_bom;
  }
  else
    bom_ = 0;
}

ustring babel_formatter::lcp_to_ucs2(const std::string &source) {
  in_translator_.clear();
  in_translator_.translate(source);
  in_translator_.flush();
  return in_translator_.get_string();
}

void babel_formatter::write(const ustring &entity) {
  if (bom_ != 0) {
    ostm_->write(bom_->chars_, bom_->length_);
    bom_ = 0;
  }  
  out_translator_.clear();
  out_translator_.translate(entity);
  out_translator_.flush();
  const std::string &res = out_translator_;
  ostm_->write(res.c_str(), res.size());
}

#ifdef USE_XERCESC

void xerces_formatter::prepare(const std::string &encoding) {
  xercesc::XMLTransService::Codes resValue;
  out_transcoder_.reset(xercesc::XMLPlatformUtils::fgTransService->
			makeNewTranscoderFor(encoding.c_str(), resValue, 4096));
  if (resValue != xercesc::XMLTransService::Ok)
    throw aka2::internal_error();

  in_transcoder_.reset(xercesc::XMLPlatformUtils::fgTransService->makeNewLCPTranscoder());
}

ustring xerces_formatter::lcp_to_ucs2(const std::string &source) {

  XMLCh *converted = in_transcoder_->transcode(source.c_str());
  ustring ret(converted);
  /** Freeing memory allocated in Xerces-C++ lib. */
  xercesc::XMLString::release(&converted);
  return ret;
}

void xerces_formatter::write(const ustring &entity) {
  const int maxBytes = 4096;
  unsigned int charsEaten;

  /** [g++2.96] std::basic_string<unsigned short>::c_str() does not work with g++2.96 STL */
  const XMLCh *toConvert = reinterpret_cast<const XMLCh*>(entity.data());

  XMLByte buffer[maxBytes];

  for (unsigned int offset = 0; offset < entity.size(); ) {
    unsigned int numBytes = out_transcoder_->
      transcodeTo(toConvert + offset,
                  entity.size(),
                  buffer,
                  maxBytes,
                  charsEaten,
                  xercesc::XMLTranscoder::UnRep_RepChar);
    ostm_->write(reinterpret_cast<char*>(buffer), numBytes);
    offset = charsEaten;
  }
}

#endif /* USE_XERCESC */

} // namespace aka2
