/* -*- 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 "ABWGenerator.hxx"

namespace librvngabw
{
ABWGenerator::ABWGenerator(ABWDocumentHandler *pHandler) :
	m_documentHandler(pHandler),
	m_stateStack(),
	m_currentElementVector(&m_dummyElementVector), m_elementVectorStack(), m_noteElementVector(), m_fileDataElementVector(), m_dummyElementVector(),
	m_xId(0), m_annotationId(0), m_footnoteId(0), m_endnoteId(0), m_imageId(0),
	m_spanManager(), m_paragraphManager(), m_listManager(m_paragraphManager), m_frameManager(), m_tableManager(m_spanManager, m_paragraphManager),
	m_idSpanListMap(), m_idSpanNameMap(), m_lastSpanName(""),
	m_idParagraphListMap(),
	m_framePageNumber(0),
	m_imageMineTypeToGenericTypeMap(), m_imageMineTypeSet(), m_useImageMineTypeSet(true), m_imageCheckHandler(nullptr), m_imageHandlerMap(), m_objectHandlerMap(),
	m_usedHeadingStyles()
{
	pushState();
	// list of knowned abiword picture, to be checked
	static char const *(imageMimeType[])=
	{
		"image/bmp", "image/x-bmp", nullptr,
		"image/x-emf", "image/emf", "application/emf", "application/x-emf", nullptr,
		"image/gif", "image/x-gif", nullptr,
		"image/jpeg", "image/x-jpeg", "image/jpg", "image/x-jpg", nullptr,
		"image/png", "image/x-png", nullptr,
		"image/svg+xml", "image/svg", "image/x-svg", nullptr,
		"image/tiff", "image/tif", "image/x-tif", "image/x-tiff", nullptr,
		"image/x-wmf", "image/wmf", "windows/metafile", nullptr,
		"image/x-wpg", "image/wpg", nullptr
	};
	char const *finalMimeType=nullptr;
	for (auto &i : imageMimeType)
	{
		if (i==nullptr)
		{
			finalMimeType=nullptr;
			continue;
		}
		m_imageMineTypeSet.insert(i);
		if (finalMimeType)
			m_imageMineTypeToGenericTypeMap[i]=finalMimeType;
		else
			finalMimeType=i;
	}
}

ABWGenerator::~ABWGenerator()
{
}

////////////////////////////////////////////////////////////
// embedded
////////////////////////////////////////////////////////////
ABWEmbeddedImage ABWGenerator::findEmbeddedImageHandler(const librevenge::RVNGString &mimeType) const
{
	auto it = m_imageHandlerMap.find(mimeType);
	if (it != m_imageHandlerMap.end())
		return it->second;

	return nullptr;
}

ABWEmbeddedObject ABWGenerator::findEmbeddedObjectHandler(const librevenge::RVNGString &mimeType) const
{
	auto it = m_objectHandlerMap.find(mimeType);
	if (it != m_objectHandlerMap.end())
		return it->second;

	return nullptr;
}

bool ABWGenerator::checkImage(librevenge::RVNGString &mimeType, const librevenge::RVNGBinaryData &image) const
{
	if (m_imageMineTypeToGenericTypeMap.find(mimeType)!=m_imageMineTypeToGenericTypeMap.end())
		mimeType=m_imageMineTypeToGenericTypeMap.find(mimeType)->second;
	if (m_imageCheckHandler && (*m_imageCheckHandler)(mimeType, image))
		return true;
	if (m_useImageMineTypeSet && m_imageMineTypeSet.find(mimeType)!=m_imageMineTypeSet.end())
		return true;
	return false;
}

////////////////////////////////////////////////////////////
// font/paragraph
////////////////////////////////////////////////////////////
void ABWGenerator::defineCharacterStyle(const librevenge::RVNGPropertyList &propList)
{
	if (!propList["librevenge:span-id"])
	{
		RVNGABW_DEBUG_MSG(("ABWGenerator::defineCharacterStyle: called without id\n"));
		return;
	}
	m_idSpanListMap[propList["librevenge:span-id"]->getInt()]=propList;
}

void ABWGenerator::openSpan(const librevenge::RVNGPropertyList &propList)
{
	librevenge::RVNGString sName("");
	librevenge::RVNGPropertyList pList(propList);
	if (pList["librevenge:span-id"])
	{
		int id=pList["librevenge:span-id"]->getInt();
		if (m_idSpanNameMap.find(id)!=m_idSpanNameMap.end())
			sName=m_idSpanNameMap.find(id)->second;
		else if (m_idSpanListMap.find(id)!=m_idSpanListMap.end())
			pList=m_idSpanListMap.find(id)->second;
		else
		{
			RVNGABW_DEBUG_MSG(("ABWGenerator::openSpan: can not find the style %d\n", id));
			pList.clear();
		}
	}

	if (sName.empty())
	{
		sName = m_spanManager.findOrAdd(pList);
		if (pList["librevenge:span-id"])
			m_idSpanNameMap[pList["librevenge:span-id"]->getInt()]=sName;
	}
	std::shared_ptr<TagOpenElement> pSpanOpenElement(new TagOpenElement("c"));
	pSpanOpenElement->addAttribute("style", sName.cstr());
	pSpanOpenElement->addAttribute("xid", ++m_xId);
	m_currentElementVector->push_back(pSpanOpenElement);
	m_lastSpanName=sName;
}

void ABWGenerator::closeSpan()
{
	m_currentElementVector->push_back(std::make_shared<TagCloseElement>("c"));
}

void ABWGenerator::defineParagraphStyle(const librevenge::RVNGPropertyList &propList)
{
	if (!propList["librevenge:paragraph-id"])
	{
		RVNGABW_DEBUG_MSG(("ABWGenerator::defineParagraphStyle: called without id\n"));
		return;
	}
	m_idParagraphListMap[propList["librevenge:paragraph-id"]->getInt()]=propList;
}

void ABWGenerator::openParagraph(const librevenge::RVNGPropertyList &propList)
{
	librevenge::RVNGPropertyList pList(propList);
	if (pList["librevenge:paragraph-id"])
	{
		int id=pList["librevenge:paragraph-id"]->getInt();
		if (m_idParagraphListMap.find(id)!=m_idParagraphListMap.end())
			pList=m_idParagraphListMap.find(id)->second;
		else
		{
			RVNGABW_DEBUG_MSG(("ABWGenerator::openParagraph: can not find the style %d\n", id));
			pList.clear();
		}
	}

	librevenge::RVNGPropertyList paraList, propPropList;
	auto paragraphName= m_paragraphManager.findOrAdd(pList, propPropList);

	// create a document element corresponding to the paragraph, and append it to our list of document elements
	paraList.insert("style", paragraphName);
	paraList.insert("xid", ++m_xId);
	if (!propPropList.empty())
	{
		librevenge::RVNGPropertyListVector propPropVector;
		propPropVector.append(propPropList);
		paraList.insert("props", propPropVector);
	}
	m_currentElementVector->push_back(std::make_shared<TagOpenElement>("p", paraList));
	if (getState().m_isInNote && !getNoteStorage().empty())
		librvngabw::spliceOrigDest(getNoteStorage(), *m_currentElementVector);
}

void ABWGenerator::closeParagraph()
{
	m_currentElementVector->push_back(std::make_shared<TagCloseElement>("p"));
}

std::string const ABWGenerator::getHeadingStyleName(int const level)
{
	if ((0 < level) && (5 > level)) // abiword only recognizes 4 levels
	{
		auto it = m_usedHeadingStyles.find(level);
		if (m_usedHeadingStyles.end() == it)
		{
			librevenge::RVNGString tmpName;
			tmpName.sprintf("Heading %d", level);
			it = m_usedHeadingStyles.insert(std::make_pair(level, tmpName.cstr())).first;
		}
		return it->second;
	}
	return "";
}

////////////////////////////////////////////////////////////
// field/link
////////////////////////////////////////////////////////////
void ABWGenerator::insertField(const librevenge::RVNGPropertyList &propList)
{
	if (!propList["librevenge:field-type"] || propList["librevenge:field-type"]->getStr().empty())
		return;

	const auto &type = propList["librevenge:field-type"]->getStr();
	librevenge::RVNGPropertyList pList;
	if (type=="text:title")
		pList.insert("type","file_name"); // or meta_title
	else if (type=="text:page-number")
		pList.insert("type", "page_number");
	else if (type=="text:page-count")
		pList.insert("type", "page_count");
	else if (type=="text:date") // checkme
		pList.insert("type", "date");
	else if (type=="text:time") // checkme
		pList.insert("type", "time");
	else
	{
		RVNGABW_DEBUG_MSG(("ABWTextGenerator::insertField: find unknown type=%s\n", type.cstr()));
		return;
	}
	pList.insert("xid", ++m_xId);
	getCurrentStorage()->push_back(std::make_shared<TagOpenElement>("field", pList));
	getCurrentStorage()->push_back(std::make_shared<TagCloseElement>("field"));
}

void ABWGenerator::openLink(const librevenge::RVNGPropertyList &propList)
{
	if (!propList["librevenge:type"])
	{
		RVNGABW_DEBUG_MSG(("ABWGenerator::openLink: linked type is not defined, assume link\n"));
	}
	std::shared_ptr<TagOpenElement> pLinkOpenElement(new TagOpenElement("a"));
	librevenge::RVNGPropertyList::Iter i(propList);
	pLinkOpenElement->addAttribute("xid", ++m_xId);
	if (propList["xlink:href"])
		pLinkOpenElement->addAttribute("xlink:href", propList["xlink:href"]->getStr());
	else
	{
		RVNGABW_DEBUG_MSG(("ABWGenerator::openLink: can not find any references, assume unknow\n"));
		pLinkOpenElement->addAttribute("xlink:href","http://localhost/");
	}
	getCurrentStorage()->push_back(pLinkOpenElement);
}

void ABWGenerator::closeLink()
{
	getCurrentStorage()->push_back(std::make_shared<TagCloseElement>("a"));
}


////////////////////////////////////////////////////////////
// list
////////////////////////////////////////////////////////////
void ABWGenerator::popListState()
{
	m_listManager.popState();
}

void ABWGenerator::pushListState()
{
	m_listManager.pushState();
}

////////////////////////////////////////////////////////////
// note and annotation
////////////////////////////////////////////////////////////
void ABWGenerator::openNote(const librevenge::RVNGPropertyList &/*propList*/, bool footnote)
{
	std::shared_ptr<TagOpenElement> pOpenEndNote(new TagOpenElement("field"));
	pOpenEndNote->addAttribute("xid", ++m_xId);
	int noteId=footnote ? m_footnoteId++ : m_endnoteId++;
	std::string noteIdString(footnote ? "footnote-id" : "endnote-id");
	pOpenEndNote->addAttribute(noteIdString.c_str(), noteId);
	pOpenEndNote->addAttribute("type", footnote ? "footnote_ref" : "endnote_ref");
	pOpenEndNote->addAttribute("style", "FootEndNoteRef");
	// fixme check propList["librevenge:number"] and propList["text:label"]
	getCurrentStorage()->push_back(pOpenEndNote);
	getCurrentStorage()->push_back(std::make_shared<TagCloseElement>("field"));

	pOpenEndNote.reset(new TagOpenElement(footnote ? "foot" : "endnote"));
	pOpenEndNote->addAttribute("xid", ++m_xId);
	pOpenEndNote->addAttribute(noteIdString.c_str(), noteId);
	getCurrentStorage()->push_back(pOpenEndNote);

	// the anchor. Note: we stored it in noteElementVector as it must insert in a paragraph
	pOpenEndNote.reset(new TagOpenElement("field"));
	pOpenEndNote->addAttribute("xid", ++m_xId);
	pOpenEndNote->addAttribute(noteIdString.c_str(), noteId);
	pOpenEndNote->addAttribute("type", footnote ? "footnote_anchor" : "endnote_anchor");
	getNoteStorage().push_back(pOpenEndNote);
	getNoteStorage().push_back(std::make_shared<TagCloseElement>("field"));

	getState().m_isInNote = true;

}

