/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libepubgen project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <sstream>
#include <string>

#include "EPUBHTMLGenerator.h"
#include "EPUBImageManager.h"
#include "EPUBListStyleManager.h"
#include "EPUBParagraphStyleManager.h"
#include "EPUBPath.h"
#include "EPUBSpanStyleManager.h"
#include "EPUBTableStyleManager.h"

#include "libepubgen_utils.h"

namespace libepubgen
{

using librevenge::RVNGBinaryData;
using librevenge::RVNGPropertyList;
using librevenge::RVNGString;

using std::string;

namespace
{

class ZoneSinkImpl
{
public:
  ZoneSinkImpl();

  void openElement(const char *name, const librevenge::RVNGPropertyList &attributes);
  void closeElement(const char *name);

  void insertCharacters(const librevenge::RVNGString &characters);

  void append(const ZoneSinkImpl &other);

  const EPUBXMLSink &get() const;
  EPUBXMLSink &get();

  bool empty() const;

  bool endsInLineBreak() const;

private:
  EPUBXMLSink m_sink;
  std::string m_lastCloseElement;
  bool m_empty;
};

ZoneSinkImpl::ZoneSinkImpl()
  : m_sink()
  , m_lastCloseElement()
  , m_empty(true)
{
}

void ZoneSinkImpl::openElement(const char *const name, const librevenge::RVNGPropertyList &attributes)
{
  m_sink.openElement(name, attributes);
  m_lastCloseElement.clear();
  m_empty = false;
}

void ZoneSinkImpl::closeElement(const char *const name)
{
  m_sink.closeElement(name);
  m_lastCloseElement = name;
  m_empty = false;
}

void ZoneSinkImpl::insertCharacters(const librevenge::RVNGString &characters)
{
  m_sink.insertCharacters(characters);
  m_lastCloseElement.clear();
  m_empty = false;
}

void ZoneSinkImpl::append(const ZoneSinkImpl &other)
{
  m_sink.append(other.m_sink);
  m_lastCloseElement = other.m_lastCloseElement;
  m_empty |= other.m_empty;
}

const EPUBXMLSink &ZoneSinkImpl::get() const
{
  return m_sink;
}

EPUBXMLSink &ZoneSinkImpl::get()
{
  return m_sink;
}

bool ZoneSinkImpl::empty() const
{
  return m_empty;
}

bool ZoneSinkImpl::endsInLineBreak() const
{
  return m_lastCloseElement == "p"
         || m_lastCloseElement == "ul"
         || m_lastCloseElement == "ol"
         || m_lastCloseElement == "br"
         ;
}

}

namespace
{

struct TextZoneSink;

//! a zone to regroup footnote/endnote,... data
struct EPUBHTMLTextZone
{
  friend struct TextZoneSink;

