/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* librvngabw
 * Version: MPL 2.0 / LGPLv2.1+
 *
 * 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/.
 *
 * Major Contributor(s):
 * Copyright (C) 2002-2004 William Lachance (wrlach@gmail.com)
 * Copyright (C) 2004 Fridrich Strba (fridrich.strba@bluewin.ch)
 *
 * For minor contributions see the git repository.
 *
 * Alternatively, the contents of this file may be used under the terms
 * of the GNU Lesser General Public License Version 2.1 or later
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
 * applicable instead of those above.
 *
 * For further information visit http://libwpd.sourceforge.net
 */

/* "This product is not manufactured, approved, or supported by
 * Corel Corporation or Corel Corporation Limited."
 */

#include <iostream> // OSNOLA: remove me
#include <map>
#include <stack>
#include <string>

#include <librevenge/librevenge.h>
#include <librvngabw/librvngabw.hxx>

#include "ABWGenerator.hxx"
#include "ABWSVGDrawingGenerator.hxx"
#include "DocumentElement.hxx"
#include "FilterInternal.hxx"
#include "Frame.hxx"
#include "ListStyle.hxx"
#include "PageSpan.hxx"
#include "Table.hxx"
#include "TextRunStyle.hxx"

namespace librvngabw
{
class ABWTextGeneratorPrivate
{
public:
	//! constructor
	explicit ABWTextGeneratorPrivate(ABWDocumentHandler *pHandler);
	//! destructor
	~ABWTextGeneratorPrivate();

	//! store the document meta data
	void setDocumentMetaData(const librevenge::RVNGPropertyList &propList);
	//! write the document meta data
	void writeDocumentMetaData(ABWDocumentHandler *pHandler);

	//! returns the curren xId
	int &getXId()
	{
		return m_ABWGenerator->m_xId;
	}

	//
	// storage
	//
	//! returns the body storage
	DocumentElementVector &getBodyStorage()
	{
		return m_bodyElementVector;
	}
	//! returns the current storage
	DocumentElementVector *getCurrentStorage() const
	{
		return m_ABWGenerator->getCurrentStorage();
	}
	//! returns the dummy data storage
	DocumentElementVector &getDummyStorage()
	{
		return m_ABWGenerator->getDummyStorage();
	}
	//! returns the file data data storage
	DocumentElementVector &getFileDataStorage()
	{
		return m_ABWGenerator->getFileDataStorage();
	}

	//! push a storage
	void pushStorage(DocumentElementVector *newStorage)
	{
		m_ABWGenerator->pushStorage(newStorage);
	}
	/** pop a storage */
	bool popStorage()
	{
		return m_ABWGenerator->popStorage();
	}
	//! write the storage data to a document handler
	static void sendStorage(DocumentElementVector const *storage, ABWDocumentHandler *pHandler)
	{
		ABWGenerator::sendStorage(storage, pHandler);
	}
	//! create the targer document
	bool writeTargetDocument(ABWDocumentHandler *pHandler);


	// page, header/footer, master page

	//! init the page manager
	void initPageManager();

	//! return the page span manager
	PageSpanManager &getPageSpanManager()
	{
		return m_pageSpanManager;
	}

	//! starts a header/footer page.
	void startHeaderFooter(bool header, const librevenge::RVNGPropertyList &propList);
	//! ends a header/footer page
	void endHeaderFooter();
	//! returns if we are in a master page
	bool inHeaderFooter() const
	{
		return getState().m_isInHeaderFooter;
	}

	//! start a new section (if possible)
	void startSection(const librevenge::RVNGPropertyList &propList, bool dummySection=false);
	//! close a section (if possible)
	void endSection(bool endPageSpan=false);

	//
	// paragraph
	//
	std::string const getParentStyleName(librevenge::RVNGPropertyList const &propList);

	//
	// list function
	//

	//! return the page span manager
	ListManager &getListManager()
	{
		return m_ABWGenerator->m_listManager;
	}
	/// pop the list state (if possible)
	void popListState()
	{
		m_ABWGenerator->m_listManager.popState();
	}
	/// push the list state by adding an empty value
	void pushListState()
	{
		m_ABWGenerator->m_listManager.pushState();
	}

	//
	// note
	//

	/// returns true if we are in a note or an annotation
	bool inNoteOrInAnnotation() const
	{
		ABWGenerator::State const &state=getState();
		return state.m_isInNote || state.m_isInAnnotation;
	}
	//
	// frame/table
	//

	//! update the max frame page number
	void updateFramePageNumber(int pageNumber)
	{
		m_ABWGenerator->updateFramePageNumber(pageNumber);
	}
	//! returns the max frame page number
	int getFramePageNumber() const
	{
		return m_ABWGenerator->getFramePageNumber();
	}

	//! returns the frame manager
	FrameManager &getFrameManager()
	{
		return m_ABWGenerator->m_frameManager;
	}

