/* AbiWord
 * Copyright (C) 2001 AbiSource, Inc.
 * Copyright (C) 2002-2004 Marc Maurer (j.m.maurer@student.utwente.nl)
 * Copyright (C) 2002-2005 William Lachance (william.lachance@sympatico.ca)
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
 * 02111-1307, USA.
 */

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <locale.h>
#include <gsf/gsf-utils.h>
#include <gsf/gsf-input-memory.h>
#include <gsf/gsf-input-stdio.h>
#include <libwpd/GSFStream.h>

#include "ut_types.h"
#include "ut_string.h"
#include "ut_string_class.h"
#include "ut_units.h"
#include "ut_growbuf.h"
#include "ut_assert.h"
#include "ut_debugmsg.h"
#include "ut_math.h" // for rint (font size)
#include "ut_rand.h"

#include "xap_Frame.h"
#include "xap_EncodingManager.h"

#include "pd_Document.h"
#include "pt_Types.h"

#include "fl_AutoLists.h"
#include "fl_AutoNum.h"

#include "ap_Strings.h"

#include "ie_imp_WordPerfect.h"
#include "ie_impexp_WordPerfect.h"

// This should probably be defined in pt_Types.h
static const UT_uint32 PT_MAX_ATTRIBUTES = 8;

ABI_ListDefinition::ABI_ListDefinition(int iOutlineHash) :
	m_iOutlineHash(iOutlineHash)
{
	for(int i=0; i<WP6_NUM_LIST_LEVELS; i++) 
	{
		m_iListIDs[i] = 0;
		m_listTypes[i] = BULLETED_LIST;
		m_iListNumbers[i] = 0;
	}
}

void ABI_ListDefinition::setListType(const int level, const char type)
{	
	switch (type)
	{
	case '1':
		m_listTypes[level-1] = NUMBERED_LIST;
		break;
	case 'a':
		m_listTypes[level-1] = LOWERCASE_LIST;
		break;
	case 'A':
		m_listTypes[level-1] = UPPERCASE_LIST;
		break;
	case 'i':
		m_listTypes[level-1] = LOWERROMAN_LIST;
		break;
	case 'I':
		m_listTypes[level-1] = UPPERROMAN_LIST;
		break;
	}
}

#define X_CheckDocumentError(v) if (!v) { UT_DEBUGMSG(("X_CheckDocumentError: %d\n", __LINE__)); }

IE_Imp_WordPerfect_Sniffer::IE_Imp_WordPerfect_Sniffer()
	: IE_ImpSniffer(IE_MIME_WP_6)
{
}

IE_Imp_WordPerfect_Sniffer::~IE_Imp_WordPerfect_Sniffer()
{
}

UT_Confidence_t IE_Imp_WordPerfect_Sniffer::recognizeContents (const char * szBuf, 
							       UT_uint32 iNumbytes)
{
	GsfInput * input = GSF_INPUT(gsf_input_memory_new(reinterpret_cast<const guint8*>(szBuf), iNumbytes, false));	
	GSFInputStream gsfInput(input);

	WPDConfidence confidence = WPDocument::isFileFormatSupported(&gsfInput, true);

	g_object_unref(input);

	switch (confidence)
	{
		case WPD_CONFIDENCE_NONE:
		// libwpd > 0.7.1 reports POOR if the text file is plain text (that _could_ be a WP4x document)
		// however, we'll let the text importer handle such cases
		case WPD_CONFIDENCE_POOR: 
			return UT_CONFIDENCE_ZILCH;
			return UT_CONFIDENCE_POOR;
		case WPD_CONFIDENCE_LIKELY:
			return UT_CONFIDENCE_SOSO;
		case WPD_CONFIDENCE_GOOD:
			return UT_CONFIDENCE_GOOD;
		case WPD_CONFIDENCE_EXCELLENT:
			return UT_CONFIDENCE_PERFECT;
		default:
			return UT_CONFIDENCE_ZILCH;
	}
}

UT_Confidence_t IE_Imp_WordPerfect_Sniffer::recognizeSuffix (const char * szSuffix)
{
	// We recognize both word documents and their template versions
	if (!UT_stricmp(szSuffix,".wpd") || !UT_stricmp(szSuffix, ".wp"))
		return UT_CONFIDENCE_PERFECT;
	return UT_CONFIDENCE_ZILCH;
}

UT_Error IE_Imp_WordPerfect_Sniffer::constructImporter (PD_Document * pDocument,
							IE_Imp ** ppie)
{
	*ppie = new IE_Imp_WordPerfect(pDocument);
	return UT_OK;
}

bool IE_Imp_WordPerfect_Sniffer::getDlgLabels  (const char ** pszDesc,
						const char ** pszSuffixList,
						IEFileType * ft)
{
	*pszDesc = "WordPerfect (.wpd, .wp)";
	*pszSuffixList = "*.wpd; *.wp";
	*ft = getFileType();
	return true;
}

/****************************************************************************/
/****************************************************************************/

