/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"




#include <hintids.hxx>
#include <svl/urihelper.hxx>
#include <unotools/pathoptions.hxx>
#include <tools/stream.hxx>
#ifndef _SFX_INIMGR_HXX
#endif
#include <sfx2/docfile.hxx>
#include <svl/itemiter.hxx>
#include <editeng/brshitem.hxx>


#include <tools/resid.hxx>
#include <fmtornt.hxx>
#include <swtypes.hxx>		// Leerstring
#include <wrtsh.hxx>
#include <uinums.hxx>
#include <poolfmt.hxx>
#include <charfmt.hxx>
#include <frmatr.hxx>

#include <unomid.h>

using namespace ::com::sun::star;


#define VERSION_30B		((sal_uInt16)250)
#define VERSION_31B		((sal_uInt16)326)
#define VERSION_40A		((sal_uInt16)364)
#define VERSION_50A     ((sal_uInt16)373)
#define VERSION_53A     ((sal_uInt16)596)
#define ACT_NUM_VERSION	VERSION_53A

#define NUMRULE_FILENAME "numrule.cfg"
#define CHAPTER_FILENAME "chapter.cfg"

/*------------------------------------------------------------------------
 Beschreibung:	Ops. zum Laden / Speichern
------------------------------------------------------------------------*/


SV_IMPL_PTRARR( _SwNumFmtsAttrs, SfxPoolItem* )


// SwNumRulesWithName ----------------------------------------------------
// PUBLIC METHODES -------------------------------------------------------
/*------------------------------------------------------------------------
 Beschreibung:	Speichern einer Regel
 Parameter:		rCopy -- die zu speichernde Regel
				nIdx -- Position, an der die Regel zu speichern ist.
						Eine alte Regel an dieser Position wird ueberschrieben.
------------------------------------------------------------------------*/

SwBaseNumRules::SwBaseNumRules( const String& rFileName )
    :
    sFileName( rFileName ),
    nVersion(0),
	bModified( sal_False )
{
	Init();
}

/*-----------------26.06.97 08.30-------------------

--------------------------------------------------*/
SwBaseNumRules::~SwBaseNumRules()
{
	if( bModified )
	{
		SvtPathOptions aPathOpt;
        String sNm( aPathOpt.GetUserConfigPath() );
		sNm += INET_PATH_TOKEN;
		sNm += sFileName;
		INetURLObject aTempObj(sNm);
		sNm = aTempObj.GetFull();
		SfxMedium aStrm( sNm, STREAM_WRITE | STREAM_TRUNC |
										STREAM_SHARE_DENYALL, sal_True );
		Store( *aStrm.GetOutStream() );
	}

	for( sal_uInt16 i = 0; i < nMaxRules; ++i )
		delete pNumRules[i];
}

/*------------------------------------------------------------------------
 Beschreibung:
------------------------------------------------------------------------*/
void  SwBaseNumRules::Init()
{
	for(sal_uInt16 i = 0; i < nMaxRules; ++i )
		pNumRules[i] = 0;

	String sNm( sFileName );
	SvtPathOptions aOpt;
	if( aOpt.SearchFile( sNm, SvtPathOptions::PATH_USERCONFIG ))
	{
		SfxMedium aStrm( sNm, STREAM_STD_READ, sal_True );
		Load( *aStrm.GetInStream() );
	}
}

/*-----------------26.06.97 08.30-------------------

--------------------------------------------------*/

void SwBaseNumRules::ApplyNumRules(const SwNumRulesWithName &rCopy, sal_uInt16 nIdx)
{
	ASSERT(nIdx < nMaxRules, Array der NumRules ueberindiziert.);
	if( !pNumRules[nIdx] )
		pNumRules[nIdx] = new SwNumRulesWithName( rCopy );
	else
		*pNumRules[nIdx] = rCopy;
}

// PROTECTED METHODES ----------------------------------------------------
/*------------------------------------------------------------------------
 Beschreibung:	Speichern
------------------------------------------------------------------------*/

sal_Bool /**/ SwBaseNumRules::Store(SvStream &rStream)
{
	rStream << ACT_NUM_VERSION;
		// Schreiben, welche Positionen durch eine Regel belegt sind
		// Anschliessend Schreiben der einzelnen Rules
	for(sal_uInt16 i = 0; i < nMaxRules; ++i)
	{
		if(pNumRules[i])
		{
			rStream << (unsigned char) sal_True;
			pNumRules[i]->Store( rStream );
		}
		else
			rStream << (unsigned char) sal_False;
	}
	return sal_True;
}



