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

#ifndef FP_LINE_H
#define FP_LINE_H

#ifdef FMT_TEST
#include <stdio.h>
#endif

#include "ut_misc.h"
#include "ut_types.h"
#include "ut_vector.h"
#include "pt_Types.h"
#include "fl_BlockLayout.h"
#include "fp_Column.h"
//#include "fmt_Types.h"

class fp_Run;
class GR_Graphics;
class fp_Container;

struct dg_DrawArgs;

// ----------------------------------------------------------------
/*
	fp_Line represents a single line.  A fp_Line is a collection of 
	Runs.
*/
#ifdef BIDI_ENABLED
//these are the initial sizes of the temp buffers
#define TEMP_LINE_BUFFER_SIZE 512

// the memory requirement connected with the following constant
// is 9 bytes per 1; 100 is reasonable: if loading documents with long paragraphs
// this will have to be reallocated, since initially each paragraph is loaded
// on a single line, but for creating documents from a scratch this is
// more than adequate.
#define RUNS_MAP_SIZE 100
/* the following define determines whether the map used to convert logical to visual position and
   vice versa is shared by all lines, or whether each line has map of its own. In the former case
   only small encrease in memory requirenments is needed, but we have to recalculate the map
   more often; in the latter case we only need to recalculate the map when a run
   is added or deleted, but the increase in memory requirenments is significant:
   sizeof(UT_uint32) * run_count * no_of_lines

   there is a mechanism in place that makes sure that the static map gets calculated only when it
   is really necessary and from my debugging experience this is not much more often that in the
   case of the non-static map, so this appears to be definitely the better option;
   PLEASE NOTE that if you change the option you will have to rebuild.
*/
#define USE_STATIC_MAP

//#define FP_LINE_DIRECTION_USED_NONE 0X00
//#define FP_LINE_DIRECTION_USED_LTR	0X01
//#define FP_LINE_DIRECTION_USED_RTL  0x02
//#define FP_LINE_DIRECTION_USED_BOTH 0x04

#endif //BIDI_ENABLED


enum FL_WORKING_DIRECTION {WORK_FORWARD = 1,WORK_BACKWARD = -1,WORK_CENTER = 0};
enum FL_WHICH_TABSTOP {USE_PREV_TABSTOP,USE_NEXT_TABSTOP,USE_FIXED_TABWIDTH};



class ABI_EXPORT fp_Line
{
public:
	fp_Line();
	~fp_Line();

	inline fp_Container* 		getContainer(void) const	{ return m_pContainer; }
	inline fl_BlockLayout*		getBlock(void) const 		{ return m_pBlock; }
	//! Return height of line as it will appear on screen
	inline UT_sint32 			getHeight(void) const 		{ return (m_iScreenHeight != -1) ? m_iScreenHeight : m_iHeight; }
	inline UT_sint32 			getHeightInLayoutUnits(void) const 		{ return m_iHeightLayoutUnits; }
	
	inline UT_sint32			getX(void) const 			{ return m_iX; }
	inline UT_sint32			getXInLayoutUnits(void) const 	{ return m_iXLayoutUnits; }
	inline UT_sint32			getY(void) const 			{ return m_iY; }
	
	inline fp_Line*				getNext(void) const 		{ return m_pNext; }
	inline fp_Line*    			getPrev(void) const 		{ return m_pPrev; }

//	inline UT_sint32 			getWidth(void) const	 	{ return m_iWidth; }
	inline UT_sint32			getMaxWidth(void) const 	{ return m_iMaxWidth; }
	inline UT_sint32			getMaxWidthInLayoutUnits(void) const 	{ UT_ASSERT(m_iMaxWidthLayoutUnits); return m_iMaxWidthLayoutUnits; }
	inline UT_sint32			getAscent(void) const 		{ return m_iAscent; }
	inline UT_sint32			getDescent(void) const 		{ return m_iDescent; }
	UT_sint32                   getNumRunsInLine(void) const {return m_vecRuns.getItemCount();}
	inline UT_sint32			getColumnGap(void) const	{ return m_pContainer->getColumnGap(); }
	
	void				setAssignedScreenHeight(UT_sint32);