IE_Imp_WordPerfect::IE_Imp_WordPerfect(PD_Document * pDocument)
  : IE_Imp (pDocument),
	m_bParagraphChanged(false),
	m_bParagraphInSection(false),
	m_bInSection(false),
	m_bSectionChanged(false),
	m_leftMargin(0.0f),
	m_rightMargin(0.0f),
	m_headerId(-1),
	m_footerId(-1),
	m_nextFreeId(0),
	m_leftMarginOffset(0.0f),
	m_rightMarginOffset(0.0f),
	m_pCurrentListDefinition(NULL),
	m_iCurrentListLevel(0),
	m_bInCell(false),
	m_bHdrFtrOpenCount(0)
{
}

IE_Imp_WordPerfect::~IE_Imp_WordPerfect()
{
	//UT_HASH_PURGEDATA(ABI_ListDefinition *,&m_listStylesHash,delete); 
}

UT_Error IE_Imp_WordPerfect::importFile(const char * szFilename)
{
	gsf_init ();

	GError *err;
	GsfInput * input;
	input = GSF_INPUT(gsf_input_stdio_new (szFilename, &err));
	if (input == NULL) 
	{
		g_return_val_if_fail (err != NULL, 1);
		
		g_warning ("'%s' error: %s", szFilename, err->message);
		g_error_free (err);
		return 1;
	}

	GSFInputStream gsfInput(input);
	WPDResult error = WPDocument::parse(&gsfInput, static_cast<WPXHLListenerImpl *>(this));

	gsf_shutdown();

	if (error != WPD_OK)
	{
		UT_DEBUGMSG(("AbiWordPerfect: ERROR: %i!\n", (int)error));
		return UT_IE_IMPORTERROR;
	}

	return UT_OK;
}

void IE_Imp_WordPerfect::pasteFromBuffer (PD_DocumentRange *, 
					  unsigned char *, unsigned int, const char *)
{
	// nada
}

void IE_Imp_WordPerfect::setDocumentMetaData(const WPXPropertyList &propList)
{
	if (propList["dc:author"])
		getDoc()->setMetaDataProp(PD_META_KEY_CREATOR, propList["dc:author"]->getStr().cstr());
	if (propList["dc:subject"])
		getDoc()->setMetaDataProp(PD_META_KEY_SUBJECT, propList["dc:subject"]->getStr().cstr());
	if (propList["dc:publisher"])
		getDoc()->setMetaDataProp(PD_META_KEY_PUBLISHER, propList["dc:publisher"]->getStr().cstr());
	if (propList["dc:type"])
		getDoc()->setMetaDataProp(PD_META_KEY_TYPE, propList["dc:category"]->getStr().cstr());
	if (propList["libwpd:keywords"])
		getDoc()->setMetaDataProp(PD_META_KEY_KEYWORDS, propList["libwpd:keywords"]->getStr().cstr());
	if (propList["dc:language"])
		getDoc()->setMetaDataProp(PD_META_KEY_LANGUAGE, propList["dc:language"]->getStr().cstr());
	if (propList["libwpd:abstract"])
		getDoc()->setMetaDataProp(PD_META_KEY_DESCRIPTION, propList["libwpd:abstract"]->getStr().cstr());
}

void IE_Imp_WordPerfect::startDocument()
{
	UT_DEBUGMSG(("AbiWordPerfect: startDocument\n"));
}

void IE_Imp_WordPerfect::endDocument()
{
	UT_DEBUGMSG(("AbiWordPerfect: endDocument\n"));
}

void IE_Imp_WordPerfect::openPageSpan(const WPXPropertyList &propList)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: openPageSpan\n"));
	
	float marginLeft = 0.0f, marginRight = 0.0f;

	if (propList["fo:margin-left"])
		marginLeft = propList["fo:margin-left"]->getFloat();
	if (propList["fo:margin-right"])
		marginRight = propList["fo:margin-right"]->getFloat();

	if (
		marginLeft != m_leftMargin ||
		marginRight != m_rightMargin //||
		//marginTop != m_marginBottom ""
		//marginBottom != m_marginBottom
		)
	{
		m_leftMargin = marginLeft;
		m_rightMargin = marginRight;
		
		// margin properties are section properties in AbiWord
		m_bSectionChanged;
	}
}

void IE_Imp_WordPerfect::openHeader(const WPXPropertyList &propList)
{
	m_bHdrFtrOpenCount++;
	
	/*
	TODO: THIS CODE IS NOT!!!! USEFULL! - DON'T TOUCH IT - MARCM
	UT_String propBuffer;

	switch (headerFooterType)
	{
		case HEADER:
			m_headerId = m_nextFreeId;
			UT_String_sprintf(propBuffer,"id:%d; listid:0; parentid=0; type=header", m_headerId);
			break;
		case FOOTER:
			m_footerId = m_nextFreeId;
			UT_String_sprintf(propBuffer,"id:%d; listid:0; parentid=0; type=footer", m_footerId);
			break;
		default:
			UT_ASSERT(SHOULD_NOT_HAPPEN);
			break;
	}
 
	const XML_Char* propsArray[3];
	propsArray[0] = "props";
	propsArray[1] = propBuffer.c_str();
	propsArray[2] = NULL;	
	
    X_CheckDocumentError(appendStrux(PTX_Section, propsArray));
	m_bInSection = true;
	m_bSectionChanged = false;*/
}