/*------------------------------------------------------------------------
 Beschreibung:	Speichern / Laden
------------------------------------------------------------------------*/


int SwBaseNumRules::Load(SvStream &rStream)
{
	int			rc = 0;

	rStream >> nVersion;

	// wegen eines kleinen aber schweren Fehlers schreibt die PreFinal die
	// gleiche VERSION_40A wie das SP2 #55402#
	if(VERSION_40A == nVersion)
	{
		DBG_ERROR("Version 364 ist nicht eindeutig #55402#");
	}
	else if( VERSION_30B == nVersion || VERSION_31B == nVersion ||
			 ACT_NUM_VERSION >= nVersion )
	{
		unsigned char bRule = sal_False;
		for(sal_uInt16 i = 0; i < nMaxRules; ++i)
		{
			rStream >> bRule;
			if(bRule)
				pNumRules[i] = new SwNumRulesWithName( rStream, nVersion );
		}
	}
	else
	{
		rc = 1;
	}

	return rc;
}

/*-----------------26.06.97 08.34-------------------

--------------------------------------------------*/

/*------------------------------------------------------------------------*/


SwChapterNumRules::SwChapterNumRules() :
	SwBaseNumRules(C2S(CHAPTER_FILENAME))
{
}

/*------------------------------------------------------------------------*/

 SwChapterNumRules::~SwChapterNumRules()
{
}

/*-----------------26.06.97 08.23-------------------

--------------------------------------------------*/
void SwChapterNumRules::ApplyNumRules(const SwNumRulesWithName &rCopy, sal_uInt16 nIdx)
{
	bModified = sal_True;
	SwBaseNumRules::ApplyNumRules(rCopy, nIdx);
}

/*------------------------------------------------------------------------*/

SwNumRulesWithName::SwNumRulesWithName( const SwNumRule &rCopy,
                                        const String &rName )
	: aName(rName)
{
    for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
	{
		const SwNumFmt* pFmt = rCopy.GetNumFmt( n );
		if( pFmt )
			aFmts[ n ] = new _SwNumFmtGlobal( *pFmt );
		else
			aFmts[ n ] = 0;
	}
}

/*------------------------------------------------------------------------
 Beschreibung:
------------------------------------------------------------------------*/
SwNumRulesWithName::SwNumRulesWithName( const SwNumRulesWithName& rCopy )
{
	memset( aFmts, 0, sizeof( aFmts ));
	*this = rCopy;
}


/*------------------------------------------------------------------------
 Beschreibung:
------------------------------------------------------------------------*/
SwNumRulesWithName::~SwNumRulesWithName()
{
	for( int n = 0; n < MAXLEVEL; ++n )
		delete aFmts[ n ];
}

/*------------------------------------------------------------------------
 Beschreibung:
------------------------------------------------------------------------*/
const SwNumRulesWithName& SwNumRulesWithName::operator=(const SwNumRulesWithName &rCopy)
{
	if( this != &rCopy )
	{
		aName = rCopy.aName;
		for( int n = 0; n < MAXLEVEL; ++n )
		{
			delete aFmts[ n ];

			_SwNumFmtGlobal* pFmt = rCopy.aFmts[ n ];
			if( pFmt )
				aFmts[ n ] = new _SwNumFmtGlobal( *pFmt );
			else
				aFmts[ n ] = 0;
		}
	}
	return *this;
}

/*------------------------------------------------------------------------
 Beschreibung:
------------------------------------------------------------------------*/
SwNumRulesWithName::SwNumRulesWithName( SvStream &rStream, sal_uInt16 nVersion )
{
	CharSet eEncoding = gsl_getSystemTextEncoding();
	rStream.ReadByteString(aName, eEncoding);

	char c;
	for(sal_uInt16 n = 0; n < MAXLEVEL; ++n )
	{
		if( VERSION_30B == nVersion )
			c = 1;
		// wegen eines kleinen aber schweren Fehlers schreibt die PreFinal die
		// gleiche VERSION_40A wie das SP2 #55402#
		else if(nVersion < VERSION_40A && n > 5)
//		else if(nVersion < VERSION_50A && n > 5)
			c = 0;
		else
			rStream >> c;

		if( c )
			aFmts[ n ] = new _SwNumFmtGlobal( rStream, nVersion );
		else
			aFmts[ n ] = 0;
	}
}