	void				setMaxWidth(UT_sint32);
	void				setMaxWidthInLayoutUnits(UT_sint32);
	void				setX(UT_sint32);
	void				setXInLayoutUnits(UT_sint32);
	void				setY(UT_sint32);
	void				setYInLayoutUnits(UT_sint32);
	inline	void		setNext(fp_Line * p)				{ m_pNext = p; }
	inline	void        setPrev(fp_Line * p)				{ m_pPrev = p; }
	void				setContainer(fp_Container*);
	inline	void		setBlock(fl_BlockLayout * pBlock)	{ m_pBlock = pBlock; }

	fp_Line*	getNextLineInSection(void) const;
	fp_Line*	getPrevLineInSection(void) const;
	fp_Run *    getRunFromIndex( UT_uint32 runIndex);
	bool		containsForcedColumnBreak(void) const;
	bool		containsForcedPageBreak(void) const;
	
	void        updateBackgroundColor();
	void 		addRun(fp_Run*);
	void		insertRunAfter(fp_Run* pRun1, fp_Run* pRun2);
	void		insertRunBefore(fp_Run* pNewRun, fp_Run* pBefore);
	void        insertRun(fp_Run*);
    bool     removeRun(fp_Run*, bool bTellTheRunAboutIt=false);
	
	inline	bool		isEmpty(void) const				{ return ((m_vecRuns.getItemCount()) == 0); }
	inline	int 		countRuns(void) const			{ return m_vecRuns.getItemCount(); }
	inline	fp_Run*     getFirstRun(void) const			{ if(countRuns() > 0)  return ((fp_Run*) m_vecRuns.getFirstItem()); else return NULL; }
	fp_Run*     getLastRun(void) const ;
	fp_Run*     getLastTextRun(void) const ;
	
	fp_Run*	calculateWidthOfRun(UT_sint32 &iXLayoutUnits,
								UT_uint32 iIndxVisual,
								FL_WORKING_DIRECTION eWorkingDirection,
								FL_WHICH_TABSTOP eUseTabStop);
							
	void		getWorkingDirectionAndTabstops(FL_WORKING_DIRECTION &eWorkingDirection, FL_WHICH_TABSTOP &eUseTabStop) const;

	inline	bool 	isFirstLineInBlock(void) const	{ return (m_pBlock->getFirstLine() == this); }
	inline	bool 	isLastLineInBlock(void) const	{ return (m_pBlock->getLastLine() == this); }
	
	void		remove(void);
	UT_sint32	getMarginBefore(void) const;
	UT_sint32	getMarginAfter(void) const;
	UT_sint32	getMarginAfterInLayoutUnits(void) const;

	void		mapXYToPosition(UT_sint32 xPos, UT_sint32 yPos, PT_DocPosition& pos, bool& bBOL, bool& bEOL);
	void		getOffsets(fp_Run* pRun, UT_sint32& xoff, UT_sint32& yoff);
	void		getScreenOffsets(fp_Run* pRun, UT_sint32& xoff, UT_sint32& yoff);

	void		clearScreen(void);
	void		clearScreenFromRunToEnd(UT_uint32 runIndex);
	void		clearScreenFromRunToEnd(fp_Run * pRun);
	void		draw(dg_DrawArgs*);
	void        draw(GR_Graphics*);
	void		align(void);
	void		layout(void);
	bool		recalculateFields(UT_uint32 iUpdateCount);
	void		recalcHeight();
	void		recalcMaxWidth();
	void		coalesceRuns(void);

	UT_sint32	calculateWidthOfLine(void);
	UT_sint32	calculateWidthOfLineInLayoutUnits(void);
	UT_sint32	calculateWidthOfTrailingSpaces(void);
	UT_sint32	calculateWidthOfTrailingSpacesInLayoutUnits(void);
	void		resetJustification();
	void		distributeJustificationAmongstSpaces(UT_sint32 iAmount);
	UT_uint32	countJustificationPoints(void)
#ifndef BIDI_ENABLED
                                              const;
#else
	                                               ;
#endif

	bool		isLastCharacter(UT_UCSChar Character) const;

