/**************************************************************
 * 
 * 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_tools.hxx"
#include "tools/geninfo.hxx"
#include <stdio.h>

//
// class GenericInformation
//

/*****************************************************************************/
GenericInformation::GenericInformation( const ByteString &rKey,
						const ByteString &rValue,
						GenericInformationList *pParentList,
						GenericInformationList *pSubInfos )
/*****************************************************************************/
				: ByteString( rKey ),
				sValue( rValue ),
				pInfoList( pSubInfos ),
				pParent( pParentList )
{
	// if a ParentList exists, insert this object into it
	if ( pParent )
		pParent->InsertInfo( this );
	// make myself owner of pInfoList
	if ( pInfoList )
		pInfoList->SetOwner( this );
}

/*****************************************************************************/
GenericInformation::GenericInformation( const GenericInformation& rInf,
										sal_Bool bCopySubs)
/*****************************************************************************/
				: ByteString( rInf ),
				sValue( rInf.sValue ),
				pInfoList( 0L ),
				pParent(NULL)
{
	if(bCopySubs && rInf.pInfoList)
		pInfoList = new GenericInformationList(*rInf.pInfoList, this);
}

/*****************************************************************************/
GenericInformation::~GenericInformation()
/*****************************************************************************/
{
	// remove pInfoList and all childs out of memory
	delete pInfoList;
	pInfoList = 0;

	// remove this Info out of ParentList
	if ( pParent )
		pParent->RemoveInfo( this );
}

/*****************************************************************************/
sal_Bool GenericInformation::InsertSubInfo( GenericInformation *pInfo )
/*****************************************************************************/
{
	return ( pInfoList && pInfoList->InsertInfo( pInfo ));
}

/*****************************************************************************/
sal_Bool GenericInformation::InsertSubInfo( const ByteString &rPathKey,	const ByteString &rValue,
					sal_Bool bSearchByPath, sal_Bool bNewPath )
/*****************************************************************************/
{
  return (pInfoList && pInfoList->InsertInfo( rPathKey, rValue, bSearchByPath, bNewPath ));
}

/*****************************************************************************/
void GenericInformation::RemoveSubInfo( GenericInformation *pInfo,
							sal_Bool bDelete )
/*****************************************************************************/
{
	pInfoList->RemoveInfo( pInfo, bDelete );
}

/*****************************************************************************/
//void GenericInformation::RemoveSelf( sal_Bool bDelete )
/*****************************************************************************/
/*{
  if ( pParent )
    pParent->RemoveInfo( this, bDelete ); // loescht sich aus der Liste vom Parent und
  // bei Bedarf auch mit obiger Methode alle Sublisten

  // loescht sich bei Bedarf auch selbst
    if ( bDelete )
    delete this;
}
*/

/*****************************************************************************/
GenericInformation *GenericInformation::GetSubInfo( ByteString &rKey,
						sal_Bool bSearchByPath,
						    sal_Bool bCreatePath )
/*****************************************************************************/
{
  if ( !pInfoList && bCreatePath )
	pInfoList = new GenericInformationList( this );
  if ( pInfoList )
    return pInfoList->GetInfo( rKey, bSearchByPath, bCreatePath );
  return NULL;
}


//
// class GenericInformationList
//

/*****************************************************************************/
GenericInformationList::GenericInformationList( GenericInformation *pParent )
/*****************************************************************************/
				: pOwner( pParent )
{
}

/*****************************************************************************/
GenericInformationList::GenericInformationList(const GenericInformationList& rList,
							GenericInformation *pParent)
/*****************************************************************************/
    : GenericInformationList_Impl()
{
	sal_uInt16 i;
	GenericInformation* pTemp,*pWork;

	pOwner = pParent;

	for(i=0;i<rList.Count();i++)
	{
		pTemp = rList.GetObject(i);
		pWork = new GenericInformation(*pTemp,sal_True);

		Insert(pWork,LIST_APPEND);
	}
}

