/* AbiWord
 * Copyright (C) 1998,1999 AbiSource, Inc.
 * 
 * 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.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "fl_AutoNum.h"
#include "fl_Layout.h"
#include "fl_BlockLayout.h"
#include "fp_Run.h"
#include "fp_Line.h"
#include "pd_Document.h"
#include "pt_PieceTable.h"
#include "pf_Frag.h"

#include "ut_string.h"
#include "ut_assert.h"
#include "ut_debugmsg.h"

class pf_Frag;

fl_AutoNum::fl_AutoNum(	UT_uint32 id,
						UT_uint32 start,
						PL_StruxDocHandle pFirst,
						fl_AutoNum * pParent,
						const XML_Char * lDelim,
						const XML_Char * lDecimal,
						List_Type lType,
						PD_Document * pDoc)
:	m_pParent(pParent),
	m_pDoc(pDoc),
	m_List_Type(lType),
	m_iID(id),
	m_iParentID(0),
	m_iLevel(pParent ? pParent->getLevel() + 1 : 1),
	m_iStartValue(start),
	m_iAsciiOffset(0),
	m_bUpdatingItems(UT_FALSE),
	m_bDirty(UT_FALSE),
	m_ioffset(0),
	m_bWordMultiStyle(UT_TRUE),
	m_pParentItem(0)
{
	UT_uint32 i;
	i =  UT_XML_strncpy( m_pszDelim, 80, lDelim);
	i =  UT_XML_strncpy( m_pszDecimal, 80, lDecimal);

 	addItem(pFirst);	

	m_pDoc->addList(this);
	// New 6/11/200. m_pParentItem is the item in the parent list
	// that the new list points
}

fl_AutoNum::fl_AutoNum(	UT_uint32 id,
						UT_uint32 parent_id,
						List_Type lType,
						UT_uint32 start,
						const XML_Char * lDelim,
						const XML_Char * lDecimal,
						PD_Document * pDoc)
:	m_pParent(0),
	m_pDoc(pDoc),
	m_List_Type(lType),
	m_iID(id),
	m_iParentID(parent_id),
	m_iLevel(1),
	m_iStartValue(start),
	m_iAsciiOffset(0),
	m_bUpdatingItems(UT_FALSE),
	m_bDirty(UT_FALSE),
	m_ioffset(0),
	m_bWordMultiStyle(UT_TRUE),
	m_pParentItem(0)
{
	// Set in Block???
	UT_XML_strncpy( m_pszDelim, 80, lDelim);
	UT_XML_strncpy( m_pszDecimal, 80, lDecimal);
}


void fl_AutoNum::addItem(PL_StruxDocHandle pItem)
{
    UT_sint32 i = m_pItems.findItem(const_cast<void *>(pItem));
    if(i < 0 )
	{
	        m_pItems.addItem(const_cast<void *>(pItem));
	}
	m_bDirty = UT_TRUE;
}

void fl_AutoNum::fixHierarchy(PD_Document * pDoc)
{
	fl_AutoNum * pParent;
	
	if (m_iParentID != 0)
		pParent = pDoc->getListByID(m_iParentID);
		// TODO Add error checking?
	else
		pParent = NULL;

	m_pParent = pParent;

	if (m_pParent)
		m_iLevel = m_pParent->getLevel() + 1;
	else
		m_iLevel = 1;
	m_bDirty = UT_TRUE;
}

fl_AutoNum::~fl_AutoNum()
{
	if (m_pParent && m_pParent->isEmpty())
		DELETEP(m_pParent);
}

static PD_Document * pCurDoc;

static int compareListItems(const void* p1, const void* p2)
{
  //
  // Fun with (void *) pointers!
  // 
  //	pf_Frag * pf1 = (pf_Frag *) p1;
  //	PD_Document * pDoc = pf1->getPieceTable()->getDocument();
	PL_StruxDocHandle sdh1 = (PL_StruxDocHandle) p1;
	PL_StruxDocHandle sdh2 = (PL_StruxDocHandle) p2;
	PT_DocPosition pos1 = pCurDoc->getStruxPosition(sdh1);
	PT_DocPosition pos2 = pCurDoc->getStruxPosition(sdh2);
	if(pos1 < pos2)
	{
	        return -1;
	}
	if(pos1 > pos2)
	{
	        return 1;
	}
	return 0;
}

void    fl_AutoNum::fixListOrder(void)
{
        pCurDoc = m_pDoc;
        m_pItems.qsort(compareListItems);
        m_bDirty = UT_TRUE;
}

void    fl_AutoNum::markAsDirty(void)
{
         m_bDirty = UT_TRUE;
}

void    fl_AutoNum::findAndSetParentItem(void)
{
	if(m_pParent == NULL)
	        return;
	//	fixListOrder();
	//	m_pParent->fixListOrder();
	//	m_pParent->update(0);

	if (m_pItems.getItemCount() == 0)
	{
		return;
	}
	PL_StruxDocHandle pCurFirst =  (PL_StruxDocHandle) m_pItems.getFirstItem();
	if(pCurFirst == NULL)
	        return;
	PT_DocPosition posCur = m_pDoc->getStruxPosition(pCurFirst);

	UT_uint32 cnt = m_pDoc->getListsCount();
	UT_ASSERT(cnt);
        UT_uint32 iList;
	fl_AutoNum * pClosestAuto = NULL;
        PT_DocPosition posClosest = 0;
        PL_StruxDocHandle pClosestItem = NULL;
	for(iList = 0; iList < cnt; iList++) 
	{
	        fl_AutoNum * pParent = m_pDoc->getNthList(iList);
		UT_uint32 i=0;
		PL_StruxDocHandle pParentItem = pParent->getNthBlock(i);
		PT_DocPosition posParent=0;
		if(pParentItem != NULL)
	        {
		        posParent = m_pDoc->getStruxPosition(pParentItem);
		}
		while(pParentItem != NULL && (posParent < posCur))
		{
	                i++;
			pParentItem = pParent->getNthBlock(i);
			if(pParentItem != NULL)
		        {
		                posParent = m_pDoc->getStruxPosition(pParentItem);
			}
		}
		if( i > 0)
	        {
	                i--;
			pParentItem = pParent->getNthBlock(i);;
			posParent = m_pDoc->getStruxPosition(pParentItem);
			if( posParent > posClosest)
			{
			        posClosest = posParent;
                                pClosestAuto = pParent;
				pClosestItem = pParentItem;
			}
		}
	}
	m_pParentItem = pClosestItem;
	m_pParent = pClosestAuto;
	if(m_pParent != NULL)
	{
	        m_iParentID = m_pParent->getID();
		m_iLevel = m_pParent->getLevel()+ 1;
	}
	else
	{
	        m_iParentID = 0;
		m_iLevel = 0;
	}
	m_bDirty = UT_TRUE;
	update(0);
}

void    fl_AutoNum::_getLabelstr( XML_Char labelStr[], UT_uint32 * insPoint, 
				  UT_uint32 depth, PL_StruxDocHandle pLayout)  
{
  // This method recursively calculates a label based on the type of label
  // of the AutoNum Class. This is output to the label string labelStr.
  //
  // insPoint is the position in the string where the new text goes. It starts
  // Pointing to the byte == 0
  // depth is the level of recursion
  // pLayout is a pointer to the Layout item containing the current list item
  //
        XML_Char p[100], leftDelim[10], rightDelim[10];
	UT_uint32 i,psz;
	//
	// Only get the next level if the list type is not bullet or similar
	//
        if(m_List_Type == NOT_A_LIST)
	{
	       *insPoint = 0;
	       return;
	}
        if(depth > 0 && m_List_Type >= BULLETED_LIST)
	{
	       *insPoint = 0;
	       return;
	}

	// TODO This is a bit of a hack to split the delim string. It would be 
	// TODO nice to clear it up.

	sprintf(p, "%s", m_pszDelim);
	UT_uint32 rTmp;
	
	i = 0;
	
	while (p[i] && p[i] != '%' && p[i+1] != 'L')
	{
		leftDelim[i] = p[i];
		i++;
	}
	leftDelim[i] = '\0';
	i += 2;
	rTmp = i;
	while (p[i] || p[i] != '\0')
	{
		rightDelim[i - rTmp] = p[i];
		i++;
	}
	rightDelim[i - rTmp] = '\0';
	//UT_DEBUGMSG(("Left Delim: %s, Right Delim: %s\n", leftDelim, rightDelim));
	

        if(m_pParent != NULL  && m_List_Type < BULLETED_LIST)
	{
	       m_pParent->_getLabelstr( labelStr, insPoint, depth+1,getParentItem());
	       //     UT_DEBUGMSG(("JORDAN: Parent Label: %s\n", labelStr));
	       if(*insPoint != 0)
	       {
		        psz = UT_XML_strlen(m_pszDecimal);
			for(i=0; i<=psz;i++)
			{
		               labelStr[(*insPoint)++] = m_pszDecimal[i];
			}
			(*insPoint)--;
	       }	       
	}
	
	UT_sint32 place = getPositionInList(pLayout,depth);
	if(place == -1)
	{
	       UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
	       labelStr[0] = NULL;
	       (*insPoint) = 0;
	       return;
	}
	place = place + m_iStartValue;

	//	if (depth == 0 )
	if( m_List_Type < BULLETED_LIST)
	{
		psz = UT_XML_strlen(leftDelim);
		for (i = 0; i < psz; i++)
		{
			labelStr[(*insPoint)++] = leftDelim[i];
		}
	}
		
	switch( m_List_Type)
	{ 
	        case NUMBERED_LIST:
		        sprintf(p,"%i",place);
			psz = UT_XML_strlen( p);
			for(i=0; i<psz; i++)
			{
			         labelStr[(*insPoint)++] = p[i];
			}
			//labelStr[(*insPoint)] = NULL;
	                break;
	        case UPPERCASE_LIST:
		        sprintf(p,"%s",dec2ascii(place - 1, 65));
			psz = UT_XML_strlen( p);
			for(i=0; i<psz; i++)
			{
			         labelStr[(*insPoint)++] = p[i];
			}
			//labelStr[(*insPoint)] = NULL;
	                break;
	        case LOWERCASE_LIST:
		        sprintf(p,"%s",dec2ascii(place - 1, 97));
			psz = UT_XML_strlen( p);
			for(i=0; i<psz; i++)
			{
			         labelStr[(*insPoint)++] = p[i];
			}
			//labelStr[(*insPoint)] = NULL;
	                break;
	        case UPPERROMAN_LIST:
		        sprintf(p,"%s",dec2roman(place,UT_FALSE));
			psz = UT_XML_strlen( p);
			for(i=0; i<psz; i++)
			{
			         labelStr[(*insPoint)++] = p[i];
			}
			//labelStr[(*insPoint)] = NULL;
	                break;
	        case LOWERROMAN_LIST:
		        sprintf(p,"%s",dec2roman(place,UT_TRUE));
			psz = UT_XML_strlen( p);
			for(i=0; i<psz; i++)
			{
			         labelStr[(*insPoint)++] = p[i];
			}
			//labelStr[(*insPoint)] = NULL;
	                break;
	        case BULLETED_LIST:
	    	        labelStr[(*insPoint)++] = (XML_Char) 0xb7;
			//labelStr[(*insPoint)] = NULL;
			break;
	        case DASHED_LIST:
		        labelStr[(*insPoint)++] = (XML_Char) '-';
			//labelStr[(*insPoint)] = NULL;
			break;
	        case SQUARE_LIST:
		        labelStr[(*insPoint)++] = (XML_Char) 0x6E;
			//labelStr[(*insPoint)] = NULL;
			break;
	        case TRIANGLE_LIST:
		        labelStr[(*insPoint)++] = (XML_Char) 0x73;
			//labelStr[(*insPoint)] = NULL;
			break;
	        case DIAMOND_LIST:
		        labelStr[(*insPoint)++] = (XML_Char) 0xA9;
			//labelStr[(*insPoint)] = NULL;
			break;
	        case STAR_LIST:
		        labelStr[(*insPoint)++] = (XML_Char) 0x53;
			//labelStr[(*insPoint)] = NULL;
			break;
	        case IMPLIES_LIST:
		        labelStr[(*insPoint)++] = (XML_Char) 0xDE;
			//labelStr[(*insPoint)] = NULL;
			break;
	        case TICK_LIST:
		        labelStr[(*insPoint)++] = (XML_Char) 0x33;
			//labelStr[(*insPoint)] = NULL;
			break;
	        case BOX_LIST:
		        labelStr[(*insPoint)++] = (XML_Char) 0x72;
			//labelStr[(*insPoint)] = NULL;
			break;
	        case HAND_LIST:
		        labelStr[(*insPoint)++] = (XML_Char) 0x2B;
			//labelStr[(*insPoint)] = NULL;
			break;
	        case HEART_LIST:
		        labelStr[(*insPoint)++] = (XML_Char) 0xAA;
			//labelStr[(*insPoint)] = NULL;
			break;
	        default:
		        UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
			break;
	}
	
	//if (depth == 0)
	if( m_List_Type < BULLETED_LIST && 
	    (UT_XML_strnicmp(m_pszDecimal,rightDelim,4) != 0 || depth == 0) )
	{
		psz = UT_XML_strlen(rightDelim);
		for (i = 0; i < psz; i++)
		{
			labelStr[(*insPoint)++] = rightDelim[i];
		}
	}
	labelStr[(*insPoint)] = NULL;
	return;
}

const XML_Char * fl_AutoNum::getLabel(PL_StruxDocHandle pItem) 
{
	static XML_Char label[100];
	UT_uint32  insPoint=0;
	UT_uint32 depth;
	depth = 0;

	_getLabelstr( label, &insPoint, depth , pItem);
	if(insPoint == 0 )
	{
	        return (const XML_Char *) NULL;
	}
	else
	{
	        return (const XML_Char *) label;
	}
}

UT_uint32 fl_AutoNum::getValue(PL_StruxDocHandle pItem) 
{
	return getPositionInList(pItem,0) + m_iStartValue;
}


void fl_AutoNum::setListType(List_Type lType)
{
        m_List_Type = lType;
}

UT_Bool fl_AutoNum::isDirty(void)
{
        return m_bDirty;
}

void fl_AutoNum::setDelim(const XML_Char * lDelim)
{
	UT_uint32 i;
	i =  UT_XML_strncpy( m_pszDelim, 80, lDelim);
	m_bDirty = UT_TRUE;
}

const XML_Char * fl_AutoNum::getDelim( void)
{
        return m_pszDelim;
}


const XML_Char * fl_AutoNum::getDecimal( void)
{
        return m_pszDecimal;
}

void fl_AutoNum::setDecimal(const XML_Char * lDecimal)
{
	UT_uint32 i;
	i =  UT_XML_strncpy( m_pszDecimal, 80, lDecimal);
	m_bDirty = UT_TRUE;
}

List_Type fl_AutoNum::getType(void)
{
        return m_List_Type;
}

void fl_AutoNum::setStartValue(UT_uint32 start)
{
	m_iStartValue = start;
        m_bDirty = UT_TRUE;
	_updateItems(0,NULL);
}

void fl_AutoNum::setAsciiOffset(UT_uint32 new_asciioffset)
{
        m_iAsciiOffset = (UT_uint16) new_asciioffset;
	m_bDirty = UT_TRUE;
}

UT_uint32 fl_AutoNum::getStartValue32(void)
{
	return m_iStartValue;
}

void fl_AutoNum::insertFirstItem(PL_StruxDocHandle pItem, PL_StruxDocHandle pLast, UT_uint32 depth)
{
	UT_sint32 i = -1; 
	if(m_pItems.getItemCount() > 0)
	        i = m_pItems.findItem((void *) pItem);
	if(i < 0)
	{
	        m_pItems.insertItemAt((void *) pItem, 0);
		m_bDirty = UT_TRUE;
	}


	if (m_pParent)
	{
	  UT_DEBUGMSG(("SEVIOR: setting parent item \n"));
		m_pParentItem = pLast;
		m_bDirty = UT_TRUE;
	}
	if ( getAutoNumFromSdh(pItem) == this)
		_updateItems(0,NULL);
}

PL_StruxDocHandle fl_AutoNum::getParentItem(void)
{
	return m_pParentItem;
}


void fl_AutoNum::setParentItem(PL_StruxDocHandle pItem)
{
	m_pParentItem = pItem;
	m_bDirty = UT_TRUE;
}

void fl_AutoNum::insertItem(PL_StruxDocHandle pItem, PL_StruxDocHandle pPrev)
{
	UT_sint32 ndx,i;
	UT_ASSERT(pItem);
	ndx = m_pItems.findItem((void *) pItem);
	if(ndx >= 0)
	        return;
	m_bDirty = UT_TRUE;
	ndx = m_pItems.findItem((void *) pPrev) + 1;
	m_pItems.insertItemAt((void *) pItem, ndx);
	if(m_pDoc->areListUpdatesAllowed() == UT_FALSE)
	       return;

	// scan through all the lists and update parent pointers
        
	UT_sint32 numlists = m_pDoc->getListsCount();
	for(i=0; i<numlists; i++)
	{
	        fl_AutoNum * pAuto = m_pDoc->getNthList(i);
		if( pPrev == pAuto->getParentItem())
		{
		        pAuto->setParentItem(pItem);
			pAuto->m_bDirty = UT_TRUE;
			pAuto->_updateItems(0,NULL);
		}
	}

	_updateItems(ndx+1,NULL);
}


void fl_AutoNum::prependItem(PL_StruxDocHandle pItem, PL_StruxDocHandle pNext)
{
	UT_sint32 ndx;
	UT_sint32 i;
	UT_ASSERT(pItem);
	PL_StruxDocHandle pPrev = NULL;
	ndx = m_pItems.findItem((void *) pItem);
	if(ndx >= 0)
	        return;
	m_bDirty = UT_TRUE;
	ndx = m_pItems.findItem((void *) pNext);
	if(ndx > 0)
	{
	        pPrev = m_pItems.getNthItem(ndx-1);
	}
	m_pItems.insertItemAt((void *) pItem, ndx);
	if(m_pDoc->areListUpdatesAllowed() == UT_FALSE)
	        return;
	if(pPrev != NULL)
	{
	// scan through all the lists and update parent pointers
        
	        UT_sint32 numlists = m_pDoc->getListsCount();
	        for(i=0; i<numlists; i++)
	        {
	                fl_AutoNum * pAuto = m_pDoc->getNthList(i);
		        if( pPrev == pAuto->getParentItem())
		        {
		                pAuto->setParentItem(pItem);
				pAuto->m_bDirty = UT_TRUE;
				pAuto->_updateItems(0,NULL);
			}
		}
	}
	_updateItems(ndx,NULL);
}

void fl_AutoNum::removeItem(PL_StruxDocHandle pItem)
{
	UT_sint32 ndx = m_pItems.findItem((void *)pItem);
	UT_sint32 i;
	//
	// For multi-views we might have already deleted pItem from the
        // fl_AutoNum 
	//
	//
	UT_ASSERT(ndx != -1);
	if(ndx < 0 )
	{
	        m_bDirty = UT_TRUE;
	        _updateItems(0,NULL);
        }        
        PL_StruxDocHandle ppItem = NULL;
	if(ndx > 0)
	{
	        ppItem =  ( PL_StruxDocHandle) m_pItems.getNthItem(ndx - 1);
	}
	
	m_pItems.deleteNthItem(ndx);
	m_bDirty = UT_TRUE;

	// scan through all the lists and update parent pointers
	
	UT_sint32 numlists = m_pDoc->getListsCount();
	for(i=0; i<numlists; i++)
	{
	        fl_AutoNum * pAuto = m_pDoc->getNthList(i);
		if( pItem == pAuto->getParentItem())
		{
		        pAuto->setParentItem(ppItem);
		        if(ppItem == NULL)
		        {
			        UT_uint32 level = pAuto->getLevel();
			        if(level > 0)
			        {
			                 level = level - 1;
				}
				else
				{
			                 UT_ASSERT(UT_SHOULD_NOT_HAPPEN);
				}
				pAuto->setLevel(level);
				pAuto->setParent(getParent());
				pAuto->m_bDirty = UT_TRUE;
				pAuto->setParentItem(getParentItem());
			}
			if(m_pDoc->areListUpdatesAllowed() == UT_TRUE)
			        pAuto->_updateItems(0,NULL);
		}
	}
	_updateItems(ndx,NULL);
}

UT_sint32 fl_AutoNum::getPositionInList(PL_StruxDocHandle pItem, UT_uint32 depth) 
{
	UT_ASSERT(m_pItems.getItemCount() > 0);
	
	PL_StruxDocHandle pTmp;
	UT_uint32 ndx = 0;
	UT_uint32 count = m_pItems.getItemCount();
	UT_Bool bOnLevel = UT_TRUE;
	UT_Bool bFirstItem = UT_FALSE;
	
	for (UT_uint32 i = 0; i < count; i++)
	{
		pTmp = (PL_StruxDocHandle) m_pItems.getNthItem(i);
     //		bOnLevel = (depth == 0);
		fl_AutoNum * pAuto = getAutoNumFromSdh(pItem);
     		bOnLevel = (UT_Bool)( pAuto == this);
		bFirstItem = (UT_Bool)(pTmp == m_pItems.getFirstItem());
		if (pTmp == pItem)
		{
			if (m_bWordMultiStyle && !bOnLevel && !bFirstItem)
				ndx--;
			return ndx;
		}
		else if (!m_bWordMultiStyle || bOnLevel || bFirstItem)
		{
			ndx++;
		}
	}
	
	return -1;
        // return m_pItems.findItem(pItem);
}

fl_AutoNum * fl_AutoNum::getAutoNumFromSdh(PL_StruxDocHandle sdh)
{
	UT_sint32 i;
	fl_AutoNum * pAuto;
	if(m_pDoc->areListUpdatesAllowed() == UT_FALSE)
	{
	        if(isItem(sdh) == UT_FALSE)
	        {
	                return (fl_AutoNum *) NULL;
		}
		return this;
	}
        UT_sint32 numLists = m_pDoc->getListsCount();
	for(i=0; i<numLists; i++)
	{
	        pAuto = m_pDoc->getNthList(i);
		if(pAuto->isItem(sdh))
		         break;
	}
	if(i>= numLists)
	{
	        return (fl_AutoNum * ) NULL;
	}
	return pAuto;
}

	   

const UT_Bool fl_AutoNum::isItem(PL_StruxDocHandle pItem) 
{
	if (m_pItems.findItem((void *)(pItem)) == -1)
		return UT_FALSE;
	else
		return UT_TRUE;
}

const UT_Bool fl_AutoNum::isEmpty() 
{
	if (m_pItems.getItemCount() > 0)
		return UT_FALSE;
	else
		return UT_TRUE;
}

PL_StruxDocHandle fl_AutoNum::getFirstItem()
{
	return (PL_StruxDocHandle)
		(m_pItems.getItemCount() ? m_pItems.getFirstItem() : 0);
}

UT_Bool fl_AutoNum::doesItemHaveLabel( fl_BlockLayout * pItem)
{
        fp_Run * pRun = pItem->getFirstRun();
	UT_Bool bStop = UT_FALSE;
	while(bStop == UT_FALSE)
	{
		if(pRun->getType() == FPRUN_FIELD)
		{
	                 fp_FieldRun * pFRun = (fp_FieldRun *) pRun;
	                 if(pFRun->getFieldType() == FPFIELD_list_label)
	                 {
			          bStop = UT_TRUE;
	                          return UT_TRUE;
			 }
		}
	        pRun = pRun->getNext();
		if(pRun == NULL)
		{
		         bStop = UT_TRUE;
		         return UT_FALSE;
		}
	}
	return UT_FALSE;
}

UT_Bool fl_AutoNum::isLastOnLevel(PL_StruxDocHandle pItem) 
{
        UT_sint32 itemloc = m_pItems.findItem((void *) pItem);
	if (itemloc == -1)
		return UT_FALSE;
	if(itemloc == (UT_sint32) (m_pItems.getItemCount() - 1))
		return UT_TRUE;
	else
		return UT_FALSE;
}

fl_AutoNum * fl_AutoNum::getActiveParent(void) 
{
	fl_AutoNum * pAutoNum = m_pParent;

	while (pAutoNum && pAutoNum->isEmpty())
		pAutoNum = pAutoNum->getParent();

	return pAutoNum;
}

void fl_AutoNum::setParent(fl_AutoNum * pParent)
{
	m_bDirty = UT_TRUE;
	m_pParent = pParent;
}

void fl_AutoNum::update(UT_uint32 start)
{
//	UT_DEBUGMSG(("Entering update\n"));
	if (isUpdating())
		return;
	//_calculateLabelStr(0);
	_updateItems(start, NULL);
	void * sdh = const_cast<void *>( getFirstItem());
	if (m_pParent && !m_pParent->isUpdating())
	{
		UT_uint32 ndx = m_pParent->m_pItems.findItem(sdh);
		m_pParent->update(ndx + 1);
	}
}

void fl_AutoNum::_updateItems(UT_uint32 start, PL_StruxDocHandle notMe)
{
  //	UT_DEBUGMSG(("Entering _updateItems\n"));
	UT_sint32 j;
	if(m_pDoc->areListUpdatesAllowed() == UT_TRUE)
	{
		UT_sint32 numlists = m_pDoc->getListsCount();
		m_bUpdatingItems = UT_TRUE;
		for (UT_uint32 i = start; i < m_pItems.getItemCount(); i++)
		{
	//	UT_DEBUGMSG(("Entering _updateItems for loop\n"));
			PL_StruxDocHandle pTmp = (PL_StruxDocHandle) m_pItems.getNthItem(i);
			UT_ASSERT(pTmp);
			m_pDoc->listUpdate(pTmp);
			
 // scan through all the lists and update child lists if connected to this item
	
			PL_StruxDocHandle pItem =  (PL_StruxDocHandle) m_pItems.getNthItem(i);  
			for(j=0; j<numlists; j++)
			{
				fl_AutoNum * pAuto = m_pDoc->getNthList(j);
				UT_ASSERT(pAuto);
				if( pItem == pAuto->getParentItem() && pItem != notMe)
				{
					pAuto->_updateItems(0,pItem);
				}
			}
		}
		m_bUpdatingItems = UT_FALSE;
		m_bDirty = UT_FALSE;
	}
}

PL_StruxDocHandle fl_AutoNum::getNthBlock( UT_uint32 list_num)
{
	if(list_num <0 || list_num >= m_pItems.getItemCount())
		return (PL_StruxDocHandle) NULL;
	else
		return (PL_StruxDocHandle) m_pItems.getNthItem(list_num);
}

PL_StruxDocHandle fl_AutoNum::getPrevInList( PL_StruxDocHandle pItem)
{
        UT_sint32 itemloc = m_pItems.findItem((void *) pItem);
	if (itemloc == -1 || itemloc == 0)
		return NULL;
        return (PL_StruxDocHandle) m_pItems.getNthItem( (UT_uint32) itemloc - 1);
}

inline UT_uint32 fl_AutoNum::_getLevelValue(fl_AutoNum * pAutoNum)
{
	PL_StruxDocHandle pBlock = const_cast<PL_StruxDocHandle>(getFirstItem());
	fl_AutoNum * pCurr = this;

	while (1)
	{
		if (pAutoNum->isItem(pBlock))
		{
			break;
		}
		else
		{
			pCurr = pCurr->getParent();
			pBlock = pCurr->getFirstItem();
		}
	}

	return pAutoNum->getValue(pBlock);
}

char *  fl_AutoNum::dec2roman(UT_sint32 value, UT_Bool lower) 
{
	char roman[80];		//Pretty big number if you ask me

	roman[0] = '\0';

	while( value >= 1000 )
	{
                strcat( roman, "M" );
		value -= 1000;
	}
	if( value >= 900 )
	{
                strcat( roman, "CM" );
		value -= 900;
	}
	while( value >= 500 )
        {
                strcat( roman, "D" );
		value -= 500;
	}
	if( value >= 400 )
	{
                strcat( roman, "CD" );
		value -= 400;
	}
	while( value >= 100 )
	{
	        strcat( roman, "C" );
                value -= 100;
	}
	if( value >= 90 )
        {
                strcat( roman, "XC" );
		value -= 90;
	}
	while( value >= 50 )
	{
                 strcat( roman, "L" );
		 value -= 50;
	}
	if( value >= 40 )
        {
	         strcat( roman, "XL" );
                 value -= 40;
        }
	while( value >= 10 )
        {
                 strcat( roman, "X" );
                 value -= 10;
	}
	if( value >= 9 )
	{
	         strcat( roman, "IX" );
		 value -= 9;
	}
	while( value >= 5 )
	{
                 strcat( roman, "V" );
		 value -= 5;
	}
        if( value >= 4 )
	{
	         strcat( roman, "IV" );
		 value -= 4;
	}
	while( value > 0 )
        {
	         strcat( roman, "I" );
		 value--;
        }

	if (lower == UT_TRUE) 
	{
		int len;
		len = strlen(roman);
		while (--len >= 0) 
		{
		        UT_sint32 r = (UT_sint32) roman[len];
		        if( (r >= (UT_sint32) 'A') && (r <= (UT_sint32) 'Z'))
			       r = r + 32;
			roman[len] = (char) r;
		}
	}

	return UT_strdup(roman);
}

char * fl_AutoNum::dec2ascii(UT_sint32 value, UT_uint32 offset) 
{
	char ascii[30];
	UT_uint32 ndx, count, i;
	
	ascii[0] = '\0';
	ndx = abs(value % 26);
	count = abs(value / 26);
	
	// For now, we do this like Word. A preference would be nice.
	for (i = 0; i <= count; i++)
	{
		ascii[i] = (char)(ndx + offset);
	}
	ascii[i] = '\0';
	
	return UT_strdup(ascii);
}

const char ** fl_AutoNum::getAttributes(void) 
{
        static char  szID[15], szPid[15], szType[5], szStart[5];
        UT_Vector va;
 
	/*       
	szID = (char *)malloc(10);
	szPid = (char *)malloc(10);
	szType = (char *)malloc(5);
	szStart = (char *)malloc(5);
	*/

        sprintf(szID, "%i", m_iID);
        va.addItem( (void *) "id"); va.addItem( (void *) szID);
        
	if (m_pParent)
		sprintf(szPid, "%i", m_pParent->getID());
	else
		sprintf(szPid, "0");
        va.addItem( (void *) "parentid"); va.addItem( (void *) szPid);
                
        sprintf(szType, "%i", m_List_Type);
        va.addItem( (void *) "type"); va.addItem(szType);
        
        sprintf(szStart, "%i", m_iStartValue);
        va.addItem( (void *) "start-value"); va.addItem(szStart);
	
	va.addItem( (void *) "list-delim"); va.addItem( (void *) m_pszDelim);
	
	va.addItem( (void *) "list-decimal"); va.addItem( (void *) m_pszDecimal);
	
        UT_uint32 counta = va.getItemCount() + 1;
	UT_uint32 i;
	const char ** attribs = (const char **) calloc(counta, sizeof(char *));
        for (i = 0; i < va.getItemCount(); i++)
        {
                attribs[i] = (const char *) va[i];
        }
	return attribs;
}