  //! the different zone
  enum Type { Z_Comment=0, Z_EndNote, Z_FootNote, Z_Main, Z_MetaData, Z_TextBox, Z_Unknown, Z_NumZones= Z_Unknown+1};
  //! constructor for basic stream
  EPUBHTMLTextZone(Type tp=Z_Unknown) : m_type(tp), m_actualId(0), m_zoneSinks()
  {
  }
  //! the type
  Type type() const
  {
    return m_type;
  }
  //! the type
  void setType(Type tp)
  {
    m_type=tp;
  }
  //! returns a new sink corresponding to this zone
  TextZoneSink *getNewSink();
  //! delete a sink corresponding to this zone
  void deleteSink(TextZoneSink *sink);
  //! returns true if there is no data
  bool isEmpty() const
  {
    for (size_t i = 0; i < m_zoneSinks.size(); i++)
      if (!m_zoneSinks[i].empty())
        return false;
    return true;
  }
  //! send the zone data
  void send(EPUBXMLSink &out) const
  {
    if (isEmpty() || m_type==Z_Unknown || m_type==Z_Main)
      return;
    if (m_type!=Z_MetaData)
    {
      out.openElement("hr", RVNGPropertyList());
      out.closeElement("hr");
    }
    if (m_type==Z_MetaData)
    {
      for (size_t i = 0; i < m_zoneSinks.size(); i++)
        out.append(m_zoneSinks[i].get());
      return;
    }
    if (m_type==Z_TextBox)
    {
      out.openElement("p", RVNGPropertyList());
      out.openElement("b", RVNGPropertyList());
      out.insertCharacters("TEXT BOXES");
      out.closeElement("b");
      out.closeElement("p");
      for (size_t i = 0; i < m_zoneSinks.size(); i++)
      {
        out.append(m_zoneSinks[i].get());
        out.openElement("hr", RVNGPropertyList());
        out.closeElement("hr");
      }
      return;
    }
    for (size_t i = 0; i < m_zoneSinks.size(); i++)
    {
      out.append(m_zoneSinks[i].get());
      // check if we need to add a return line
      if (!m_zoneSinks[i].endsInLineBreak())
      {
        out.openElement("br", RVNGPropertyList());
        out.closeElement("br");
      }
    }
  }

protected:
  //! return a label corresponding to the zone
  std::string label(int id) const;
  //! the zone type
  Type m_type;
  //! the actual id
  mutable int m_actualId;
  //! the list of data string
  std::vector<ZoneSinkImpl> m_zoneSinks;
private:
  EPUBHTMLTextZone(EPUBHTMLTextZone const &orig);
  EPUBHTMLTextZone operator=(EPUBHTMLTextZone const &orig);
};

struct TextZoneSink
{
  //! constructor
  TextZoneSink(EPUBHTMLTextZone *zone): m_zone(zone), m_zoneId(0), m_sink(), m_delayedLabel()
  {
    if (m_zone)
      m_zoneId=m_zone->m_actualId++;
  }
  //! destructor
  ~TextZoneSink() { }
  //! add a label called on main and a label in this ( delayed to allow openParagraph to be called )
  void addLabel(EPUBXMLSink &output)
  {
    std::string lbl=label();
    if (!lbl.length())
      return;
    {
      RVNGPropertyList supAttrs;
      supAttrs.insert("id", ("called" + lbl).c_str());
      output.openElement("sup", supAttrs);
      RVNGPropertyList aAttrs;
      aAttrs.insert("href", ("#data" + lbl).c_str());
      output.openElement("a", aAttrs);
      output.insertCharacters(lbl.c_str());
      output.closeElement("a");
      output.closeElement("sup");
    }
    flush();
    {
      RVNGPropertyList supAttrs;
      supAttrs.insert("id", ("data" + lbl).c_str());
      m_delayedLabel.openElement("sup", supAttrs);
      RVNGPropertyList aAttrs;
      aAttrs.insert("href", ("#called" + lbl).c_str());
      m_delayedLabel.openElement("a", aAttrs);
      m_delayedLabel.insertCharacters(lbl.c_str());
      m_delayedLabel.closeElement("a");
      m_delayedLabel.closeElement("sup");
    }
  }
  //! flush delayed label, ...
  void flush()
  {
    m_sink.append(m_delayedLabel);
    m_delayedLabel = ZoneSinkImpl();
  }
  //! return the sink
  EPUBXMLSink &getSink()
  {
    return m_sink.get();
  }
  //! send the data to the zone
  void send()
  {
    if (!m_zone || m_zone->m_type==EPUBHTMLTextZone::Z_Main)
    {
      EPUBGEN_DEBUG_MSG(("TextZoneSink::send: must not be called\n"));
      return;
    }
    flush();
    if (m_zone->m_zoneSinks.size() <= size_t(m_zoneId))
      m_zone->m_zoneSinks.resize(size_t(m_zoneId)+1);
    m_zone->m_zoneSinks[size_t(m_zoneId)] = m_sink;
  }
  //! send the data to the zone
  void sendMain(EPUBXMLSink &output)
  {
    flush();
    output.append(m_sink.get());
  }
protected:
  //! return the zone label
  std::string label() const
  {
    if (!m_zone || m_zone->m_type==EPUBHTMLTextZone::Z_Main)
    {
      EPUBGEN_DEBUG_MSG(("TextZoneSink::label: must not be called\n"));
      return "";
    }
    return m_zone->label(m_zoneId);
  }
  //! a zone
  EPUBHTMLTextZone *m_zone;
  //! the zone id
  int m_zoneId;
  //! the sink
  ZoneSinkImpl m_sink;
  //! the label
  ZoneSinkImpl m_delayedLabel;
private:
  TextZoneSink(TextZoneSink const &orig);
  TextZoneSink operator=(TextZoneSink const &orig);
};

TextZoneSink *EPUBHTMLTextZone::getNewSink()
{
  return new TextZoneSink(this);
}

void EPUBHTMLTextZone::deleteSink(TextZoneSink *sink)
{
  delete sink;
}

std::string EPUBHTMLTextZone::label(int id) const
{
  char c=0;
  switch (m_type)
  {
  case Z_Comment:
    c='C';
    break;
  case Z_EndNote:
    c='E';
    break;
  case Z_FootNote:
    c='F';
    break;
  case Z_TextBox:
    c='T';
    break;
  case Z_Main:
  case Z_MetaData:
  case Z_Unknown:
  case Z_NumZones:
  default:
    break;
  }
  if (c==0)
    return "";
  std::stringstream s;
  s << c << id+1;
  return s.str();
}

}

//! the internal state of a html document generator
struct EPUBHTMLGeneratorImpl
{
  //! constructor
  EPUBHTMLGeneratorImpl(EPUBXMLSink &document, EPUBImageManager &imageManager, EPUBListStyleManager &listStyleManager, EPUBParagraphStyleManager &paragraphStyleManager, EPUBSpanStyleManager &spanStyleManager, EPUBTableStyleManager &tableStyleManager, const EPUBPath &path, const EPUBPath &stylesheetPath)
    : m_document(document)
    , m_imageManager(imageManager)
    , m_listManager(listStyleManager)
    , m_paragraphManager(paragraphStyleManager)
    , m_spanManager(spanStyleManager)
    , m_tableManager(tableStyleManager)
    , m_path(path)
    , m_stylesheetPath(stylesheetPath)
    , m_actualPage(0)
    , m_ignore(false)
    , m_actualSink()
    , m_sinkStack()
  {
    for (int i = 0; i < EPUBHTMLTextZone::Z_NumZones; ++i)
      m_zones[i].setType(EPUBHTMLTextZone::Type(i));
    m_actualSink=m_zones[EPUBHTMLTextZone::Z_Main].getNewSink();
  }
  //! destructor
  ~EPUBHTMLGeneratorImpl()
  {
    for (size_t i=0; i<m_sinkStack.size(); ++i)
    {
      if (m_sinkStack[i]) delete m_sinkStack[i];
    }
    if (m_actualSink) delete m_actualSink;
  }