/*****************************************************************************/
GenericInformationList::~GenericInformationList()
/*****************************************************************************/
{
	// delete all Informations stored in this List
	// ### GH: Hier werden dann wohl etwa die H�lfte der Eintr�ge gel�scht
/*	for ( sal_uIntPtr i = 0; i < Count(); i++ ) {
		GetObject( i )->ListDeleted();
		delete GetObject( i );
		Remove( i );*/
	// Neue Variante:
	while ( Count() ) {
		GetObject( 0 )->ListDeleted();
		delete GetObject( 0 );
		Remove( (sal_uIntPtr)0 );
	}
}

/*****************************************************************************/
GenericInformation *GenericInformationList::Search( sal_uIntPtr &rPos, ByteString sKey,
												   sal_uIntPtr nStart, sal_uIntPtr nEnd )
/*****************************************************************************/
{
	if ( Count() == 0 ) {
		rPos = 0;
		return NULL;
	}

	if ( nStart == nEnd ) {
		rPos = nStart;
		ByteString sCandidate = ByteString( *GetObject( nStart ));
		if ( sCandidate.ToUpperAscii() == sKey.ToUpperAscii()) {
			return GetObject( nStart ); // found !!!
		}
		else {
			// requested key not found
			return NULL;
		}
	}

	// search binary in existing list
	sal_uIntPtr nActPos = nStart + (( nEnd - nStart ) / 2 );
	rPos = nActPos;
	ByteString sCandidate = ByteString( *GetObject( nActPos ));

	if ( sCandidate.ToUpperAscii()  == sKey.ToUpperAscii())
		return GetObject( nActPos ); // found !!!

	// split the list at ActPos
	if ( sCandidate < sKey )
		return Search( rPos, sKey, nActPos + 1, nEnd );
	else
		return Search( rPos, sKey, nStart, nActPos );
}

/*****************************************************************************/
GenericInformation *GenericInformationList::GetInfo( ByteString &rKey,
						     sal_Bool bSearchByPath,
						     sal_Bool bCreatePath )
/*****************************************************************************/
{

	rKey.EraseLeadingChars( '/' );
	rKey.EraseTrailingChars( '/' );

	ByteString sKey;
	if ( bSearchByPath )
		sKey = rKey.GetToken( 0, '/' );
	else
		sKey = rKey;

	sal_uIntPtr nPos = 0;
	GenericInformation *pReturnInfo = Search( nPos, sKey, 0, Count() - 1 );
	/* wenn kein Searchpath gesetzt und kein Returninfo vorhanden,
	 *   gib NULL zurueck
	 * wenn Searchpath gesetzt und returninfo vorhanden,
	 *   suche weiter nach unten
	 * wenn searchpath gesetzt kein returninfo vorhanden und newpath gesetzt,
	 *   mache neues Verzeichniss
	 */
	sal_uInt16 nTokenCount = rKey.GetTokenCount('/');
	// search for next key of path in next level of tree
	if ( bSearchByPath && (nTokenCount > 1)) {
	  ByteString sPath = ByteString(rKey.Copy( sKey.Len() + 1 ));
	  if ( !pReturnInfo ) { // wenn kein Return, dann muss man es anlegen
	    if ( !bCreatePath ) // wenn aber kein Create, dann nicht anlegen
	      return NULL;
	    pReturnInfo = new GenericInformation( sKey, "", this, NULL);
	    pReturnInfo->SetSubList( new GenericInformationList( pReturnInfo ));
	  }
	  return pReturnInfo->GetSubInfo( sPath, sal_True, bCreatePath );
	}
	if ( !pReturnInfo && bCreatePath ) {
	  pReturnInfo = new GenericInformation ( sKey, "", this, NULL);
	}

	return pReturnInfo; // kann durchaus NULL sein.
}

/*****************************************************************************/
sal_uIntPtr GenericInformationList::InsertSorted( GenericInformation *pInfo,
										sal_Bool bOverwrite,
										sal_uIntPtr nStart, sal_uIntPtr nEnd )