void IE_Imp_WordPerfect::closeHeader()
{
	m_bHdrFtrOpenCount--;
	/*
	TODO: THIS CODE IS NOT!!!! USEFULL! - DON'T TOUCH IT - MARCM
	m_nextFreeId++;
	*/
}

void IE_Imp_WordPerfect::openFooter(const WPXPropertyList &propList)
{
	m_bHdrFtrOpenCount++;
	// see above comments re: openHeader
}

void IE_Imp_WordPerfect::closeFooter()
{
	m_bHdrFtrOpenCount--;
	// see above comments re: closeHeader
}

void IE_Imp_WordPerfect::openParagraph(const WPXPropertyList &propList, const WPXPropertyListVector &tabStops)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: openParagraph()\n"));
	// for now, we always append these options
	float marginTop = 0.0f, marginBottom = 0.0f;
	float marginLeft = 0.0f, marginRight = 0.0f, textIndent = 0.0f;
	if (propList["fo:margin-top"])
	    marginTop = propList["fo:margin-top"]->getFloat();
	if (propList["fo:margin-bottom"])
	    marginBottom = propList["fo:margin-bottom"]->getFloat();
	if (propList["fo:margin-left"])
	    marginLeft = propList["fo:margin-left"]->getFloat();
	if (propList["fo:margin-right"])
	    marginRight = propList["fo:margin-right"]->getFloat();
	if (propList["fo:text-indent"])
	    textIndent = propList["fo:text-indent"]->getFloat();

	m_topMargin = marginTop;
	m_bottomMargin = marginBottom;
	m_leftMarginOffset = marginLeft;
	m_rightMarginOffset = marginRight;
	m_textIndent = textIndent;

	UT_String propBuffer;
	propBuffer += "text-align:";
	if (propList["fo:text-align"])
	{
		// AbiWord follows xsl:fo, except here, for some reason..
		if (propList["fo:text-align"]->getStr() == "end")
			propBuffer += "right";
		else
			propBuffer += propList["fo:text-align"]->getStr().cstr();
	}
	else
		propBuffer += "left";

	float lineSpacing = 1.0f;
	if (propList["fo:line-height"])
		lineSpacing = propList["fo:line-height"]->getFloat();
	
	UT_String tmpBuffer;
	UT_String_sprintf(tmpBuffer, "; margin-top:%.4fin; margin-bottom:%.4fin; margin-left:%.4fin; margin-right:%.4fin; text-indent:%.4fin; line-height:%.4f",
		m_topMargin, m_bottomMargin, m_leftMarginOffset, m_rightMarginOffset, m_textIndent, lineSpacing);
	propBuffer += tmpBuffer;

	UT_DEBUGMSG(("AbiWordPerfect: Appending paragraph properties: %s\n", propBuffer.c_str()));
	const XML_Char* propsArray[3];
	propsArray[0] = "props";
	propsArray[1] = propBuffer.c_str();
	propsArray[2] = NULL;
	X_CheckDocumentError(appendStrux(PTX_Block, propsArray));

	if (propList["fo:break-before"])
	{
		if (strcmp(propList["fo:break-before"]->getStr().cstr(), "page") == 0)
		{
			UT_UCS4Char ucs = UCS_FF;
			X_CheckDocumentError(appendSpan(&ucs,1));			
		}
		else if (strcmp(propList["fo:break-before"]->getStr().cstr(), "column") == 0)
		{
			UT_UCS4Char ucs = UCS_VTAB;
			X_CheckDocumentError(appendSpan(&ucs,1));
		}
	}
}

void IE_Imp_WordPerfect::openSpan(const WPXPropertyList &propList)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: Appending current text properties\n"));
	
	XML_Char* pProps = "props";
	UT_String propBuffer;
	UT_String tempBuffer;
	
	// bold
	propBuffer += "font-weight:";
	propBuffer += (propList["fo:font-weight"] ? propList["fo:font-weight"]->getStr().cstr() : "normal");
	
	// italic
	propBuffer += "; font-style:";
	propBuffer += (propList["fo:font-style"] ? propList["fo:font-style"]->getStr().cstr() : "normal");
	
	// superscript or subscript
	if (propList["style:text-position"])
	{
		propBuffer += "; text-position:";
		if (strncmp(propList["style:text-position"]->getStr().cstr(), "super", 5) == 0)
			propBuffer += "superscript"; 
		else 
			propBuffer += "subscript";
	}

	if (propList["style:text-underline"] || propList["style:text-crossing-out"])
	{
		propBuffer += "; text-decoration:";
		if (propList["style:text-underline"])
			propBuffer += "underline ";
		if (propList["style:text-crossing-out"])
			propBuffer += "line-through";

	}
	
	if (propList["style:font-name"])
	{
		propBuffer += "; font-family:";
		propBuffer += propList["style:font-name"]->getStr().cstr();
	}

	// font face
	if (propList["fo:font-size"])
	{
		propBuffer += "; font-size:";
		propBuffer += propList["fo:font-size"]->getStr().cstr();
	}

	if (propList["fo:color"])
	{
		propBuffer += "; color:";
		propBuffer += propList["fo:color"]->getStr().cstr();
	}

	if (propList["style:text-background-color"])
	{
		propBuffer += "; bgcolor:";
		propBuffer += propList["style:text-background-color"]->getStr().cstr();
	}

	UT_DEBUGMSG(("AbiWordPerfect: Appending span format: %s\n", propBuffer.c_str()));
	const XML_Char* propsArray[5];
	
	propsArray[0] = pProps;
	propsArray[1] = propBuffer.c_str();
	propsArray[2] = NULL;
	X_CheckDocumentError(appendFmt(propsArray));
}