	//! returns the table manager
	TableManager &getTableManager()
	{
		return m_ABWGenerator->m_tableManager;
	}

	//
	// image
	//

	//! inserts a binary object
	void insertBinaryObject(const librevenge::RVNGPropertyList &propList)
	{
		m_ABWGenerator->insertBinaryObject(propList);
	}
	//! inserts a svg binary object
	void insertSVGBinaryObject(const librevenge::RVNGString &picture,
	                           const librevenge::RVNGPropertyList &propList,
	                           double const &minx, double const &miny, double const &maxx, double const &maxy)
	{
		m_ABWGenerator->insertSVGBinaryObject(picture, propList, minx, miny, maxx, maxy);
	}

	//
	// state gestion
	//

	//! returns the actual state
	ABWGenerator::State &getState()
	{
		return m_ABWGenerator->getState();
	}
	//! returns the actual state
	ABWGenerator::State const &getState() const
	{
		return m_ABWGenerator->getState();
	}
	//! push a state
	void pushState()
	{
		m_ABWGenerator->pushState();
	}
	//! pop a state
	void popState()
	{
		m_ABWGenerator->popState();
	}
	//! the main document generator
	std::shared_ptr<ABWGenerator> m_ABWGenerator;

	//! the meta data elements
	DocumentElementVector m_metaDataElementVector;
	//! content elements
	DocumentElementVector m_bodyElementVector;

	//! the actual page number
	int m_pageNumber;

	//! page span manager
	PageSpanManager m_pageSpanManager;

	//! the current page span
	PageSpan *m_currentPageSpan;

	//! the actual graphic style
	librevenge::RVNGPropertyList m_graphicPropertyStyle;
private:
	ABWTextGeneratorPrivate(const ABWTextGeneratorPrivate &);
	ABWTextGeneratorPrivate &operator=(const ABWTextGeneratorPrivate &);

};

ABWTextGeneratorPrivate::ABWTextGeneratorPrivate(ABWDocumentHandler *pHandler) :
	m_ABWGenerator(new ABWGenerator(pHandler)),
	m_metaDataElementVector(), m_bodyElementVector(),
	m_pageNumber(0), m_pageSpanManager(), m_currentPageSpan(nullptr),
	m_graphicPropertyStyle()
{
	m_ABWGenerator->m_currentElementVector=&m_bodyElementVector;
	initPageManager();
}

ABWTextGeneratorPrivate::~ABWTextGeneratorPrivate()
{
}

////////////////////////////////////////////////////////////
// page function
////////////////////////////////////////////////////////////
void ABWTextGeneratorPrivate::startHeaderFooter(bool, const librevenge::RVNGPropertyList &)
{
	pushState();
	if (inHeaderFooter())
	{
		RVNGABW_DEBUG_MSG(("ABWTextGeneratorPrivate::startHeaderFooter: a master page is already open\n"));
		return;
	}
	getState().m_isInHeaderFooter=true;
}

void ABWTextGeneratorPrivate::endHeaderFooter()
{
	if (!inHeaderFooter())
	{
		RVNGABW_DEBUG_MSG(("ABWTextGeneratorPrivate::endHeaderFooter: can not find any open master page\n"));
		return;
	}
	popState();
}

////////////////////////////////////////////////////////////
// meta data
////////////////////////////////////////////////////////////
void ABWTextGeneratorPrivate::startSection(const librevenge::RVNGPropertyList &propList, bool dummySection)
{
	if (getFrameManager().getActualFrame())
		return;
	bool isSectionOpened=getState().m_isSectionOpened;
	pushState();
	if (!m_currentPageSpan || isSectionOpened)
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::openSection: can not open a section\n"));
		return;
	}
	if (!inHeaderFooter())
		pushStorage(m_currentPageSpan->openStorage(PageSpan::C_MainContent, getXId()));
	getState().m_isSectionOpened=true;
	getState().m_isDummySectionOpened=dummySection;
	m_currentPageSpan->updateSectionPropList(propList);
}

void ABWTextGeneratorPrivate::endSection(bool endPageSpan)
{
	if (getFrameManager().getActualFrame())
		return;
	auto state=getState();
	bool hasData=!getCurrentStorage()->empty();
	popState();
	if (!m_currentPageSpan || !state.m_isSectionOpened || inHeaderFooter())
		return;
	if (state.m_isDummySectionOpened && !hasData && !endPageSpan)
	{
		popStorage();
		m_currentPageSpan->closeStorage();
		return;
	}
	// time to send stored page's frames (if there exist)
	getFrameManager().writeContents(*getCurrentStorage(), Frame::U_Page);
	popStorage();
	// time to send the previous section (if it exists)
	if (!state.m_isDummySectionOpened || !m_currentPageSpan->isSomeMainSectionSent() || hasData)
		m_currentPageSpan->appendMainContent(m_bodyElementVector);
	else
		m_currentPageSpan->resetContent(PageSpan::C_MainContent);
	m_currentPageSpan->closeStorage();
}