/*------------------------------------------------------------------------
 Beschreibung:
------------------------------------------------------------------------*/

void SwNumRulesWithName::MakeNumRule( SwWrtShell& rSh, SwNumRule& rChg ) const
{
    // --> OD 2008-02-11 #newlistlevelattrs#
    // --> OD 2008-06-06 #i89178#
    rChg = SwNumRule( aName, numfunc::GetDefaultPositionAndSpaceMode() );
    // <--
	rChg.SetAutoRule( sal_False );
	_SwNumFmtGlobal* pFmt;
	for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
		if( 0 != ( pFmt = aFmts[ n ] ) )
		{
			SwNumFmt aNew;
			pFmt->ChgNumFmt( rSh, aNew );
			rChg.Set( n, aNew );
		}
}

/*------------------------------------------------------------------------
 Beschreibung:
------------------------------------------------------------------------*/
void SwNumRulesWithName::Store( SvStream &rStream )
{
	CharSet eEncoding = gsl_getSystemTextEncoding();
	rStream.WriteByteString(aName, eEncoding);

	for( sal_uInt16 n = 0; n < MAXLEVEL; ++n )
	{
		_SwNumFmtGlobal* pFmt = aFmts[ n ];
		if( pFmt )
		{
			rStream << (char)1;
			pFmt->Store( rStream );
		}
		else
			rStream << (char)0;
	}
}
/*------------------------------------------------------------------------
 Beschreibung:
------------------------------------------------------------------------*/


SwNumRulesWithName::_SwNumFmtGlobal::_SwNumFmtGlobal( const SwNumFmt& rFmt )
	: aFmt( rFmt ), nCharPoolId( USHRT_MAX )
{
	// relative Abstaende ?????

	SwCharFmt* pFmt = rFmt.GetCharFmt();
	if( pFmt )
	{
		sCharFmtName = pFmt->GetName();
		nCharPoolId = pFmt->GetPoolFmtId();
		if( pFmt->GetAttrSet().Count() )
		{
			SfxItemIter aIter( pFmt->GetAttrSet() );
			const SfxPoolItem *pCurr = aIter.GetCurItem();
			while( sal_True )
			{
				aItems.Insert( pCurr->Clone(), aItems.Count() );
				if( aIter.IsAtEnd() )
					break;
				pCurr = aIter.NextItem();
			}
		}

		aFmt.SetCharFmt( 0 );
	}
}

/*------------------------------------------------------------------------
 Beschreibung:
------------------------------------------------------------------------*/

SwNumRulesWithName::_SwNumFmtGlobal::_SwNumFmtGlobal( const _SwNumFmtGlobal& rFmt )
    :
    aFmt( rFmt.aFmt ),
    sCharFmtName( rFmt.sCharFmtName ),
    nCharPoolId( rFmt.nCharPoolId )
{
	for( sal_uInt16 n = rFmt.aItems.Count(); n; )
		aItems.Insert( rFmt.aItems[ --n ]->Clone(), aItems.Count() );
}

/*------------------------------------------------------------------------
 Beschreibung:
------------------------------------------------------------------------*/