void IE_Imp_WordPerfect::openSection(const WPXPropertyList &propList, const WPXPropertyListVector &columns)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: openSection\n"));

	// TODO: support spaceAfter
	if (propList["fo:margin-left"])
		m_leftMargin = propList["fo:margin-left"]->getFloat();
	if (propList["fo:margin-right"])
		m_rightMargin = propList["fo:margin-right"]->getFloat();

	_appendSection((columns.count() == 0) ? 1 : columns.count(), m_leftMargin, m_rightMargin); 
}

void IE_Imp_WordPerfect::insertTab()
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: insertTab\n"));

	UT_UCS4Char ucs = UCS_TAB;
	X_CheckDocumentError(appendSpan(&ucs,1));	
}

void IE_Imp_WordPerfect::insertText(const WPXString &text)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	if (text.len())
	{
		UT_DEBUGMSG(("AbiWordPerfect: insertText\n"));
		UT_UCS4String ucs4(text.cstr());
		X_CheckDocumentError(appendSpan(ucs4.ucs4_str(), ucs4.length()));
	}
}

void IE_Imp_WordPerfect::insertLineBreak()
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: insertLineBreak\n"));

	UT_UCSChar ucs = UCS_LF;
	X_CheckDocumentError(appendSpan(&ucs,1));
}



void IE_Imp_WordPerfect::defineOrderedListLevel(const WPXPropertyList &propList)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: defineOrderedListLevel\n"));

	int listID = 0, startingNumber = 0, level = 1;
	char listType = '1';
	UT_UTF8String textBeforeNumber, textAfterNumber;
	
	if (propList["libwpd:id"])
		listID = propList["libwpd:id"]->getInt();
	if (propList["text:start-value"])
		startingNumber = propList["text:start-value"]->getInt();
	if (propList["libwpd:level"])
		level = propList["libwpd:level"]->getInt();
	if (propList["style:num-prefix"])
		textBeforeNumber += propList["style:num-prefix"]->getStr().cstr();
	if (propList["style:num-suffix"])
		textAfterNumber += propList["style:num-suffix"]->getStr().cstr();
	if (propList["style:num-format"])
	{
		listType = propList["style:num-format"]->getStr().cstr()[0];
	}
	if (!m_pCurrentListDefinition || 
		m_pCurrentListDefinition->getOutlineHash() != listID ||
		(m_pCurrentListDefinition->getLevelNumber(level) != startingNumber && 
		 level == 1))
	{
		if (m_pCurrentListDefinition)
			delete (m_pCurrentListDefinition);

		m_pCurrentListDefinition = new ABI_ListDefinition(listID);
	}
	
	if (!m_pCurrentListDefinition->getListID(level))
	{
		m_pCurrentListDefinition->setListType(level, listType);
		m_pCurrentListDefinition->setListID(level, UT_rand());
		_updateDocumentOrderedListDefinition(m_pCurrentListDefinition, level, listType, textBeforeNumber, textAfterNumber, startingNumber);
	}
}

void IE_Imp_WordPerfect::defineUnorderedListLevel(const WPXPropertyList &propList)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: defineUnorderedListLevel\n"));

	int listID = 0, level = 1;
	WPXString textBeforeNumber, textAfterNumber;
	if (propList["libwpd:id"])
		listID = propList["libwpd:id"]->getInt();
	if (propList["libwpd:level"])
		level = propList["libwpd:level"]->getInt();

	if (!m_pCurrentListDefinition || m_pCurrentListDefinition->getOutlineHash() != listID)
	{
		if (m_pCurrentListDefinition)
			delete (m_pCurrentListDefinition);
		
		m_pCurrentListDefinition = new ABI_ListDefinition(listID);
	}

	if (!m_pCurrentListDefinition->getListID(level))
	{
		m_pCurrentListDefinition->setListID(level, UT_rand());
		_updateDocumentUnorderedListDefinition(m_pCurrentListDefinition, level);
	}
}