std::string const ABWTextGeneratorPrivate::getParentStyleName(librevenge::RVNGPropertyList const &propList)
{
	std::string name("Standard");

	auto const *const outlineLevel = propList["text:outline-level"];
	if (outlineLevel)
	{
		std::string const headingName(m_ABWGenerator->getHeadingStyleName(outlineLevel->getInt()));
		if (!headingName.empty())
			name = headingName;
	}

	return name;
}

////////////////////////////////////////////////////////////
// meta data
////////////////////////////////////////////////////////////
void ABWTextGeneratorPrivate::setDocumentMetaData(const librevenge::RVNGPropertyList &propList)
{
	if (propList.empty()) return;
	static char const *(metaCorrespondance[])=
	{
		"dc:author", "dc.creator", "dc:creator", "dc.creator", "dc:subject", "dc.subject",
		"dc:title", "dc.title", "dc:publisher", "dc.publisher", "dc:type", "dc.type",
		"librevenge:keywords", "abiword.keywords", "dc:language", "dc.language",
		"librevenge:abstract", "dc.description"
	};
	librevenge::RVNGPropertyList metaList;
	for (int i=0; i<int(RVNGABW_N_ELEMENTS(metaCorrespondance)/2); ++i)
	{
		if (!propList[metaCorrespondance[2*i]]) continue;
		metaList.insert("key", metaCorrespondance[2*i+1]);
		m_metaDataElementVector.push_back(std::make_shared<TagOpenElement>("m", metaList));
		m_metaDataElementVector.push_back(std::make_shared<TextElement>(propList[metaCorrespondance[2*i]]->getStr()));
		m_metaDataElementVector.push_back(std::make_shared<TagCloseElement>("m"));
	}
	metaList.insert("key", "abiword.generator");
	m_metaDataElementVector.push_back(std::make_shared<TagOpenElement>("m", metaList));
	m_metaDataElementVector.push_back(std::make_shared<TextElement>("librvngabw"));
	m_metaDataElementVector.push_back(std::make_shared<TagCloseElement>("m"));
	metaList.insert("key", "dc.format");
	m_metaDataElementVector.push_back(std::make_shared<TagOpenElement>("m", metaList));
	m_metaDataElementVector.push_back(std::make_shared<TextElement>("application/x-abiword"));
	m_metaDataElementVector.push_back(std::make_shared<TagCloseElement>("m"));
}

void ABWTextGeneratorPrivate::writeDocumentMetaData(ABWDocumentHandler *pHandler)
{
	if (m_metaDataElementVector.empty()) return;
	TagOpenElement("metadata").write(pHandler);
	sendStorage(&m_metaDataElementVector, pHandler);
	pHandler->endElement("metadata");
}

void ABWTextGeneratorPrivate::initPageManager()
{
}

bool ABWTextGeneratorPrivate::writeTargetDocument(ABWDocumentHandler *pHandler)
{
	RVNGABW_DEBUG_MSG(("ABWTextGenerator: Document Body: Printing out the header stuff..\n"));

	pHandler->startDocument();

	librevenge::RVNGPropertyList docContentPropList, docContentPropPropList;
	librevenge::RVNGPropertyListVector docContentPropPropVector;
	docContentPropList.insert("template", "false");
	docContentPropList.insert("xmlns:ct", "http://www.abisource.com/changetracking.dtd");
	docContentPropList.insert("xmlns:fo", "http://www.w3.org/1999/XSL/Format");
	docContentPropList.insert("xmlns:math", "http://www.w3.org/1998/Math/MathML");
	docContentPropList.insert("xid-max", getXId());
	docContentPropList.insert("xmlns:dc", "http://purl.org/dc/elements/1.1/");
	docContentPropList.insert("fileformat", librevenge::RVNGPropertyFactory::newStringProp("1.1"));
	docContentPropList.insert("xmlns:svg", "http://www.w3.org/2000/svg");
	docContentPropList.insert("xmlns:awml", "http://www.abisource.com/awml.dtd");
	docContentPropList.insert("xmlns", "http://www.abisource.com/awml.dtd");
	docContentPropList.insert("xmlns:xlink", "http://www.w3.org/1999/xlink");
	docContentPropList.insert("version", "3.0.99");
	docContentPropList.insert("xml:space", "preserve");
	docContentPropPropList.insert("dom-dir", "ltr");
	docContentPropPropList.insert("document-footnote-restart-section", "0");
	docContentPropPropList.insert("document-endnote-type", "numeric");
	docContentPropPropList.insert("document-endnote-place-enddoc", "1");
	docContentPropPropList.insert("document-endnote-initial", "1");
	docContentPropPropList.insert("lang", "en-US");
	docContentPropPropList.insert("document-endnote-restart-section", "0");
	docContentPropPropList.insert("document-footnote-restart-page", "0");
	docContentPropPropList.insert("document-footnote-type", "numeric");
	docContentPropPropList.insert("document-footnote-initial", "1");
	docContentPropPropList.insert("document-endnote-place-endsection", "0");
	docContentPropPropVector.append(docContentPropPropList);
	docContentPropList.insert("props", docContentPropPropVector);
	pHandler->startElement("abiword", docContentPropList);

	// write out the metadata
	writeDocumentMetaData(pHandler);


	// write the styles
	m_ABWGenerator->writeStyles(pHandler);
	getListManager().write(pHandler);

	// writing out the document
	sendStorage(&m_bodyElementVector, pHandler);

	// data : image, ...
	if (!getFileDataStorage().empty())
	{
		pHandler->startElement("data", librevenge::RVNGPropertyList());
		sendStorage(&getFileDataStorage(), pHandler);
		pHandler->endElement("data");
	}
	pHandler->endElement("abiword");

	pHandler->endDocument();

	return true;
}