void ABWGenerator::closeNote(bool footnote)
{
	getState().m_isInNote = false;
	if (!getNoteStorage().empty())
	{
		// we need to insert the note anchor
		librevenge::RVNGPropertyList propList;
		propList.insert("xid", ++m_xId);
		getCurrentStorage()->push_back(std::make_shared<TagOpenElement>("p", propList));
		librvngabw::spliceOrigDest(getNoteStorage(), *getCurrentStorage());
		getCurrentStorage()->push_back(std::make_shared<TagCloseElement>("p"));
	}
	getCurrentStorage()->push_back(std::make_shared<TagCloseElement>(footnote ? "foot" : "endnote"));
}

void ABWGenerator::openAnnotation(const librevenge::RVNGPropertyList &/*propList*/)
{
	RVNGABW_DEBUG_MSG(("ABWGenerator::openAnnotation called, checkme\n"));

	std::shared_ptr<TagOpenElement> pAnnotationNote(new TagOpenElement("ann"));
	pAnnotationNote->addAttribute("xid", ++m_xId);
	int noteId=m_annotationId++;
	pAnnotationNote->addAttribute("annotation", noteId);
	getCurrentStorage()->push_back(pAnnotationNote);

	pAnnotationNote.reset(new TagOpenElement("annotate"));
	pAnnotationNote->addAttribute("xid", ++m_xId);
	pAnnotationNote->addAttribute("annotation-id", noteId);
	getCurrentStorage()->push_back(pAnnotationNote);

	getState().m_isInAnnotation = true;
}