//void IE_Imp_WordPerfect::openOrderedListLevel(const int listID)
void IE_Imp_WordPerfect::openOrderedListLevel(const WPXPropertyList &propList)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: openOrderedListLevel\n"));
	
	m_iCurrentListLevel++;
}

void IE_Imp_WordPerfect::closeOrderedListLevel()
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: closeOrderedListLevel (level: %i)\n", m_iCurrentListLevel));
	UT_ASSERT(m_iCurrentListLevel > 0); 
	
	// every time we close a list level, the level above it is normally renumbered to start at "1"
	// again. this code takes care of that.
	if (m_iCurrentListLevel < (WP6_NUM_LIST_LEVELS-1))
		m_pCurrentListDefinition->setLevelNumber(m_iCurrentListLevel + 1, 0);
	
	m_iCurrentListLevel--;
}

void IE_Imp_WordPerfect::openUnorderedListLevel(const WPXPropertyList &propList)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: openUNorderedListLevel\n"));
	
	m_iCurrentListLevel++;
}

void IE_Imp_WordPerfect::closeUnorderedListLevel()
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: closeUnorderedListLevel (level: %i)\n", m_iCurrentListLevel));
	UT_ASSERT(m_iCurrentListLevel > 0); 
	
	m_iCurrentListLevel--;
}

// ASSUMPTION: We assume that unordered lists will always pass a number of "0". unpredictable behaviour
// may result otherwise
void IE_Imp_WordPerfect::openListElement(const WPXPropertyList &propList, const WPXPropertyListVector &tabStops)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: openListElement\n"));
	
	UT_ASSERT(m_pCurrentListDefinition); // FIXME: ABI_LISTS_IMPORT throw an exception back to libwpd, if this fails
	
	// Paragraph properties for our list element
	UT_String szListID;
	UT_String szParentID;
	UT_String szLevel;
	UT_String_sprintf(szListID,"%d",m_pCurrentListDefinition->getListID(m_iCurrentListLevel));
	if (m_iCurrentListLevel > 1) 
		UT_String_sprintf(szParentID,"%d", m_pCurrentListDefinition->getListID((m_iCurrentListLevel-1)));
	else
		UT_String_sprintf(szParentID,"0"); 
	UT_String_sprintf(szLevel,"%d", m_iCurrentListLevel);
	
	const XML_Char* listAttribs[PT_MAX_ATTRIBUTES*2 + 1];
	UT_uint32 attribsCount=0;
	
	listAttribs[attribsCount++] = PT_LISTID_ATTRIBUTE_NAME;
	listAttribs[attribsCount++] = szListID.c_str();
	listAttribs[attribsCount++] = PT_PARENTID_ATTRIBUTE_NAME;
	listAttribs[attribsCount++] = szParentID.c_str();
	listAttribs[attribsCount++] = PT_LEVEL_ATTRIBUTE_NAME;
	listAttribs[attribsCount++] = szLevel.c_str();
	
	// Now handle the Abi List properties
	UT_String propBuffer;
	UT_String tempBuffer;
	UT_String_sprintf(tempBuffer,"list-style:%i;", m_pCurrentListDefinition->getListType(m_iCurrentListLevel));
	propBuffer += tempBuffer;

#if 0
	// FIXME: writing the list delimiter is kind of tricky and silly (because wordperfect wants to define
	// it within the document, while abi wants to (sensibly probably) define it in the list definition)
	// (we reset it each time but only for numbered lists)
	if (listDefinition->isLevelNumbered(m_iCurrentListLevel)) 
	{  
		UT_DEBUGMSG(("WordPerfect: Appending this list delim: %s\n", m_rightListDelim.c_str()));
		listDefinition->setListRightDelimText(m_iCurrentListLevel, m_rightListDelim.c_str());
		X_CheckWordPerfectError(_updateDocumentListDefinition(listDefinition, m_iCurrentListLevel));
	}
#endif

	if (m_pCurrentListDefinition->getListType(m_iCurrentListLevel) == BULLETED_LIST)
		UT_String_sprintf(tempBuffer, "field-font:Symbol; ");
	else
		UT_String_sprintf(tempBuffer, "field-font:NULL; ");
	
	m_pCurrentListDefinition->incrementLevelNumber(m_iCurrentListLevel);
	
	propBuffer += tempBuffer;
	UT_String_sprintf(tempBuffer, "start-value:%i; ", 1);
	propBuffer += tempBuffer;
	if (propList["fo:text-indent"])
	{
		UT_String_sprintf(tempBuffer, "text-indent:%s; ", propList["fo:text-indent"]->getStr().cstr());
		propBuffer += tempBuffer;
	}
	if (propList["fo:margin-left"])
	{
		UT_String_sprintf(tempBuffer, "margin-left:%s", propList["fo:margin-left"]->getStr().cstr());
		propBuffer += tempBuffer;
	}
	listAttribs[attribsCount++] = PT_PROPS_ATTRIBUTE_NAME;
	listAttribs[attribsCount++] = propBuffer.c_str();
	listAttribs[attribsCount++] = NULL;
	
	X_CheckDocumentError(appendStrux(PTX_Block, listAttribs));
	
	// hang text off of a list label
	getDoc()->appendFmtMark();
	UT_DEBUGMSG(("WordPerfect: LISTS - Appended a list tag def'n (character props)\n"));
	
	// append a list field label
	const XML_Char* fielddef[5];
	fielddef[0] ="type";
	fielddef[1] = "list_label";
	fielddef[2] = NULL;
	X_CheckDocumentError(appendObject(PTO_Field,fielddef));
	UT_DEBUGMSG(("WordPerfect: LISTS - Appended a field def'n\n"));
	
	// insert a tab
	UT_UCS4Char ucs = UCS_TAB;
	X_CheckDocumentError(appendSpan(&ucs,1));
}