ABWTextGenerator::ABWTextGenerator(ABWDocumentHandler *pHandler) : m_data(new ABWTextGeneratorPrivate(pHandler))
{
}

ABWTextGenerator::~ABWTextGenerator()
{
	if (m_data)
		delete m_data;
}

void ABWTextGenerator::setDocumentMetaData(const librevenge::RVNGPropertyList &propList)
{
	m_data->setDocumentMetaData(propList);
}

void ABWTextGenerator::defineEmbeddedFont(const librevenge::RVNGPropertyList &/*propList*/)
{
	// TODO: implement me
}

void ABWTextGenerator::openPageSpan(const librevenge::RVNGPropertyList &propList)
{
	bool firstPage=m_data->m_currentPageSpan==nullptr;
	m_data->m_currentPageSpan = m_data->getPageSpanManager().add(propList);
	if (firstPage && m_data->m_currentPageSpan)
		m_data->m_currentPageSpan->writePageSize(*m_data->getCurrentStorage());
	// open a dummy section
	m_data->startSection(librevenge::RVNGPropertyList(), true);
}

void ABWTextGenerator::closePageSpan()
{
	// close the dummy section
	m_data->endSection(true);
	if (m_data->getCurrentStorage() != &m_data->getBodyStorage())
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::closePageSpan: oops something is very wrong\n\tDo you forget to close a section?\n"));
		while (m_data->popStorage())
		{
		}
		if (m_data->m_currentPageSpan)
			m_data->m_currentPageSpan->appendMainContent(m_data->m_bodyElementVector);
	}
	if (m_data->m_currentPageSpan)
		m_data->m_currentPageSpan->appendAuxiliaryContents(*m_data->getCurrentStorage());
}

void ABWTextGenerator::openHeader(const librevenge::RVNGPropertyList &propList)
{
	if (m_data->inHeaderFooter() || !m_data->m_currentPageSpan)
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::openHeader: can not open a header\n"));
		return;
	}
	m_data->startHeaderFooter(true, propList);
	if (!m_data->inHeaderFooter())
		return;

	librvngabw::DocumentElementVector *pHeaderFooterContentElements=nullptr;
	if (propList["librevenge:occurrence"] && (propList["librevenge:occurrence"]->getStr() == "even" ||
	                                          propList["librevenge:occurrence"]->getStr() == "left"))
		pHeaderFooterContentElements=m_data->m_currentPageSpan->openStorage(PageSpan::C_HeaderLeft, m_data->getXId());
	else if (propList["librevenge:occurrence"] && propList["librevenge:occurrence"]->getStr() == "first")
		pHeaderFooterContentElements=m_data->m_currentPageSpan->openStorage(PageSpan::C_HeaderFirst, m_data->getXId());
	else if (propList["librevenge:occurrence"] && propList["librevenge:occurrence"]->getStr() == "last")
		pHeaderFooterContentElements=m_data->m_currentPageSpan->openStorage(PageSpan::C_HeaderLast, m_data->getXId());
	else
		pHeaderFooterContentElements=m_data->m_currentPageSpan->openStorage(PageSpan::C_Header, m_data->getXId());

	if (!pHeaderFooterContentElements)
		m_data->pushStorage(&m_data->getDummyStorage());
	else
		m_data->pushStorage(pHeaderFooterContentElements);
}

void ABWTextGenerator::closeHeader()
{
	if (!m_data->inHeaderFooter())
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::closeHeader: no header/footer is already opened\n"));
		return;
	}
	m_data->endHeaderFooter();
	if (m_data->getCurrentStorage()!=&m_data->getDummyStorage())
		m_data->m_currentPageSpan->closeStorage();
	m_data->popStorage();
}