SwNumRulesWithName::_SwNumFmtGlobal::_SwNumFmtGlobal( SvStream& rStream,
														sal_uInt16 nVersion )
	: nCharPoolId( USHRT_MAX )
{
	CharSet eEncoding = gsl_getSystemTextEncoding();
	{
		sal_uInt16 nUS;
		sal_Char cChar;
		short nShort;
		sal_Bool bFlag;
		String sStr;

		rStream >> nUS;				aFmt.SetNumberingType((sal_Int16)nUS );
		if( VERSION_53A > nVersion )
		{
			rStream >> cChar;		aFmt.SetBulletChar( cChar );
		}
		else
		{
			rStream >> nUS; 		aFmt.SetBulletChar( nUS );
		}

		rStream >> bFlag;			aFmt.SetIncludeUpperLevels( bFlag );

		if( VERSION_30B == nVersion )
		{
			long nL;
			rStream >> cChar;		aFmt.SetStart( (sal_uInt16)cChar );

			rStream.ReadByteString(sStr, eEncoding);
			aFmt.SetPrefix( sStr );
			rStream.ReadByteString(sStr, eEncoding);
			aFmt.SetSuffix( sStr );
			rStream >> nUS;			aFmt.SetNumAdjust( SvxAdjust( nUS ) );
			rStream >> nL;			aFmt.SetLSpace( lNumIndent );
			rStream >> nL;			aFmt.SetFirstLineOffset( (short)nL );
		}
		else 				// alter StartWert war ein Byte
		{
			rStream >> nUS;			aFmt.SetStart( nUS );
			rStream.ReadByteString(sStr, eEncoding);
			aFmt.SetPrefix( sStr );
			rStream.ReadByteString(sStr, eEncoding);
			aFmt.SetSuffix( sStr );
			rStream >> nUS;			aFmt.SetNumAdjust( SvxAdjust( nUS ) );
			rStream >> nUS;			aFmt.SetAbsLSpace( nUS );
			rStream >> nShort;		aFmt.SetFirstLineOffset( nShort );
			rStream >> nUS;			aFmt.SetCharTextDistance( nUS );
			rStream >> nShort;		aFmt.SetLSpace( nShort );
			rStream >> bFlag;
		}

		sal_uInt16	nFamily;
		sal_uInt16  nCharSet;
		short   nWidth;
		short   nHeight;
		sal_uInt16  nPitch;
		String aName;

		rStream.ReadByteString(aName, eEncoding);
		rStream >> nFamily >> nCharSet >> nWidth >> nHeight >> nPitch;

		if( aName.Len() )
		{
			Font aFont( nFamily, Size( nWidth, nHeight ) );
			aFont.SetName( aName );
			aFont.SetCharSet( (CharSet)nCharSet );
			aFont.SetPitch( (FontPitch)nPitch );

			aFmt.SetBulletFont( &aFont );
		}
		else
			nCharSet = RTL_TEXTENCODING_SYMBOL;

		if( VERSION_53A > nVersion )
			aFmt.SetBulletChar( ByteString::ConvertToUnicode(
                                            sal_Char(aFmt.GetBulletChar()), nCharSet ));
	}

	if( VERSION_30B != nVersion )
	{
		sal_uInt16 nItemCount;
		rStream >> nCharPoolId;
		rStream.ReadByteString(sCharFmtName, eEncoding);
		rStream >> nItemCount;

		while( nItemCount-- )
		{
			sal_uInt16 nWhich, nVers;
			rStream >> nWhich >> nVers;
			aItems.Insert( GetDfltAttr( nWhich )->Create( rStream, nVers ),
							aItems.Count() );
		}
	}

	if( VERSION_40A == nVersion && SVX_NUM_BITMAP == aFmt.GetNumberingType() )
	{
		sal_uInt8 cF;
		Size aSz;

		rStream >> aSz.Width() >> aSz.Height();

		rStream >> cF;
		if( cF )
		{
			SvxBrushItem* pBrush = 0;
			SwFmtVertOrient* pVOrient = 0;
			sal_uInt16 nVer;

			if( cF & 1 )
			{
				rStream >> nVer;
				pBrush = (SvxBrushItem*)GetDfltAttr( RES_BACKGROUND )
										->Create( rStream, nVer );
			}

			if( cF & 2 )
			{
				rStream >> nVer;
				pVOrient = (SwFmtVertOrient*)GetDfltAttr( RES_VERT_ORIENT )
										->Create( rStream, nVer );
			}
            sal_Int16 eOrient = text::VertOrientation::NONE;
			if(pVOrient)
                eOrient = (sal_Int16)pVOrient->GetVertOrient();
			aFmt.SetGraphicBrush( pBrush, &aSz, pVOrient ? &eOrient : 0 );
		}
	}
}


/*------------------------------------------------------------------------
 Beschreibung:
------------------------------------------------------------------------*/

SwNumRulesWithName::_SwNumFmtGlobal::~_SwNumFmtGlobal()
{
}
/*------------------------------------------------------------------------
 Beschreibung:
------------------------------------------------------------------------*/