  //! returns the actual output ( sending delayed data if needed)
  EPUBXMLSink &output(bool sendDelayed=true)
  {
    if (sendDelayed)
      m_actualSink->flush();
    return m_actualSink->getSink();
  }
  //! returns the actual sink
  TextZoneSink &getSink()
  {
    return *m_actualSink;
  }
  void push(EPUBHTMLTextZone::Type type)
  {
    m_sinkStack.push_back(m_actualSink);
    if (type==EPUBHTMLTextZone::Z_Main || type==EPUBHTMLTextZone::Z_NumZones)
    {
      EPUBGEN_DEBUG_MSG(("EPUBHTMLGeneratorImpl::push: can not push the main zone\n"));
      type=EPUBHTMLTextZone::Z_Unknown;
    }
    m_actualSink=m_zones[type].getNewSink();
  }
  void pop()
  {
    if (m_sinkStack.size() <= 0)
    {
      EPUBGEN_DEBUG_MSG(("EPUBHTMLGenerator::pop: can not pop sink\n"));
      return;
    }
    if (m_actualSink)
    {
      m_actualSink->send();
      delete m_actualSink;
    }
    m_actualSink = m_sinkStack.back();
    m_sinkStack.pop_back();
  }
  void sendMetaData(EPUBXMLSink &out)
  {
    m_zones[EPUBHTMLTextZone::Z_MetaData].send(out);
  }
  void flushUnsent(EPUBXMLSink &out)
  {
    if (m_sinkStack.size())
    {
      EPUBGEN_DEBUG_MSG(("EPUBHTMLGenerator::flushUnsent: the sink stack is not empty\n"));
      while (m_sinkStack.size())
        pop();
    }
    // first send the main data
    if (!m_actualSink)
    {
      EPUBGEN_DEBUG_MSG(("EPUBHTMLGenerator::flushUnsent: can not find the main block\n"));
    }
    else
      m_actualSink->sendMain(out);
    m_zones[EPUBHTMLTextZone::Z_Comment].send(out);
    m_zones[EPUBHTMLTextZone::Z_FootNote].send(out);
    m_zones[EPUBHTMLTextZone::Z_EndNote].send(out);
    m_zones[EPUBHTMLTextZone::Z_TextBox].send(out);
  }