void ABWTextGenerator::openFooter(const librevenge::RVNGPropertyList &propList)
{
	if (m_data->inHeaderFooter() || !m_data->m_currentPageSpan)
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::openFooter: can not open a footer\n"));
		return;
	}
	m_data->startHeaderFooter(false, propList);
	if (!m_data->inHeaderFooter())
		return;

	auto *pHeaderFooterContentElements = new librvngabw::DocumentElementVector;
	if (propList["librevenge:occurrence"] && (propList["librevenge:occurrence"]->getStr() == "even" ||
	                                          propList["librevenge:occurrence"]->getStr() == "left"))
		pHeaderFooterContentElements=m_data->m_currentPageSpan->openStorage(PageSpan::C_FooterLeft, m_data->getXId());
	else if (propList["librevenge:occurrence"] && propList["librevenge:occurrence"]->getStr() == "first")
		pHeaderFooterContentElements=m_data->m_currentPageSpan->openStorage(PageSpan::C_FooterFirst, m_data->getXId());
	else if (propList["librevenge:occurrence"] && propList["librevenge:occurrence"]->getStr() == "last")
		pHeaderFooterContentElements=m_data->m_currentPageSpan->openStorage(PageSpan::C_FooterLast, m_data->getXId());
	else
		pHeaderFooterContentElements=m_data->m_currentPageSpan->openStorage(PageSpan::C_Footer, m_data->getXId());

	if (!pHeaderFooterContentElements && m_data->m_currentPageSpan)
		m_data->pushStorage(&m_data->getDummyStorage());
	else
		m_data->pushStorage(pHeaderFooterContentElements);
}

void ABWTextGenerator::closeFooter()
{
	if (!m_data->inHeaderFooter())
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::closeFooter: no header/footer is already opened\n"));
		return;
	}
	m_data->endHeaderFooter();
	if (m_data->getCurrentStorage()!=&m_data->getDummyStorage() && m_data->m_currentPageSpan)
		m_data->m_currentPageSpan->closeStorage();
	m_data->popStorage();
}

void ABWTextGenerator::openSection(const librevenge::RVNGPropertyList &propList)
{
	// close the dummy section
	m_data->endSection();
	m_data->startSection(propList);
}

void ABWTextGenerator::closeSection()
{
	m_data->endSection();
	// open the dummy section
	m_data->startSection(librevenge::RVNGPropertyList(), true);
}

void ABWTextGenerator::defineParagraphStyle(librevenge::RVNGPropertyList const &propList)
{
	librevenge::RVNGPropertyList pList(propList);
	pList.insert("style:parent-style-name", m_data->getParentStyleName(pList).c_str());
	m_data->m_ABWGenerator->defineParagraphStyle(pList);
}

void ABWTextGenerator::openParagraph(const librevenge::RVNGPropertyList &propList)
{
	librevenge::RVNGPropertyList finalPropList(propList);
	if (!m_data->getState().m_isTableCellOpened)
		finalPropList.insert("style:parent-style-name", m_data->getParentStyleName(propList).c_str());
	if (propList["fo:break-before"] && propList["fo:break-before"]->getStr()=="page")
		// first flush the page frame ( just in case we have added some frame on the current page )
		m_data->getFrameManager().writeContents(*m_data->getCurrentStorage(), Frame::U_Page);
	m_data->m_ABWGenerator->openParagraph(finalPropList);
	if (propList["fo:break-before"])
	{
		openSpan(librevenge::RVNGPropertyList());
		if (propList["fo:break-before"]->getStr()=="page")
		{
			m_data->getCurrentStorage()->push_back(std::make_shared<TagOpenElement>("pbr"));
			m_data->getCurrentStorage()->push_back(std::make_shared<TagCloseElement>("pbr"));
			++m_data->m_pageNumber;
		}
		else if (propList["fo:break-before"]->getStr()=="column")
		{
			m_data->getCurrentStorage()->push_back(std::make_shared<TagOpenElement>("cbr"));
			m_data->getCurrentStorage()->push_back(std::make_shared<TagCloseElement>("cbr"));
			// TODO: update page number if needed
		}
		closeSpan();
	}
	m_data->pushState();
	m_data->getState().m_paragraphOpenedAtCurrentLevel=true;
}

void ABWTextGenerator::closeParagraph()
{
	ABWGenerator::State const &state=m_data->getState();
	if (!state.m_paragraphOpenedAtCurrentLevel)
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::closeParagraph: can not find any opened paragraph\n"));
		return;
	}
	m_data->m_ABWGenerator->closeParagraph();
	if (!state.m_isInFrame)
	{
		// time to send stored block or column's frames (if there exist)
		m_data->getFrameManager().writeContents(*m_data->getCurrentStorage(), Frame::U_Block);
		m_data->getFrameManager().writeContents(*m_data->getCurrentStorage(), Frame::U_Column);
	}
	m_data->popState();
}

void ABWTextGenerator::defineCharacterStyle(librevenge::RVNGPropertyList const &propList)
{
	m_data->m_ABWGenerator->defineCharacterStyle(propList);
}

void ABWTextGenerator::openSpan(const librevenge::RVNGPropertyList &propList)
{
	m_data->m_ABWGenerator->openSpan(propList);
}