void IE_Imp_WordPerfect::openFootnote(const WPXPropertyList &propList)
{
	if (m_bHdrFtrOpenCount) return; // HACK

	const XML_Char** propsArray = NULL;
	
	UT_String footnoteId;
	UT_String_sprintf(footnoteId,"%i",UT_rand());	
	
	propsArray = static_cast<const XML_Char **>(UT_calloc(7, sizeof(XML_Char *)));
	propsArray [0] = "type";
	propsArray [1] = "footnote_ref";
	propsArray [2] = "footnote-id";
	propsArray [3] = footnoteId.c_str();
	propsArray [4] = NULL;
	propsArray [5] = NULL;
	propsArray [6] = NULL;
	X_CheckDocumentError(appendObject(PTO_Field, propsArray));

	const XML_Char * attribs[3] ={"footnote-id", footnoteId.c_str(), NULL};
	X_CheckDocumentError(appendStrux(PTX_SectionFootnote,attribs));
	
	X_CheckDocumentError(appendStrux(PTX_Block,NULL));

	propsArray = static_cast<const XML_Char **>(UT_calloc(7, sizeof(XML_Char *)));
	propsArray [0] = "type";
	propsArray [1] = "footnote_anchor";
	propsArray [2] = "footnote-id";
	propsArray [3] = footnoteId.c_str();
	propsArray [4] = NULL;
	propsArray [5] = NULL;
	propsArray [6] = NULL;
	X_CheckDocumentError(appendObject(PTO_Field, propsArray));
}

void IE_Imp_WordPerfect::closeFootnote()
{
	if (m_bHdrFtrOpenCount) return; // HACK
	X_CheckDocumentError(appendStrux(PTX_EndFootnote,NULL));
}

void IE_Imp_WordPerfect::openEndnote(const WPXPropertyList &propList)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	const XML_Char** propsArray = NULL;
	
	UT_String endnoteId;
	UT_String_sprintf(endnoteId,"%i",UT_rand());	
	
	propsArray = static_cast<const XML_Char **>(UT_calloc(7, sizeof(XML_Char *)));
	propsArray [0] = "type";
	propsArray [1] = "endnote_ref";
	propsArray [2] = "endnote-id";
	propsArray [3] = endnoteId.c_str();
	propsArray [4] = NULL;
	propsArray [5] = NULL;
	propsArray [6] = NULL;
	X_CheckDocumentError(appendObject(PTO_Field, propsArray));

	const XML_Char * attribs[3] ={"endnote-id", endnoteId.c_str(), NULL};
	X_CheckDocumentError(appendStrux(PTX_SectionEndnote,attribs));
	
	X_CheckDocumentError(appendStrux(PTX_Block,NULL));

	propsArray = static_cast<const XML_Char **>(UT_calloc(7, sizeof(XML_Char *)));
	propsArray [0] = "type";
	propsArray [1] = "endnote_anchor";
	propsArray [2] = "endnote-id";
	propsArray [3] = endnoteId.c_str();
	propsArray [4] = NULL;
	propsArray [5] = NULL;
	propsArray [6] = NULL;
	X_CheckDocumentError(appendObject(PTO_Field, propsArray));
}

void IE_Imp_WordPerfect::closeEndnote()
{
	if (m_bHdrFtrOpenCount) return; // HACK
	X_CheckDocumentError(appendStrux(PTX_EndEndnote,NULL));
}

void IE_Imp_WordPerfect::openTable(const WPXPropertyList &propList, const WPXPropertyListVector &columns)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	// TODO: handle 'marginLeftOffset' and 'marginRightOffset'
	UT_DEBUGMSG(("AbiWordPerfect: openTable\n"));
	
	UT_String propBuffer;

	if (propList["table:align"])
	{
		// no need to support left: default behaviour

		//if (strcmp(propList["table:align"]->getStr().cstr(), "right"))
		// abiword does not support this I think
		//if (strcmp(propList["table:align"]->getStr().cstr(), "center"))
		// abiword does not support this I think
		//if (strcmp(propList["table:align"]->getStr().cstr(), "margins"))
		// abiword does not support this I think
		if (strcmp(propList["table:align"]->getStr().cstr(), "margins"))
		{
			if (propList["fo:margin-left"])
				UT_String_sprintf(propBuffer, "table-column-leftpos:%s; ", propList["fo:margin-left"]->getStr().cstr());
		}
	}
	
	propBuffer += "table-column-props:";
	WPXPropertyListVector::Iter i(columns);
	for (i.rewind(); i.next();)
	{
		UT_String tmpBuffer;
		if (i()["style:column-width"])
			UT_String_sprintf(tmpBuffer, "%s/", i()["style:column-width"]->getStr().cstr());
		propBuffer += tmpBuffer;
	}

	const XML_Char* propsArray[3];
	propsArray[0] = "props";
	propsArray[1] = propBuffer.c_str();
	propsArray[2] = NULL;

	X_CheckDocumentError(appendStrux(PTX_SectionTable, propsArray));
}