void SwNumRulesWithName::_SwNumFmtGlobal::Store( SvStream& rStream )
{
	CharSet eEncoding = gsl_getSystemTextEncoding();
	{
		String aName;
		sal_uInt16 nFamily = FAMILY_DONTKNOW, nCharSet = 0, nPitch = 0;
		short  nWidth = 0, nHeight = 0;

		const Font* pFnt = aFmt.GetBulletFont();
		if( pFnt )
		{
			aName = pFnt->GetName();
			nFamily = (sal_uInt16)pFnt->GetFamily();
			nCharSet = (sal_uInt16)pFnt->GetCharSet();
			nWidth = (short)pFnt->GetSize().Width();
			nHeight = (short)pFnt->GetSize().Height();
			nPitch = (sal_uInt16)pFnt->GetPitch();
		}

		rStream << sal_uInt16(aFmt.GetNumberingType())
				<< aFmt.GetBulletChar()
				<< (aFmt.GetIncludeUpperLevels() > 0)
				<< aFmt.GetStart();
		rStream.WriteByteString( aFmt.GetPrefix(), eEncoding );
		rStream.WriteByteString( aFmt.GetSuffix(), eEncoding );
		rStream << sal_uInt16( aFmt.GetNumAdjust() )
				<< aFmt.GetAbsLSpace()
				<< aFmt.GetFirstLineOffset()
				<< aFmt.GetCharTextDistance()
				<< aFmt.GetLSpace()
				<< sal_False;//aFmt.IsRelLSpace();
		rStream.WriteByteString( aName, eEncoding );
		rStream << nFamily
				<< nCharSet
				<< nWidth
				<< nHeight
				<< nPitch;
	}
	rStream << nCharPoolId;
	rStream.WriteByteString( sCharFmtName, eEncoding );
	rStream << aItems.Count();

	for( sal_uInt16 n = aItems.Count(); n; )
	{
		SfxPoolItem* pItem = aItems[ --n ];
		sal_uInt16 nIVers = pItem->GetVersion( SOFFICE_FILEFORMAT_50 );
		ASSERT( nIVers != USHRT_MAX,
				"Was'n das: Item-Version USHRT_MAX in der aktuellen Version" );
		rStream << pItem->Which()
				<< nIVers;
		pItem->Store( rStream, nIVers );
	}

	// Erweiterungen fuer Version 40A

	if( SVX_NUM_BITMAP == aFmt.GetNumberingType() )
	{
		rStream << (sal_Int32)aFmt.GetGraphicSize().Width()
				<< (sal_Int32)aFmt.GetGraphicSize().Height();
		sal_uInt8 cFlg = ( 0 != aFmt.GetBrush() ? 1 : 0 ) +
					( 0 != aFmt.GetGraphicOrientation() ? 2 : 0 );
		rStream << cFlg;

		if( aFmt.GetBrush() )
		{
			sal_uInt16 nVersion = aFmt.GetBrush()->GetVersion( SOFFICE_FILEFORMAT_50 );
			rStream << nVersion;
			aFmt.GetBrush()->Store( rStream, nVersion );
		}
		if( aFmt.GetGraphicOrientation() )
		{
			sal_uInt16 nVersion = aFmt.GetGraphicOrientation()->GetVersion( SOFFICE_FILEFORMAT_50 );
			rStream << nVersion;
			aFmt.GetGraphicOrientation()->Store( rStream, nVersion );
		}
	}
}

/*------------------------------------------------------------------------
 Beschreibung:
------------------------------------------------------------------------*/

void SwNumRulesWithName::_SwNumFmtGlobal::ChgNumFmt( SwWrtShell& rSh,
							SwNumFmt& rNew ) const
{
	SwCharFmt* pFmt = 0;
	if( sCharFmtName.Len() )
	{
		// suche erstmal ueber den Namen
		sal_uInt16 nArrLen = rSh.GetCharFmtCount();
		for( sal_uInt16 i = 1; i < nArrLen; ++i )
		{
			pFmt = &rSh.GetCharFmt( i );
			if( COMPARE_EQUAL == pFmt->GetName().CompareTo( sCharFmtName ))
				// ist vorhanden, also belasse die Attribute wie sie sind!
				break;
			pFmt = 0;
		}

		if( !pFmt )
		{
			if( IsPoolUserFmt( nCharPoolId ) )
			{
				pFmt = rSh.MakeCharFmt( sCharFmtName );
				pFmt->SetAuto( sal_False );
			}
			else
				pFmt = rSh.GetCharFmtFromPool( nCharPoolId );

			if( !pFmt->GetDepends() )		// Attribute setzen
				for( sal_uInt16 n = aItems.Count(); n; )
                    pFmt->SetFmtAttr( *aItems[ --n ] );
		}
	}
	((SwNumFmt&)aFmt).SetCharFmt( pFmt );
	rNew = aFmt;
	if( pFmt )
		((SwNumFmt&)aFmt).SetCharFmt( 0 );
}