  EPUBXMLSink &m_document;
  EPUBImageManager &m_imageManager;
  EPUBListStyleManager &m_listManager;
  EPUBParagraphStyleManager &m_paragraphManager;
  EPUBSpanStyleManager &m_spanManager;
  EPUBTableStyleManager &m_tableManager;
  const EPUBPath m_path;
  const EPUBPath m_stylesheetPath;

  int m_actualPage;
  bool m_ignore;

protected:
  TextZoneSink *m_actualSink;
  std::vector<TextZoneSink *> m_sinkStack;

  EPUBHTMLTextZone m_zones[EPUBHTMLTextZone::Z_NumZones];
private:
  EPUBHTMLGeneratorImpl(EPUBHTMLGeneratorImpl const &orig);
  EPUBHTMLGeneratorImpl operator=(EPUBHTMLGeneratorImpl const &orig);
};

EPUBHTMLGenerator::EPUBHTMLGenerator(EPUBXMLSink &document, EPUBImageManager &imageManager, EPUBListStyleManager &listStyleManager, EPUBParagraphStyleManager &paragraphStyleManager, EPUBSpanStyleManager &spanStyleManager, EPUBTableStyleManager &tableStyleManager, const EPUBPath &path, const EPUBPath &stylesheetPath)
  : m_impl(new EPUBHTMLGeneratorImpl(document, imageManager, listStyleManager, paragraphStyleManager, spanStyleManager, tableStyleManager, path, stylesheetPath))
{
}

EPUBHTMLGenerator::~EPUBHTMLGenerator()
{
  if (m_impl) delete m_impl;
}

void EPUBHTMLGenerator::setDocumentMetaData(const RVNGPropertyList &propList)
{
  m_impl->push(EPUBHTMLTextZone::Z_MetaData);
  EPUBXMLSink &meta=m_impl->output();
  static char const *wpdMetaFields[9]=
  {
    "meta:initial-creator", "dc:creator", "dc:subject", "dc:publisher", "meta:keywords",
    "dc:language", "dc:description", "librevenge:descriptive-name", "librevenge:descriptive-type"
  };
  static char const *metaFields[9]=
  {
    "author", "typist", "subject", "publisher", "keywords",
    "language", "abstract", "descriptive-name", "descriptive-type"
  };
  for (int i = 0; i < 9; i++)
  {
    if (!propList[wpdMetaFields[i]])
      continue;
    RVNGPropertyList attrs;
    attrs.insert("name", metaFields[i]);
    attrs.insert("content", propList[wpdMetaFields[i]]->getStr());
    meta.openElement("meta", attrs);
    meta.closeElement("meta");
  }
  meta.openElement("title", RVNGPropertyList());
  if (propList["librevenge:descriptive-name"])
    meta.insertCharacters(propList["librevenge:descriptive-name"]->getStr());
  meta.closeElement("title");
  m_impl->pop();
}

void EPUBHTMLGenerator::startDocument(const RVNGPropertyList &)
{
}

void EPUBHTMLGenerator::endDocument()
{
  RVNGPropertyList htmlAttrs;
  // TODO: set lang and xml:lang from metadata
  htmlAttrs.insert("xmlns", "http://www.w3.org/1999/xhtml");
  m_impl->m_document.openElement("html", htmlAttrs);
  m_impl->m_document.openElement("head", RVNGPropertyList());
  m_impl->m_document.openElement("title", RVNGPropertyList());
  m_impl->m_document.closeElement("title");
  RVNGPropertyList metaAttrs;
  metaAttrs.insert("http-equiv", "content-type");
  metaAttrs.insert("content", "text/html; charset=UTF-8");
  m_impl->m_document.openElement("meta", metaAttrs);
  m_impl->m_document.closeElement("meta");
  m_impl->sendMetaData(m_impl->m_document);
  RVNGPropertyList linkAttrs;
  linkAttrs.insert("href", m_impl->m_stylesheetPath.relativeTo(m_impl->m_path).str().c_str());
  linkAttrs.insert("type", "text/css");
  m_impl->m_document.insertEmptyElement("link", linkAttrs);
  m_impl->m_document.closeElement("head");
  m_impl->m_document.openElement("body", RVNGPropertyList());
  m_impl->flushUnsent(m_impl->m_document);
  m_impl->m_document.closeElement("body");
  m_impl->m_document.closeElement("html");
}

void EPUBHTMLGenerator::defineEmbeddedFont(const RVNGPropertyList &/*propList*/)
{
}

void EPUBHTMLGenerator::openPageSpan(const RVNGPropertyList & /* propList */)
{
  m_impl->m_actualPage++;
}

void EPUBHTMLGenerator::closePageSpan()
{
}

void EPUBHTMLGenerator::definePageStyle(const RVNGPropertyList &) {}

void EPUBHTMLGenerator::openHeader(const RVNGPropertyList & /* propList */)
{
  m_impl->m_ignore = true;
}

void EPUBHTMLGenerator::closeHeader()
{
  m_impl->m_ignore = false;
}


void EPUBHTMLGenerator::openFooter(const RVNGPropertyList & /* propList */)
{
  m_impl->m_ignore = true;
}

void EPUBHTMLGenerator::closeFooter()
{
  m_impl->m_ignore = false;
}

void EPUBHTMLGenerator::defineSectionStyle(const RVNGPropertyList &) {}
void EPUBHTMLGenerator::openSection(const RVNGPropertyList & /* propList */) {}
void EPUBHTMLGenerator::closeSection() {}

void EPUBHTMLGenerator::defineParagraphStyle(const RVNGPropertyList &propList)
{
  m_impl->m_paragraphManager.defineParagraph(propList);
}

void EPUBHTMLGenerator::openParagraph(const RVNGPropertyList &propList)
{
  if (m_impl->m_ignore)
    return;

  RVNGPropertyList attrs;
  attrs.insert("class", m_impl->m_paragraphManager.getClass(propList).c_str());
  m_impl->output(false).openElement("p", attrs);
}

void EPUBHTMLGenerator::closeParagraph()
{
  if (m_impl->m_ignore)
    return;

  m_impl->output().closeElement("p");
}

void EPUBHTMLGenerator::defineCharacterStyle(const RVNGPropertyList &propList)
{
  m_impl->m_spanManager.defineSpan(propList);
}

void EPUBHTMLGenerator::openSpan(const RVNGPropertyList &propList)
{
  if (m_impl->m_ignore)
    return;

  RVNGPropertyList attrs;
  attrs.insert("class", m_impl->m_spanManager.getClass(propList).c_str());
  m_impl->output(false).openElement("span", attrs);
}

void EPUBHTMLGenerator::closeSpan()
{
  if (m_impl->m_ignore)
    return;
  m_impl->output().closeElement("span");
}

void EPUBHTMLGenerator::openLink(const RVNGPropertyList &propList)
{
  if (m_impl->m_ignore)
    return;

  if (!propList["librevenge:type"])
  {
    EPUBGEN_DEBUG_MSG(("EPUBHTMLGenerator::openLink: librevenge:type: not filled, suppose link\n"));
  }
  RVNGPropertyList attrs;
  if (propList["xlink:href"])
    attrs.insert("href", RVNGString::escapeXML(propList["xlink:href"]->getStr()).cstr());
  m_impl->output(false).openElement("a", attrs);
}

void EPUBHTMLGenerator::closeLink()
{
  if (m_impl->m_ignore)
    return;
  m_impl->output().closeElement("a");
}

void EPUBHTMLGenerator::insertTab()
{
  if (m_impl->m_ignore)
    return;

  // Does not have a lot of effect since tabs in html are ignorable white-space
  m_impl->output().insertCharacters("\t");
}

void EPUBHTMLGenerator::insertLineBreak()
{
  if (m_impl->m_ignore)
    return;
  m_impl->output().openElement("br", RVNGPropertyList());
  m_impl->output().closeElement("br");
}

void EPUBHTMLGenerator::insertField(const RVNGPropertyList & /* propList */)
{
  if (m_impl->m_ignore)
    return;
  m_impl->output().insertCharacters("#");
}

void EPUBHTMLGenerator::insertText(const RVNGString &text)
{
  if (m_impl->m_ignore)
    return;
  m_impl->output().insertCharacters(RVNGString::escapeXML(text));
}

void EPUBHTMLGenerator::insertSpace()
{
  if (m_impl->m_ignore)
    return;
  m_impl->output().insertCharacters("&nbsp;");
}

void EPUBHTMLGenerator::openOrderedListLevel(const RVNGPropertyList &propList)
{
  if (m_impl->m_ignore)
    return;
  m_impl->m_listManager.defineLevel(propList, true);
  RVNGPropertyList attrs;
  attrs.insert("class", m_impl->m_listManager.openLevel(propList, true).c_str());
  // fixme: if level is > 1, we must first insert a div here
  m_impl->output(false).openElement("ol", attrs);
}

void EPUBHTMLGenerator::closeOrderedListLevel()
{
  if (m_impl->m_ignore)
    return;
  m_impl->m_listManager.closeLevel();
  m_impl->output().closeElement("ol");
}

void EPUBHTMLGenerator::openUnorderedListLevel(const RVNGPropertyList &propList)
{
  if (m_impl->m_ignore)
    return;
  m_impl->m_listManager.defineLevel(propList, false);
  RVNGPropertyList attrs;
  attrs.insert("class", m_impl->m_listManager.openLevel(propList, false).c_str());
  // fixme: if level is > 1, we must first insert a div here
  m_impl->output(false).openElement("ul", attrs);
}

void EPUBHTMLGenerator::closeUnorderedListLevel()
{
  if (m_impl->m_ignore)
    return;
  m_impl->m_listManager.closeLevel();
  m_impl->output().closeElement("ul");
}


void EPUBHTMLGenerator::openListElement(const RVNGPropertyList &propList)
{
  if (m_impl->m_ignore)
    return;
  RVNGPropertyList attrs;
  attrs.insert("class", m_impl->m_listManager.getClass(propList).c_str());
  m_impl->output(false).openElement("li", attrs);
}

void EPUBHTMLGenerator::closeListElement()
{
  if (m_impl->m_ignore)
    return;
  m_impl->output().closeElement("li");
}

void EPUBHTMLGenerator::openFootnote(const RVNGPropertyList &)
{
  if (m_impl->m_ignore)
    return;
  m_impl->push(EPUBHTMLTextZone::Z_FootNote);
  m_impl->getSink().addLabel(m_impl->output());
}

void EPUBHTMLGenerator::closeFootnote()
{
  if (m_impl->m_ignore)
    return;
  m_impl->pop();
}

void EPUBHTMLGenerator::openEndnote(const RVNGPropertyList &)
{
  if (m_impl->m_ignore)
    return;
  m_impl->push(EPUBHTMLTextZone::Z_EndNote);
  m_impl->getSink().addLabel(m_impl->output());
}

void EPUBHTMLGenerator::closeEndnote()
{
  if (m_impl->m_ignore)
    return;
  m_impl->pop();
}

void EPUBHTMLGenerator::openComment(const RVNGPropertyList & /*propList*/)
{
  if (m_impl->m_ignore)
    return;
  m_impl->push(EPUBHTMLTextZone::Z_Comment);
  m_impl->getSink().addLabel(m_impl->output());
}

void EPUBHTMLGenerator::closeComment()
{
  if (m_impl->m_ignore)
    return;
  m_impl->pop();
}

void EPUBHTMLGenerator::openTextBox(const RVNGPropertyList & /*propList*/)
{
  if (m_impl->m_ignore)
    return;
  m_impl->push(EPUBHTMLTextZone::Z_TextBox);
  m_impl->getSink().addLabel(m_impl->output());
}

void EPUBHTMLGenerator::closeTextBox()
{
  if (m_impl->m_ignore)
    return;
  m_impl->pop();
}

void EPUBHTMLGenerator::openTable(const RVNGPropertyList &propList)
{
  if (m_impl->m_ignore)
    return;

  const librevenge::RVNGPropertyListVector *columns = propList.child("librevenge:table-columns");
  if (columns)
    m_impl->m_tableManager.openTable(*columns);
  m_impl->output().openElement("table", RVNGPropertyList());
  m_impl->output().openElement("tbody", RVNGPropertyList());
}

void EPUBHTMLGenerator::openTableRow(const RVNGPropertyList &propList)
{
  if (m_impl->m_ignore)
    return;
  RVNGPropertyList attrs;
  attrs.insert("class", m_impl->m_tableManager.getRowClass(propList).c_str());
  m_impl->output().openElement("tr", attrs);
}

void EPUBHTMLGenerator::closeTableRow()
{
  if (m_impl->m_ignore)
    return;
  m_impl->output().closeElement("tr");
}

void EPUBHTMLGenerator::openTableCell(const RVNGPropertyList &propList)
{
  if (m_impl->m_ignore)
    return;
  RVNGPropertyList attrs;
  attrs.insert("class", m_impl->m_tableManager.getCellClass(propList).c_str());
  if (propList["table:number-columns-spanned"])
    attrs.insert("colspan", propList["table:number-columns-spanned"]->getInt());
  if (propList["table:number-rows-spanned"])
    attrs.insert("rowspan", propList["table:number-rows-spanned"]->getInt());
  m_impl->output().openElement("td", attrs);

}

void EPUBHTMLGenerator::closeTableCell()
{
  if (m_impl->m_ignore)
    return;
  m_impl->output().closeElement("td");
}

void EPUBHTMLGenerator::insertCoveredTableCell(const RVNGPropertyList & /* propList */) {}

void EPUBHTMLGenerator::closeTable()
{
  if (m_impl->m_ignore)
    return;
  m_impl->output().closeElement("tbody");
  m_impl->output().closeElement("table");
  m_impl->m_tableManager.closeTable();
}

void EPUBHTMLGenerator::openFrame(const RVNGPropertyList & /* propList */) {}
void EPUBHTMLGenerator::closeFrame() {}

void EPUBHTMLGenerator::openGroup(const librevenge::RVNGPropertyList & /* propList */) {}
void EPUBHTMLGenerator::closeGroup() {}

void EPUBHTMLGenerator::defineGraphicStyle(const librevenge::RVNGPropertyList & /* propList */) {}
void EPUBHTMLGenerator::drawRectangle(const librevenge::RVNGPropertyList & /* propList */) {}
void EPUBHTMLGenerator::drawEllipse(const librevenge::RVNGPropertyList & /* propList */) {}
void EPUBHTMLGenerator::drawPolygon(const librevenge::RVNGPropertyList & /* propList */) {}
void EPUBHTMLGenerator::drawPolyline(const librevenge::RVNGPropertyList & /* propList */) {}
void EPUBHTMLGenerator::drawPath(const librevenge::RVNGPropertyList & /* propList */) {}
void EPUBHTMLGenerator::drawConnector(const librevenge::RVNGPropertyList & /* propList */) {}

void EPUBHTMLGenerator::insertBinaryObject(const RVNGPropertyList &propList)
{
  m_impl->output().openElement("p", RVNGPropertyList());

  const EPUBPath &path = m_impl->m_imageManager.insert(
                           RVNGBinaryData(propList["office:binary-data"]->getStr()),
                           propList["librevenge:mimetype"]->getStr());

  RVNGPropertyList attrs;
  attrs.insert("src", path.relativeTo(m_impl->m_path).str().c_str());
  // FIXME: use alternative repr. if available
  attrs.insert("alt", path.str().c_str());
  m_impl->output().insertEmptyElement("img", attrs);

  m_impl->output().closeElement("p");
}

void EPUBHTMLGenerator::insertEquation(const RVNGPropertyList & /* propList */) {}

}

/* vim:set shiftwidth=2 softtabstop=2 expandtab: */
