/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libe-book 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 <cassert>
#include <deque>
#include <string>

#include "libebook_utils.h"

#include "FB2Content.h"
#include "FB2ContentMap.h"
#include "FB2ExtrasCollector.h"
#include "FB2Style.h"

using std::string;

namespace libebook
{

class GroupContent : public FB2Content
{
  typedef std::deque<const FB2Content *> List_t;

public:
  GroupContent();
  virtual ~GroupContent();

  void add(const FB2Content *content);

protected:
  virtual void unfold(FB2Collector &collector) const;

private:
  List_t m_items;
};

namespace
{

class TextContent : public FB2Content
{
public:
  TextContent(const char *text, const FB2Style &style);

private:
  virtual void unfold(FB2Collector &collector) const;

private:
  const string m_text;
  const FB2Style m_style;
};

class ParagraphContent : public GroupContent
{
public:
  ParagraphContent(const FB2BlockFormat &format);

private:
  virtual void unfold(FB2Collector &collector) const;

private:
  const FB2BlockFormat m_format;
};

class ImageContent : public FB2Content
{
public:
  ImageContent(const string &contentType, const string &base64Data);

private:
  virtual void unfold(FB2Collector &collector) const;

private:
  const string m_contentType;
  const string m_data;
};

}

namespace
{

TextContent::TextContent(const char *text, const FB2Style &style)
  : m_text(text)
  , m_style(style)
{
}

void TextContent::unfold(FB2Collector &collector) const
{
  collector.openSpan(m_style);
  collector.insertText(m_text.c_str());
  collector.closeSpan();
}

ParagraphContent::ParagraphContent(const FB2BlockFormat &format)
  : m_format(format)
{
}

void ParagraphContent::unfold(FB2Collector &collector) const
{
  collector.openParagraph(m_format);
  GroupContent::unfold(collector);
  collector.closeParagraph();
}

ImageContent::ImageContent(const string &contentType, const string &base64Data)
  : m_contentType(contentType)
  , m_data(base64Data)
{
}

void ImageContent::unfold(FB2Collector &collector) const
{
  collector.insertBitmapData(m_contentType.c_str(), m_data.c_str());
}

}

GroupContent::GroupContent()
  : m_items()
{
}

GroupContent::~GroupContent()
{
  for (List_t::const_iterator it = m_items.begin(), last = m_items.end(); it != last; ++it)
    delete *it;
  m_items.clear();
}

void GroupContent::add(const FB2Content *content)
{
  m_items.push_back(content);
}

void GroupContent::unfold(FB2Collector &collector) const
{
  for (List_t::const_iterator it = m_items.begin(), last = m_items.end(); it != last; ++it)
    (*it)->unfold(collector);
}

FB2ExtrasCollector::FB2ExtrasCollector(FB2ContentMap &notes, FB2ContentMap &bitmaps)
  : m_notes(notes)
  , m_bitmaps(bitmaps)
  , m_currentID()
  , m_currentNote(0)
  , m_currentPara(0)
  , m_currentStyle(0)
{
}

FB2ExtrasCollector::~FB2ExtrasCollector()
{
  flushCurrentNote();
}

void FB2ExtrasCollector::defineMetadataEntry(const char *, const char *)
{
}

void FB2ExtrasCollector::openMetadataEntry(const char *)
{
}

void FB2ExtrasCollector::closeMetadataEntry()
{
}

void FB2ExtrasCollector::defineID(const char *id)
{
  flushCurrentNote();

  m_currentID = id;
  m_currentNote = new GroupContent();
}

void FB2ExtrasCollector::openPageSpan()
{
}

void FB2ExtrasCollector::closePageSpan()
{
}

void FB2ExtrasCollector::openBlock()
{
  // TODO: implement me
}

void FB2ExtrasCollector::closeBlock()
{
  // TODO: implement me
}

void FB2ExtrasCollector::openParagraph(const FB2BlockFormat &format)
{
  if (isInNote())
    m_currentPara = new ParagraphContent(format);
}

void FB2ExtrasCollector::closeParagraph()
{
  if (isInNote())
  {
    m_currentNote->add(m_currentPara);
    m_currentPara = 0;
  }
}

void FB2ExtrasCollector::openSpan(const FB2Style &style)
{
  if (isInNote())
  {
    delete m_currentStyle;
    m_currentStyle = new FB2Style(style);
  }
}

void FB2ExtrasCollector::closeSpan()
{
  if (isInNote())
  {
    delete m_currentStyle;
    m_currentStyle = 0;
  }
}

void FB2ExtrasCollector::insertText(const char *text)
{
  if (isInNote())
  {
    assert(m_currentPara);
    m_currentPara->add(new TextContent(text, *m_currentStyle));
  }
}

void FB2ExtrasCollector::openTable(const FB2BlockFormat &format)
{
  // TODO: implement me
  (void) format;
}

void FB2ExtrasCollector::closeTable()
{
  // TODO: implement me
}

void FB2ExtrasCollector::openTableRow(const FB2BlockFormat &format)
{
  // TODO: implement me
  (void) format;
}

void FB2ExtrasCollector::closeTableRow()
{
  // TODO: implement me
}

void FB2ExtrasCollector::openTableCell(int rowspan, int colspan)
{
  // TODO: implement me
  (void) rowspan;
  (void) colspan;
}

void FB2ExtrasCollector::closeTableCell()
{
  // TODO: implement me
}

void FB2ExtrasCollector::insertCoveredTableCell()
{
  // TODO: implement me
}

void FB2ExtrasCollector::insertFootnote(const char *)
{
}

void FB2ExtrasCollector::insertBitmap(const char *)
{
}

void FB2ExtrasCollector::insertBitmapData(const char *const contentType, const char *const base64Data)
{
  if (!m_currentID.empty())
    m_bitmaps.insert(m_currentID, new ImageContent(contentType, base64Data));
}

void FB2ExtrasCollector::flushCurrentNote()
{
  if (isInNote())
    m_notes.insert(m_currentID, m_currentNote);

  m_currentID.clear();
  m_currentNote = 0;

  // created, but not used; do not leak
  delete m_currentPara;
  m_currentPara = 0;
  delete m_currentStyle;
  m_currentStyle = 0;
}

bool FB2ExtrasCollector::isInNote() const
{
  return !m_currentID.empty() && m_currentNote;
}

}

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