void ABWTextGenerator::closeSpan()
{
	m_data->m_ABWGenerator->closeSpan();
}

void ABWTextGenerator::openLink(const librevenge::RVNGPropertyList &propList)
{
	m_data->m_ABWGenerator->openLink(propList);
}

void ABWTextGenerator::closeLink()
{
	m_data->m_ABWGenerator->closeLink();
}

// -------------------------------
//      list
// -------------------------------
void ABWTextGenerator::openOrderedListLevel(const librevenge::RVNGPropertyList &propList)
{
	m_data->getListManager().openLevel(propList, true);
}

void ABWTextGenerator::openUnorderedListLevel(const librevenge::RVNGPropertyList &propList)
{
	m_data->getListManager().openLevel(propList, false);
}

void ABWTextGenerator::closeOrderedListLevel()
{
	m_data->getListManager().closeLevel();
}

void ABWTextGenerator::closeUnorderedListLevel()
{
	m_data->getListManager().closeLevel();
}

void ABWTextGenerator::openListElement(const librevenge::RVNGPropertyList &propList)
{
	if (!m_data->getListManager().openListElement(propList, *m_data->getCurrentStorage(), m_data->getXId()))
		return;

	m_data->pushState();
	m_data->getState().m_listElementOpenedAtCurrentLevel=true;
}

void ABWTextGenerator::closeListElement()
{
	auto const &state=m_data->getState();
	if (!state.m_listElementOpenedAtCurrentLevel)
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::closeListElement: can not find any opened paragraph\n"));
		return;
	}
	m_data->getListManager().closeListElement(*m_data->getCurrentStorage());
	if (!state.m_isInFrame)
	{
		// time to send stored block or column's frames (if there exist)
		m_data->getFrameManager().writeContents(*m_data->getCurrentStorage(), Frame::U_Block);
		m_data->getFrameManager().writeContents(*m_data->getCurrentStorage(), Frame::U_Column);
	}
	m_data->popState();
}

void ABWTextGenerator::openFootnote(const librevenge::RVNGPropertyList &propList)
{
	m_data->pushListState();
	m_data->m_ABWGenerator->openNote(propList, true);
}

void ABWTextGenerator::closeFootnote()
{
	m_data->m_ABWGenerator->closeNote(true);
	m_data->popListState();
}

void ABWTextGenerator::openEndnote(const librevenge::RVNGPropertyList &propList)
{
	m_data->pushListState();
	m_data->m_ABWGenerator->openNote(propList, false);
}

void ABWTextGenerator::closeEndnote()
{
	m_data->m_ABWGenerator->closeNote(false);
	m_data->popListState();
}

void ABWTextGenerator::openComment(const librevenge::RVNGPropertyList &propList)
{
	m_data->pushListState();
	m_data->m_ABWGenerator->openAnnotation(propList);
}

void ABWTextGenerator::closeComment()
{
	m_data->m_ABWGenerator->closeAnnotation();
	m_data->popListState();
}

void ABWTextGenerator::openTable(const librevenge::RVNGPropertyList &propList)
{
	if (m_data->inNoteOrInAnnotation())
		return;
	auto table=m_data->getTableManager().openTable(propList, m_data->getXId());
	if (!table)
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::openTable: can not open a table\n"));
		return;
	}
	m_data->pushStorage(table->getStorage());
	m_data->pushState();
}

void ABWTextGenerator::closeTable()
{
	if (m_data->inNoteOrInAnnotation())
		return;
	m_data->popState();
	m_data->popStorage();
	auto table=m_data->getTableManager().getActualTable();
	if (!table)
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::closeTable: can not find any opened table\n"));
		return;
	}
	table->checkTable();
	table->write(*m_data->getCurrentStorage());
	m_data->getTableManager().closeTable();
}

void ABWTextGenerator::openTableRow(const librevenge::RVNGPropertyList &propList)
{
	if (m_data->inNoteOrInAnnotation() || !m_data->getTableManager().getActualTable())
		return;
	m_data->getTableManager().getActualTable()->openRow(propList);
}

void ABWTextGenerator::closeTableRow()
{
	if (m_data->inNoteOrInAnnotation() || !m_data->getTableManager().getActualTable())
		return;
	m_data->getTableManager().getActualTable()->closeRow();
}

void ABWTextGenerator::openTableCell(const librevenge::RVNGPropertyList &propList)
{
	auto table=m_data->getTableManager().getActualTable();
	if (m_data->inNoteOrInAnnotation() || !table)
		return;

	m_data->getState().m_isTableCellOpened = table->openCell(propList);
	if (m_data->getState().m_isTableCellOpened)
		m_data->pushStorage(table->getStorage());
}

void ABWTextGenerator::closeTableCell()
{
	if (m_data->inNoteOrInAnnotation())
		return;
	if (m_data->getState().m_isTableCellOpened && m_data->getTableManager().getActualTable())
	{
		m_data->getTableManager().getActualTable()->closeCell();
		m_data->popStorage();
	}
	m_data->getState().m_isTableCellOpened=false;
}