void ABWGenerator::closeAnnotation()
{
	getState().m_isInAnnotation = false;
	getCurrentStorage()->push_back(std::make_shared<TagCloseElement>("annotate"));
	getCurrentStorage()->push_back(std::make_shared<TagCloseElement>("ann"));
}

////////////////////////////////////////////////////////////
// storage
////////////////////////////////////////////////////////////
void ABWGenerator::insertSVGBinaryObject(const librevenge::RVNGString &picture,
                                         const librevenge::RVNGPropertyList &propList,
                                         double const &minx, double const &miny, double const &maxx, double const &maxy)
{
	if (picture.empty())
	{
		RVNGABW_DEBUG_MSG(("ABWGenerator::insertSVGBinaryObject: can not find picture\n"));
		return;
	}

	librevenge::RVNGPropertyList pList(propList);
	pList.insert("svg:x", minx, librevenge::RVNG_INCH);
	pList.insert("svg:y", miny, librevenge::RVNG_INCH);
	pList.insert("svg:width", maxx-minx, librevenge::RVNG_INCH);
	pList.insert("svg:height", maxy-miny, librevenge::RVNG_INCH);

	auto frame=m_frameManager.openFrame(pList, m_xId);
	if (!frame) return;
	if (frame->getAnchorType()==Frame::A_Page)
		updateFramePageNumber(frame->getPageNumber());
	librevenge::RVNGBinaryData data;
	data.append((unsigned char const *)picture.cstr(), strlen(picture.cstr()));
	writeBinaryObject(data.getBase64Data(),"image/svg+xml");
	m_frameManager.closeFrame();

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

void ABWGenerator::insertBinaryObject(const librevenge::RVNGPropertyList &propList)
{
	auto frame=m_frameManager.getActualFrame();
	if (!frame) return;
	Frame::AnchorToUse anchor=frame->getAnchorToUse();

	unsigned long num=1;
	auto const *replacements=propList.child("librevenge:replacement-objects");
	if (replacements)
		num+=replacements->count();
	for (unsigned long c=0; c<num; ++c)
	{
		const librevenge::RVNGPropertyList &pList=c==0 ? propList : (*replacements)[c-1];
		if (!pList["office:binary-data"] || !pList["librevenge:mime-type"])
		{
			RVNGABW_DEBUG_MSG(("ABWGenerator::insertBinaryObject: can not find data or mimetype\n"));
			continue;
		}

		auto tmpObjectHandler = findEmbeddedObjectHandler(pList["librevenge:mime-type"]->getStr());
		if (tmpObjectHandler && anchor!=Frame::U_Cell)
		{
			if (getState().m_isInHeaderFooter)
			{
				static bool first=true;
				if (first)
				{
					first=false;
					RVNGABW_DEBUG_MSG(("ABWGenerator::insertBinaryObject[embedded]: can not create frame in header/footer\n"));
				}
				continue;
			}

			// first save all the state and reset them to original
			auto stateStack=m_stateStack;
			while (!m_stateStack.empty())
				m_stateStack.pop();
			pushState();

			auto listStateStack=m_listManager.getStack();
			while (!m_listManager.getStack().empty())
				m_listManager.getStack().pop();
			pushListState();

			DocumentElementVector imageStorage;
			pushStorage(&getDummyStorage()); // save current element and set it to dummy
			auto elementVectorStack=m_elementVectorStack;
			while (!m_elementVectorStack.empty())
				m_elementVectorStack.pop();
			pushStorage(&imageStorage);

			bool ok=false;
			try
			{
				librevenge::RVNGBinaryData data(pList["office:binary-data"]->getStr());
				ok=tmpObjectHandler(data, *this);
			}
			catch (...)
			{
				RVNGABW_DEBUG_MSG(("ABWGenerator::insertBinaryObject: ARGHH, catch an exception when decoding data!!!\n"));
				ok=false;
			}
			m_stateStack=stateStack;
			m_listManager.getStack()=listStateStack;
			m_elementVectorStack=elementVectorStack;
			popStorage();  // restore current element
			if (ok)
			{
				librvngabw::spliceOrigDest(imageStorage, frame->getStorage("textbox"));
				return;
			}
			continue;
		}

		librevenge::RVNGString finalOutput(""), mimeType("");
		auto tmpImageHandler = findEmbeddedImageHandler(pList["librevenge:mime-type"]->getStr());
		if (tmpImageHandler)
		{
			try
			{
				librevenge::RVNGBinaryData data(pList["office:binary-data"]->getStr());
				librevenge::RVNGBinaryData output;
				if (tmpImageHandler(data, output))
					finalOutput=output.getBase64Data();
			}
			catch (...)
			{
				RVNGABW_DEBUG_MSG(("ABWGenerator::insertBinaryObject: ARGHH, catch an exception when decoding data!!!\n"));
				finalOutput="";
			}
		}
		else
			// assuming we have a binary image or a object_ole that we can just insert as it is
		{
			mimeType=pList["librevenge:mime-type"]->getStr();
			try
			{
				finalOutput=pList["office:binary-data"]->getStr();
			}
			catch (...)
			{
				RVNGABW_DEBUG_MSG(("ABWGenerator::insertBinaryObject: ARGHH, catch an exception when decoding picture!!!\n"));
				finalOutput="";
			}
		}
		if (finalOutput.empty())
			continue;
		if (writeBinaryObject(finalOutput, mimeType))
			return;
	}
	static bool first=true;
	if (first)
	{
		first=false;
		RVNGABW_DEBUG_MSG(("ABWGenerator::writeBinaryObject: can not find any image to send\n"));
	}
}

bool ABWGenerator::writeBinaryObject(const librevenge::RVNGString &picture, const librevenge::RVNGString &mime)
{
	auto frame=m_frameManager.getActualFrame();
	if (!frame) return false;
	try
	{
		librevenge::RVNGPropertyList dataList;
		librevenge::RVNGString name;
		name.sprintf("Image%d", ++m_imageId);
		dataList.insert("name", name);
		dataList.insert("base64", "yes");
		librevenge::RVNGString mimeType(mime);
		if (mimeType.empty() || mimeType=="image/pict")
			mimeType=PictureManager::getType(picture);
		librevenge::RVNGBinaryData data(picture);
		// AbiWord seems to do weird thing if it does not know a picture format, so ...
		if (!checkImage(mimeType,data))
		{
			static bool first=true;
			if (first)
			{
				first=false;
				RVNGABW_DEBUG_MSG(("ABWGenerator::writeBinaryObject: can not find some image mime-type\n"));
			}
			return false;
		}
		if (mimeType.empty())
			dataList.insert("mime-type", "image/pict");
		else
			dataList.insert("mime-type", mimeType);
		m_fileDataElementVector.push_back(std::make_shared<TagOpenElement>("d", dataList));
		m_fileDataElementVector.push_back(std::make_shared<CharDataElement>(picture.cstr()));
		m_fileDataElementVector.push_back(std::make_shared<TagCloseElement>("d"));

		// we need to create the frame storage
		bool createImageFrame=frame->getAnchorType()==Frame::A_Page || frame->getAnchorType()==Frame::A_Section || frame->getAnchorType()==Frame::A_Frame;

		if (createImageFrame && !getState().m_isInFrame)
		{
			if (getState().m_isInHeaderFooter)
			{
				static bool first=true;
				if (first)
				{
					first=false;
					RVNGABW_DEBUG_MSG(("ABWGenerator::writeBinaryObject: can not create frame in header/footer\n"));
				}
				return false;
			}
			frame->setFrameType("image");
			frame->setBackgroundImage(name);
			if (frame->getAnchorToUse()!=Frame::U_Page)
				frame->write(*m_currentElementVector, librevenge::RVNGPropertyList());
		}
		else
		{
			bool needPara=!getState().m_paragraphOpenedAtCurrentLevel;
			if (needPara)
			{
				librevenge::RVNGPropertyList paraList;
				paraList.insert("xid", ++m_xId);
				m_currentElementVector->push_back(std::make_shared<TagOpenElement>("p", paraList));
			}
			librevenge::RVNGPropertyList imageList, imagePropList;
			imageList.insert("xid", ++m_xId);
			imageList.insert("dataid", name);
			imageList.insert("alt", "");
			imageList.insert("title", "");
			frame->addDimensionsTo(imagePropList);

			if (!imagePropList.empty())
			{
				librevenge::RVNGPropertyListVector imagePropVector;
				imagePropVector.append(imagePropList);
				imageList.insert("props", imagePropVector);
			}

			m_currentElementVector->push_back(std::make_shared<TagOpenElement>("image", imageList));
			m_currentElementVector->push_back(std::make_shared<TagCloseElement>("image"));
			if (needPara)
				m_currentElementVector->push_back(std::make_shared<TagCloseElement>("p"));
		}
	}
	catch (...)
	{
		RVNGABW_DEBUG_MSG(("ABWGenerator::writeBinaryObject: ARGHH, catch an exception when storing the picture!!!\n"));
		return false;
	}
	return true;
}

////////////////////////////////////////////////////////////
// storage
////////////////////////////////////////////////////////////
void ABWGenerator::sendStorage(DocumentElementVector const *storage, ABWDocumentHandler *pHandler)
{
	if (!storage)
	{
		RVNGABW_DEBUG_MSG(("ABWGenerator::sendStorage: called without storage\n"));
		return;
	}
	for (const auto &i : *storage)
	{
		if (i)i->write(pHandler);
	}
}

void ABWGenerator::pushStorage(DocumentElementVector *newStorage)
{
	if (!newStorage)
	{
		RVNGABW_DEBUG_MSG(("ABWGenerator::pushStorage: called without storage\n"));
		return;
	}
	m_elementVectorStack.push(m_currentElementVector);
	m_currentElementVector=newStorage;
}

bool ABWGenerator::popStorage()
{
	if (m_elementVectorStack.empty())
	{
		RVNGABW_DEBUG_MSG(("ABWGenerator::popStorage: the stack is empty\n"));
		return false;
	}
	m_currentElementVector=m_elementVectorStack.top();
	m_elementVectorStack.pop();
	return false;
}

////////////////////////////////////////////////////////////
// style
////////////////////////////////////////////////////////////
void ABWGenerator::writeStyles(ABWDocumentHandler *pHandler)
{
	TagOpenElement("styles").write(pHandler);

	if (m_footnoteId || m_endnoteId)
	{
		librevenge::RVNGPropertyList defaultSpan, defaultSpanProp;
		librevenge::RVNGPropertyListVector defaultSpanPropVector;
		defaultSpan.insert("name", "FootEndNoteRef");
		defaultSpan.insert("type", "C");
		defaultSpanProp.insert("text-position","superscript");
		defaultSpanProp.insert("font-size","10pt");
		defaultSpanPropVector.append(defaultSpanProp);
		defaultSpan.insert("props", defaultSpanPropVector);
		pHandler->startElement("s", defaultSpan);
		pHandler->endElement("s");
	}
	m_spanManager.write(pHandler);

	librevenge::RVNGPropertyList defaultPara, defaultParaProp;
	librevenge::RVNGPropertyListVector defaultParaPropVector;
	defaultPara.insert("name", "Standard");
	defaultPara.insert("type", "P");
	defaultParaProp.insert("margin-bottom", "0in");
	defaultParaProp.insert("margin-left", "0in");
	defaultParaProp.insert("margin-right", "0in");
	defaultParaProp.insert("margin-top", "0in");
	defaultParaProp.insert("line-height", "1.");
	defaultParaPropVector.append(defaultParaProp);
	defaultPara.insert("props", defaultParaPropVector);
	pHandler->startElement("s", defaultPara);
	pHandler->endElement("s");

	for (auto &it : m_usedHeadingStyles)
	{
		librevenge::RVNGPropertyList attrList;
		attrList.insert("name", it.second.c_str());
		attrList.insert("type", "P");
		attrList.insert("basedon", "Standard");
		attrList.insert("followedby", "Standard");

		librevenge::RVNGPropertyList propList;
		propList.insert("keep-with-next", "1");
		propList.insert("font-weight", "bold");
		propList.insert("margin-top", "22pt");
		propList.insert("margin-bottom", "3pt");
		// TODO: font size

		librevenge::RVNGPropertyListVector propsVector;
		propsVector.append(propList);
		attrList.insert("props", propsVector);

		pHandler->startElement("s", attrList);
		pHandler->endElement("s");
	}

	m_paragraphManager.write(pHandler);

	pHandler->endElement("styles");
}

}