	bool		findNextTabStop(UT_sint32 iStartX, UT_sint32& iPosition, eTabType& iType, eTabLeader& iLeader );
	bool		findNextTabStopInLayoutUnits(UT_sint32 iStartX, UT_sint32& iPosition, eTabType& iType, eTabLeader& iLeader);
	bool		findPrevTabStop(UT_sint32 iStartX, UT_sint32& iPosition, eTabType& iType, eTabLeader& iLeader );
	bool		findPrevTabStopInLayoutUnits(UT_sint32 iStartX, UT_sint32& iPosition, eTabType& iType, eTabLeader& iLeader);
	
	void		setNeedsRedraw(void);
	//void		setRedoLayout(void){ m_bRedoLayout = true; }
	bool		needsRedraw(void) { return m_bNeedsRedraw; }
	void		redrawUpdate(void);
#ifdef BIDI_ENABLED
	fp_Run *	getLastVisRun();
	fp_Run *	getFirstVisRun();
	UT_uint32	getVisIndx(fp_Run* pRun);
	fp_Run *	getRunAtVisPos(UT_uint32 i);
	void		setMapOfRunsDirty(){m_bMapDirty = true;};
	void		addDirectionUsed(FriBidiCharType dir, bool bRefreshMap = true);
	void		removeDirectionUsed(FriBidiCharType dir, bool bRefreshMap = true);
	void		changeDirectionUsed(FriBidiCharType oldDir, FriBidiCharType newDir, bool bRefreshMap = true);


#endif

#ifdef FMT_TEST
	void		__dump(FILE * fp) const;
#endif	

protected:
	void 	_calculateWidthOfRun(UT_sint32 &iX,
							  UT_sint32 &iXLayoutUnits,
							  fp_Run * pRun,
							  UT_uint32 iIndx,
							  UT_uint32 iCountRuns,
							  FL_WORKING_DIRECTION eWorkingDirection,
							  FL_WHICH_TABSTOP eUseTabStop
#ifdef BIDI_ENABLED
							  ,FriBidiCharType iDomDirection
#endif							
							  );
private:
void		_splitRunsAtSpaces(void);


	fl_BlockLayout*	m_pBlock;
	fp_Container*	m_pContainer;
	
	UT_sint32	 	m_iWidth;
	UT_sint32	 	m_iWidthLayoutUnits;
	UT_sint32	 	m_iMaxWidth;
	UT_sint32		m_iMaxWidthLayoutUnits;
	UT_sint32       m_iClearToPos;
	UT_sint32       m_iClearLeftOffset;
	UT_sint32 		m_iHeight;
	//! Height assigned on screen
	//! -1 if undefined
	UT_sint32 		m_iScreenHeight;
	UT_sint32 		m_iHeightLayoutUnits;
	UT_sint32 		m_iAscent;
	UT_sint32		m_iDescent;

	UT_sint32		m_iX;
	UT_sint32		m_iXLayoutUnits;
	UT_sint32		m_iY;
	UT_sint32		m_iYLayoutUnits;
	
	UT_Vector		m_vecRuns;

	fp_Line*		m_pNext;
	fp_Line*        m_pPrev;

	bool			m_bNeedsRedraw;
	//bool			m_bRedoLayout;
	static UT_sint32 * s_pOldXs;
	static UT_uint32   s_iOldXsSize;
	static UT_uint32   s_iClassInstanceCounter;	
	
#ifdef BIDI_ENABLED
	UT_uint32       _getRunVisIndx(UT_uint32 indx);
	UT_uint32       _getRunLogIndx(UT_uint32 indx);
	UT_sint32       _createMapOfRuns();	
	
#ifdef USE_STATIC_MAP
	static UT_Byte *   s_pEmbeddingLevels;
	static UT_uint16 * s_pMapOfRunsL2V;
	static UT_uint16 * s_pMapOfRunsV2L;
	static UT_uint32 * s_pPseudoString;
	
	
	static UT_uint32   s_iMapOfRunsSize;
	static fp_Line   * s_pMapOwner;
	bool            m_bMapDirty;
#else	
	UT_sint32  * 	m_pMapOfDirs;
	UT_sint32  *    m_pMapOfRunsL2V;
	UT_sint32  *    m_pMapOfRunsV2L;
	
	UT_uint32       m_iMapOfRunsSize;
#endif
	UT_uint32		m_iRunsRTLcount;
	UT_uint32		m_iRunsLTRcount;
	UT_sint32		m_iMaxDirLevel;
#endif //BIDI_ENABLED
};

#endif /* FP_LINE_H */