void IE_Imp_WordPerfect::openTableRow(const WPXPropertyList &propList)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: openRow\n"));
	if (m_bInCell)
	{		
		X_CheckDocumentError(appendStrux(PTX_EndCell, NULL));
	}
	
	m_bInCell = false;
}

void IE_Imp_WordPerfect::openTableCell(const WPXPropertyList &propList)
{
	if (m_bHdrFtrOpenCount) return; // HACK
	int col =0,  row = 0, colSpan = 0, rowSpan = 0;
	if (propList["libwpd:column"])
		col = propList["libwpd:column"]->getInt();
	if (propList["libwpd:row"])
		row = propList["libwpd:row"]->getInt();
	if (propList["table:number-columns-spanned"])
		colSpan = propList["table:number-columns-spanned"]->getInt();
	if (propList["table:number-rows-spanned"])
		rowSpan = propList["table:number-rows-spanned"]->getInt();

	UT_DEBUGMSG(("AbiWordPerfect: openCell(col: %d, row: %d, colSpan: %d, rowSpan: %d\n", col, row, colSpan, rowSpan));
	if (m_bInCell)
	{
		X_CheckDocumentError(appendStrux(PTX_EndCell, NULL));
	}
	
	UT_String propBuffer;
	UT_String_sprintf(propBuffer, "left-attach:%d; right-attach:%d; top-attach:%d; bot-attach:%d", col, col+colSpan, row, row+rowSpan);
	
	UT_String borderStyle;
	// we only support bg-style:1 for now
	bool borderLeftSolid = false;
	bool borderRightSolid = false;
	bool borderTopSolid = false;
	bool borderBottomSolid = false;
	if (propList["fo:border-left"])
		borderLeftSolid = strncmp(propList["fo:border-left"]->getStr().cstr(), "0.0inch", 7);
	if (propList["fo:border-right"])
		borderRightSolid = strncmp(propList["fo:border-right"]->getStr().cstr(), "0.0inch", 7);
	if (propList["fo:border-top"])
		borderTopSolid = strncmp(propList["fo:border-top"]->getStr().cstr(), "0.0inch", 7);
	if (propList["fo:border-bottom"])
		borderBottomSolid = strncmp(propList["fo:border-bottom"]->getStr().cstr(), "0.0inch", 7);

	UT_String_sprintf(borderStyle, "; left-style:%s; right-style:%s; top-style:%s; bot-style:%s", 
					  (borderLeftSolid ? "solid" : "none"),
					  (borderRightSolid ? "solid" : "none"), 
					  (borderTopSolid ? "solid" : "none"), 
					  (borderBottomSolid ? "solid" : "none"));
	propBuffer += borderStyle;
		
	// we only support bg-style:1 for now
	if (propList["fo:background-color"])
	{
		UT_String bgCol;
		UT_String_sprintf(bgCol, "; bg-style:1; background-color:%s", &(propList["fo:background-color"]->getStr().cstr()[1]));
		propBuffer += bgCol;
	}
	
	UT_DEBUGMSG(("AbiWordPerfect: Inserting a Cell definition: %s\n", propBuffer.c_str()));
	
	const XML_Char* propsArray[3];
	propsArray[0] = "props";
	propsArray[1] = propBuffer.c_str();
	propsArray[2] = NULL;
	
	X_CheckDocumentError(appendStrux(PTX_SectionCell, propsArray));
	m_bInCell = true;
}

void IE_Imp_WordPerfect::closeTable()
{
	if (m_bHdrFtrOpenCount) return; // HACK
	UT_DEBUGMSG(("AbiWordPerfect: Closing table\n"));
	
	if (m_bInCell)
	{
		X_CheckDocumentError(appendStrux(PTX_EndCell, NULL));
	}
	X_CheckDocumentError(appendStrux(PTX_EndTable, NULL));
	m_bInCell = false;
	
	// we need to open a new paragraph after a table, since libwpd does NOT do it
	// FIXME: NEED TO PASS THE CURRENT PROPERTIES INSTEAD OF NULL
	// NOTE: THIS SUCKS.........
	X_CheckDocumentError(appendStrux(PTX_Block, NULL));
}