void ABWTextGenerator::insertCoveredTableCell(const librevenge::RVNGPropertyList &propList)
{
	if (m_data->inNoteOrInAnnotation() || !m_data->getTableManager().getActualTable())
		return;
	m_data->getTableManager().getActualTable()->insertCoveredCell(propList);
}

void ABWTextGenerator::insertTab()
{
	librevenge::RVNGString tab;
	tab.append(0x9);
	m_data->getCurrentStorage()->push_back(std::make_shared<TextElement>(tab));
}

void ABWTextGenerator::insertSpace()
{
	m_data->getCurrentStorage()->push_back(std::make_shared<TextElement>(" "));
}

void ABWTextGenerator::insertLineBreak()
{
	m_data->getCurrentStorage()->push_back(std::make_shared<TagOpenElement>("br"));
	m_data->getCurrentStorage()->push_back(std::make_shared<TagCloseElement>("br"));
}

void ABWTextGenerator::insertField(const librevenge::RVNGPropertyList &propList)
{
	m_data->m_ABWGenerator->insertField(propList);
}

void ABWTextGenerator::insertText(const librevenge::RVNGString &text)
{
	if (text.empty()) return;
	m_data->getCurrentStorage()->push_back(std::make_shared<TextElement>(text));
}

void ABWTextGenerator::openFrame(const librevenge::RVNGPropertyList &propList)
{
	m_data->pushListState();
	librevenge::RVNGPropertyList pList(propList);
	if (!propList["text:anchor-type"])
		pList.insert("text:anchor-type","paragraph");
	auto frame=m_data->getFrameManager().openFrame(pList, m_data->getXId());
	if (frame && frame->getAnchorType()==Frame::A_Page)
		m_data->updateFramePageNumber(frame->getPageNumber());
}

void ABWTextGenerator::closeFrame()
{
	m_data->popListState();

	auto frame=m_data->getFrameManager().getActualFrame();
	if (!frame) return;

	m_data->getFrameManager().closeFrame();
	if (frame->getAnchorToUse()==Frame::U_Page || frame->isEmpty())
		return;
	auto const &state=m_data->getState();
	if (state.m_paragraphOpenedAtCurrentLevel || state.m_listElementOpenedAtCurrentLevel)
		return;
	frame->write(*m_data->getCurrentStorage(), librevenge::RVNGPropertyList());
}

void ABWTextGenerator::insertBinaryObject(const librevenge::RVNGPropertyList &propList)
{
	if (!m_data->getFrameManager().isFrameOpened())   // Image without a frame simply doesn't make sense for us
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::insertBinaryObject: can not find the current frame\n"));
		return;
	}
	m_data->insertBinaryObject(propList);
}

void ABWTextGenerator::openGroup(const librevenge::RVNGPropertyList &/*propList*/)
{
	//m_data->openGroup(propList);
}

void ABWTextGenerator::closeGroup()
{
	//m_data->closeGroup();
}

void ABWTextGenerator::defineGraphicStyle(const librevenge::RVNGPropertyList &propList)
{
	m_data->m_graphicPropertyStyle=propList;
}

void ABWTextGenerator::drawRectangle(const librevenge::RVNGPropertyList &propList)
{
	ABWSVGDrawingGenerator svgGenerator;
	svgGenerator.setStyle(m_data->m_graphicPropertyStyle);
	svgGenerator.drawRectangle(propList);
	librevenge::RVNGString data;
	double minX, minY, maxX, maxY;
	if (svgGenerator.getPicture(data, minX, minY, maxX, maxY))
		m_data->insertSVGBinaryObject(data, propList, minX, minY, maxX, maxY);
}

void ABWTextGenerator::drawEllipse(const librevenge::RVNGPropertyList &propList)
{
	ABWSVGDrawingGenerator svgGenerator;
	svgGenerator.setStyle(m_data->m_graphicPropertyStyle);
	svgGenerator.drawEllipse(propList);
	librevenge::RVNGString data;
	double minX, minY, maxX, maxY;
	if (svgGenerator.getPicture(data, minX, minY, maxX, maxY))
		m_data->insertSVGBinaryObject(data, propList, minX, minY, maxX, maxY);
}


void ABWTextGenerator::drawPolygon(const librevenge::RVNGPropertyList &propList)
{
	ABWSVGDrawingGenerator svgGenerator;
	svgGenerator.setStyle(m_data->m_graphicPropertyStyle);
	svgGenerator.drawPolygon(propList);
	librevenge::RVNGString data;
	double minX, minY, maxX, maxY;
	if (svgGenerator.getPicture(data, minX, minY, maxX, maxY))
		m_data->insertSVGBinaryObject(data, propList, minX, minY, maxX, maxY);
}