/*****************************************************************************/
{
    if ( Count() == 0 ) {
		// empty list, so insert at first pos
		Insert( pInfo, LIST_APPEND );
		return 0;
	}

	ByteString sKey( pInfo->GetBuffer());
    sKey.ToUpperAscii();

    // Check to sppeed up reading a (partially) sorted list
    if ( nStart == 0 && Count()-1 == nEnd )
    {
		ByteString sCandidate( *GetObject( nEnd ));
		if ( sCandidate.ToUpperAscii() < sKey )
        {
			Insert( pInfo, LIST_APPEND );
			return nEnd+1;
        }
    }

// ### GH: dieser Block schein �berfl�ssig zu sein
	if ( Count() == 1 ) {
		ByteString sCandidate( *GetObject( 0 ));
		if ( sCandidate.ToUpperAscii() == sKey ) {
			// key allready exists in list
			if ( bOverwrite )
				Replace( pInfo, sal_uIntPtr(0));	// ### Laut NF scheint hier ein Memory Leak zu sein
			return 0;
		}
		else if ( sCandidate > sKey ) {
			Insert( pInfo, sal_uIntPtr(0));
			return 0;
		}
		else {
			Insert( pInfo, LIST_APPEND );
			return 1;
		}
	}
// ### GH: /ENDE/ dieser Block schein �berfl�ssig zu sein

	sal_uIntPtr nActPos = nStart + (( nEnd - nStart ) / 2 );
	ByteString sCandidate = ByteString( *GetObject( nActPos ));

	if ( sCandidate.ToUpperAscii() == sKey ) {
		// key allready exists in list
		if ( bOverwrite )
			Replace( pInfo, nActPos );	// ### Laut NF scheint hier ein Memory Leak zu sein
		return nActPos;
	}

	if ( nStart == nEnd ) {
		// now more ways to search for key -> insert here
		if ( sCandidate > sKey ) {
			Insert( pInfo, nStart );
			return nStart;
		}
		else {
			Insert( pInfo, nStart + 1 );
			return ( nStart + 1 );
		}
	}

	if ( nActPos == Count() - 1 ) {
		// reached end of list -> insert here
		Insert( pInfo, LIST_APPEND );
		return ( nActPos + 1 );
	}

	ByteString sSecondCand = ByteString( *GetObject( nActPos + 1 ));
	if (( sCandidate < sKey ) && ( sSecondCand.ToUpperAscii() > sKey )) {
		// optimal position to insert object
		Insert( pInfo, nActPos + 1 );
		return ( nActPos + 1 );
	}

	if ( sCandidate < sKey )
		return InsertSorted( pInfo, bOverwrite, nActPos + 1, nEnd );
	else
		return InsertSorted( pInfo, bOverwrite, nStart, nActPos );
}

/*****************************************************************************/
sal_Bool GenericInformationList::InsertInfo( GenericInformation *pInfo,
								sal_Bool bOverwrite )
/*****************************************************************************/
{
	if ( !pInfo->Len())
		return sal_False;

	InsertSorted( pInfo, bOverwrite, 0, Count() - 1 );
	return sal_True;
}


/*****************************************************************************/
sal_Bool GenericInformationList::InsertInfo( const ByteString &rPathKey, const ByteString &rValue,
					 sal_Bool bSearchByPath, sal_Bool bNewPath )
/*****************************************************************************/
{
  GenericInformation *pInfo;
  ByteString sPathKey ( rPathKey );
  sPathKey.EraseLeadingChars( '/' );
  sPathKey.EraseTrailingChars( '/' );

  pInfo = GetInfo( sPathKey, bSearchByPath, bNewPath );

  if ( pInfo ) {
    pInfo->SetValue( rValue );
    return sal_True;
  }
  return sal_False;
}

/*****************************************************************************/
void GenericInformationList::RemoveInfo( GenericInformation *pInfo,
								sal_Bool bDelete )
/*****************************************************************************/
{
	Remove( pInfo );
	if ( bDelete )
		delete pInfo;
/*	if ( Count() == 0 && pOwner )	// Leere Listen entfernen;
	{
		SetOwner( NULL );
		delete this;
	} Rausgepatched by GH */
}

GenericInformation* GenericInformationList::SetOwner( GenericInformation *pNewOwner )
{
	GenericInformation *pOldOwner = pOwner;
	if ( pOwner )	// bei parent austragen;
		pOwner->SetSubList( NULL );
	if ( pNewOwner )
		pNewOwner->SetSubList( this );
	pOwner = pNewOwner;
	return pOldOwner;
}