UT_Error IE_Imp_WordPerfect::_appendSection(int numColumns, const float marginLeft, const float marginRight)
{
	UT_DEBUGMSG(("AbiWordPerfect: Appending section\n"));
	
	UT_String myProps("") ;
	setlocale(LC_NUMERIC, "C");
	myProps += UT_String_sprintf("columns:%d; page-margin-left:%.4fin; page-margin-right:%.4fin", numColumns, marginLeft, marginRight);
	setlocale(LC_NUMERIC, NULL);
	
	XML_Char * propsArray[3];
	propsArray[0] = "props";
	propsArray[1] = const_cast<XML_Char*>(reinterpret_cast<const XML_Char*>(myProps.c_str()));
	propsArray[2] = NULL ;
	X_CheckDocumentError(appendStrux(PTX_Section, (const XML_Char**)propsArray));
	
	m_bInSection = true;
	
	m_leftMargin = marginLeft;
	m_rightMargin = marginRight;
	
	m_bSectionChanged = false;
	
	return UT_OK;
}

// NB: AbiWord-2.0 doesn't properly support nested lists with different nested styles: only "1" style
// really looks proper. We hack around this be only using the style given at level "1"
// NB: AbiWord-2.0 doesn't properly support setting list delimeters at levels greater than 1,
// we hack around this by using only "plain" (e.g.: NULL) list delimeters on levels greater than 1.
UT_Error IE_Imp_WordPerfect::_updateDocumentOrderedListDefinition(ABI_ListDefinition *pListDefinition, int iLevel, 
																  const char listType, const UT_UTF8String &sTextBeforeNumber, 
																  const UT_UTF8String &sTextAfterNumber, int iStartingNumber)
{
	UT_DEBUGMSG(("AbiWordPerfect: Updating document list definition (iLevel: %i)\n", iLevel));

	if (iLevel > 1)
	fprintf(stderr, "WLACH: Parent's list id is.. %i\n", pListDefinition->getListID((iLevel-1)));

	// finally, set the document's list identification info..
	fl_AutoNum * pAuto = getDoc()->getListByID(pListDefinition->getListID(iLevel));
	// not in document yet, we should create a list for it
	if (pAuto == NULL) 
	{	
		UT_DEBUGMSG(("AbiWordPerfect: pAuto is NULL: creating a list\n", iLevel));
		if (iLevel > 1) 
		{	
			pAuto = new fl_AutoNum(pListDefinition->getListID(iLevel), 
								   pListDefinition->getListID((iLevel-1)), 
								   pListDefinition->getListType(1), 
								   iStartingNumber, 
								   const_cast<XML_Char*>(reinterpret_cast<const XML_Char*>("%L")), 
								   ".", 
								   getDoc(), 
								   NULL);
		}   
		else 
		{
			UT_UTF8String sNumberingString;
			UT_UTF8String sNumber("%L", (size_t)0);
			
			sNumberingString += sTextBeforeNumber;
			sNumberingString += sNumber;
			sNumberingString += sTextAfterNumber;
	
			pAuto = new fl_AutoNum(pListDefinition->getListID(iLevel), 0, pListDefinition->getListType(iLevel), iStartingNumber, 
								   const_cast<XML_Char*>(reinterpret_cast<const XML_Char*>(sNumberingString.utf8_str())), ".", getDoc(), NULL);
		}
		getDoc()->addList(pAuto);
	}
	// we should update what we have
	else 
	{
		UT_DEBUGMSG(("AbiWordPerfect: pAuto already exists\n", iLevel));
	}

	pAuto->fixHierarchy();

	return UT_OK;
}

UT_Error IE_Imp_WordPerfect::_updateDocumentUnorderedListDefinition(ABI_ListDefinition *pListDefinition, int iLevel)
{
	UT_DEBUGMSG(("AbiWordPerfect: Updating document list definition (iLevel: %i)\n", iLevel));
	
	// finally, set the document's list identification info..
	fl_AutoNum * pAuto = getDoc()->getListByID(pListDefinition->getListID(iLevel));
	// not in document yet, we should create a list for it
	if (pAuto == NULL) 
	{	
		UT_DEBUGMSG(("AbiWordPerfect: pAuto is NULL: creating a list\n", iLevel));
		if (iLevel > 1) 
		{	
			pAuto = new fl_AutoNum(pListDefinition->getListID(iLevel), pListDefinition->getListID((iLevel-1)), 
								   pListDefinition->getListType(1), 0, const_cast<XML_Char*>(reinterpret_cast<const XML_Char*>("%L")), ".", getDoc(), NULL);
		}   
		else
			pAuto = new fl_AutoNum(pListDefinition->getListID(iLevel), 0, pListDefinition->getListType(iLevel), 0, 
								   const_cast<XML_Char*>(reinterpret_cast<const XML_Char*>("%L")), ".", getDoc(), NULL);
		  
		getDoc()->addList(pAuto);
	}
	// we should update what we have
	else 
	{	
		UT_DEBUGMSG(("AbiWordPerfect: pAuto already exists\n", iLevel));
	}

	pAuto->fixHierarchy();

	return UT_OK;
}