void ABWTextGenerator::drawPolyline(const librevenge::RVNGPropertyList &propList)
{
	ABWSVGDrawingGenerator svgGenerator;
	svgGenerator.setStyle(m_data->m_graphicPropertyStyle);
	svgGenerator.drawPolyline(propList);
	librevenge::RVNGString data;
	double minX, minY, maxX, maxY;
	if (svgGenerator.getPicture(data, minX, minY, maxX, maxY))
		m_data->insertSVGBinaryObject(data, propList, minX, minY, maxX, maxY);
}


void ABWTextGenerator::drawPath(const librevenge::RVNGPropertyList &propList)
{
	ABWSVGDrawingGenerator svgGenerator;
	svgGenerator.setStyle(m_data->m_graphicPropertyStyle);
	svgGenerator.drawPath(propList);
	librevenge::RVNGString data;
	double minX, minY, maxX, maxY;
	if (svgGenerator.getPicture(data, minX, minY, maxX, maxY))
		m_data->insertSVGBinaryObject(data, propList, minX, minY, maxX, maxY);
}

void ABWTextGenerator::drawConnector(const librevenge::RVNGPropertyList &/*propList*/)
{
	//m_data->drawConnector(propList);
}

void ABWTextGenerator::openTextBox(const librevenge::RVNGPropertyList &/*propList*/)
{
	auto frame=m_data->getFrameManager().getActualFrame();
	if (!frame)   // Text box without a frame simply doesn't make sense for us
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::openTextBox: can not find the current frame\n"));
		return;
	}
	m_data->pushListState();
	m_data->pushState();

	auto &state = m_data->getState();
	state.m_isInFrame = state.m_isTextBoxOpened = true;
	if (m_data->inHeaderFooter())
	{
		static bool first=true;
		if (first)
		{
			first=false;
			RVNGABW_DEBUG_MSG(("ABWGenerator::openTextBox: can not create textbox in header footer\n"));
		}
		m_data->pushStorage(&m_data->getDummyStorage());
		return;
	}
	static bool first=true;
	if (first)
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::openTextBox: called with some textboxes, check result\n"));
		first=false;
	}
	m_data->pushStorage(&frame->getStorage("textbox"));
}

void ABWTextGenerator::closeTextBox()
{
	if (!m_data->getState().m_isTextBoxOpened)
		return;
	m_data->popListState();
	m_data->popState();
	m_data->popStorage();
}

void ABWTextGenerator::defineSectionStyle(librevenge::RVNGPropertyList const &)
{
}

void ABWTextGenerator::insertEquation(librevenge::RVNGPropertyList const &)
{
}

void ABWTextGenerator::endDocument()
{
	if (!m_data->m_ABWGenerator->m_documentHandler)
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::endDocument: can not find any document handler\n"));
		return;
	}
	if (m_data->getFramePageNumber()>m_data->m_pageNumber+1)
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::endDocument: must add some pagebreak to be sure that all frame appear\n"));
		// we must insert some page break, so let reopened a section to add some page break
		librevenge::RVNGPropertyList propList;
		propList.insert("xid", ++m_data->getXId());
		m_data->getCurrentStorage()->push_back(std::make_shared<TagOpenElement>("section", propList));
		propList.insert("xid", ++m_data->getXId());
		m_data->getCurrentStorage()->push_back(std::make_shared<TagOpenElement>("p", propList));
		for (int i=m_data->m_pageNumber+1; i<m_data->getFramePageNumber(); ++i)
		{
			m_data->getCurrentStorage()->push_back(std::make_shared<TagOpenElement>("pbr"));
			m_data->getCurrentStorage()->push_back(std::make_shared<TagCloseElement>("pbr"));
		}
		m_data->getCurrentStorage()->push_back(std::make_shared<TagCloseElement>("p"));
		m_data->getCurrentStorage()->push_back(std::make_shared<TagCloseElement>("section"));
	}
	m_data->writeTargetDocument(m_data->m_ABWGenerator->m_documentHandler);
}

void ABWTextGenerator::startDocument(const librevenge::RVNGPropertyList &)
{
}

void ABWTextGenerator::definePageStyle(librevenge::RVNGPropertyList const &)
{
}

void ABWTextGenerator::registerCheckImageHandler(ABWCheckImage checkHandler, bool useDefault)
{
	m_data->m_ABWGenerator->registerCheckImageHandler(checkHandler, useDefault);
}

void ABWTextGenerator::registerEmbeddedImageHandler(const librevenge::RVNGString &mimeType, ABWEmbeddedImage imageHandler)
{
	m_data->m_ABWGenerator->registerEmbeddedImageHandler(mimeType, imageHandler);
}

void ABWTextGenerator::registerEmbeddedObjectHandler(const librevenge::RVNGString &mimeType, ABWEmbeddedObject objectHandler)
{
	m_data->m_ABWGenerator->registerEmbeddedObjectHandler(mimeType, objectHandler);
}
}

/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */
