/**************************************************************
 * 
 * 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 <stdio.h>
#include <tools/stream.hxx>
#include <tools/fsys.hxx>

#include "tools/iparser.hxx"
#include "tools/geninfo.hxx"



//
// class InformationParser
//

#define cKeyLevelChar '\t'

/*****************************************************************************/
InformationParser::InformationParser( sal_Bool bReplace )
/*****************************************************************************/
				: bRecover( sal_False ),
				sOldLine( "" ),
				bReplaceVariables( bReplace ),
				nLevel( 0 ),
				sUPD( "" ),
				sVersion( "" ),
				pActStream( NULL ),
				nErrorCode( 0 ),
				nErrorLine( 0 ),
				sErrorText( "" ),
				nActLine( 0 )
{
}

/*****************************************************************************/
InformationParser::~InformationParser()
/*****************************************************************************/
{
}

/*****************************************************************************/
ByteString &InformationParser::ReadLine()
/*****************************************************************************/
{
	ByteString sLine;

	if ( bRecover ) {
		bRecover = sal_False;
	}
	else {
		 if ( !pActStream->IsEof()) {
			pActStream->ReadLine( sLine );
            xub_StrLen nStart = 0;
            xub_StrLen nEnd = sLine.Len();
            sal_Bool bCopy = sal_False;
            while ( nStart < nEnd && ( sLine.GetChar( nStart ) == ' ' || sLine.GetChar( nStart ) == 0x09 ) )
            {
                nStart++;
                bCopy = sal_True;
            }

            while ( nStart < nEnd && ( sLine.GetChar( nEnd-1 ) == ' ' || sLine.GetChar( nEnd-1 ) == 0x09 ) )
            {
                nEnd--;
                bCopy = sal_True;
            }

            if ( bCopy )
                sLine = sLine.Copy( nStart, nEnd - nStart );

			if (( sLine.GetChar( 0 ) == '#' ) || ( !sLine.Len())) {
				if ( sCurrentComment.Len())
					sCurrentComment += "\n";
				sCurrentComment += sLine;
				return ReadLine();
			}
			else {
				if ( bReplaceVariables ) {
					sLine.SearchAndReplaceAll( "%UPD", sUPD );
					sLine.SearchAndReplaceAll( "%VERSION", sVersion );
				}
			}
		}
        else {
			if ( nLevel ) {
            	sLine = "}";
	            fprintf( stdout, "Reached EOF parsing %s. Suplying extra '}'\n",ByteString( sStreamName, gsl_getSystemTextEncoding()).GetBuffer() );
	//	        nErrorCode = IP_UNEXPECTED_EOF;
	//	        nErrorLine = nActLine;
			}
			else
				sLine = "";
        }

		sOldLine = sLine;
		nActLine++;
	}

	return sOldLine;
}

/*****************************************************************************/
GenericInformation *InformationParser::ReadKey(
									GenericInformationList *pExistingList )
/*****************************************************************************/
{
	// this method has no error handling yet, but it works very fast.
	// it is used to create whole informations and sub informations in
	// a simple data format in memory, readed in a configuration file with
	// following format:

	/*

	key [value]
	{
		key [value]
		key [value]
		{
			key [value]
			...
			...
		}
	}
	key [value]
	...
	...

	*/

	GenericInformation *pInfo = NULL;

	ByteString sLine( ReadLine());
	ByteString sKey;
	ByteString sValue;
	ByteString sComment( sCurrentComment );
	sCurrentComment = "";

	// key separated from value by tab?
	sal_uInt16 nWSPos = sLine.Search( ' ' );
	if ( sLine.Search( '\t' ) < nWSPos ) {
		nWSPos = sLine.Search( '\t' );
		sLine.SearchAndReplace( "\t", " " );
	}

	if ( sLine.GetTokenCount( ' ' ) > 1 ) {
		sKey = sLine.GetToken( 0, ' ' );
		sValue = sLine.Copy( sKey.Len() + 1 );
		while (( sValue.Search( ' ' ) == 0 ) || ( sValue.Search( '\t' ) == 0 )) {
			sValue.Erase( 0, 1 );
		}
	}
	else
		sKey=sLine;

	if ( bReplaceVariables && !nLevel ) {
		sUPD = sKey.Copy( sKey.Len() - 3 );
		sVersion = sKey;
	}

	if ( ReadLine() == "{" ) {
		nLevel++;
		GenericInformationList *pSubList = new GenericInformationList();
		while ( ReadLine() != "}" ) {
			Recover();
			ReadKey( pSubList );
		}
		nLevel--;
		pInfo = new GenericInformation( sKey, sValue,
						pExistingList, pSubList );
		pInfo->SetComment( sComment );
	}
	else {
		Recover();
        if ( !sKey.Equals( "}" ) && !sKey.Equals( "{" ) )
        {
		    pInfo = new GenericInformation( sKey, sValue, pExistingList );
		    pInfo->SetComment( sComment );
        }
	}

	return pInfo;
}

/*****************************************************************************/
void InformationParser::Recover()
/*****************************************************************************/
{
	bRecover = sal_True;
}

/*****************************************************************************/
sal_Bool InformationParser::Save( SvStream &rOutStream,
			      const GenericInformationList *pSaveList,
			      sal_uInt16 level, sal_Bool bStripped )
/*****************************************************************************/
{
	sal_uInt16 i;
	sal_uIntPtr nInfoListCount;
	ByteString sTmpStr;
	GenericInformation *pGenericInfo;
	GenericInformationList *pGenericInfoList;

 	static ByteString aKeyLevel;
    aKeyLevel.Expand( level, cKeyLevelChar );

	for ( nInfoListCount = 0; nInfoListCount < pSaveList->Count(); nInfoListCount++) {
	    // Key-Value Paare schreiben
    	pGenericInfo = pSaveList->GetObject( nInfoListCount );
    	sTmpStr = "";
        if ( !bStripped && level )
        	sTmpStr.Append( aKeyLevel.GetBuffer(), level );

        if ( !bStripped )
		    for ( i = 0; i < pGenericInfo->GetComment().GetTokenCount( '\n' ); i++ ) {
			    sTmpStr += pGenericInfo->GetComment().GetToken( i, '\n' );
			    sTmpStr += "\n";
                if ( level )
        	        sTmpStr.Append( aKeyLevel.GetBuffer(), level );
		    }

    	sTmpStr += pGenericInfo->GetBuffer();
    	sTmpStr += ' ';
    	sTmpStr += pGenericInfo->GetValue();
    	if ( !rOutStream.WriteLine( sTmpStr ) )
      		return sal_False;

    	// wenn vorhanden, bearbeite recursive die Sublisten
    	if (( pGenericInfoList = pGenericInfo->GetSubList() ) != NULL ) {
      		// oeffnende Klammer
      		sTmpStr = "";
            if ( !bStripped && level )
        	    sTmpStr.Append( aKeyLevel.GetBuffer(), level );
      		sTmpStr += '{';
      		if ( !rOutStream.WriteLine( sTmpStr ) )
				return sal_False;
      		// recursiv die sublist abarbeiten
      		if ( !Save( rOutStream, pGenericInfoList, level+1, bStripped ) )
    			return sal_False;
      			// schliessende Klammer
      		sTmpStr = "";
            if ( !bStripped && level )
        	    sTmpStr.Append( aKeyLevel.GetBuffer(), level );
      		sTmpStr += '}';
      		if ( !rOutStream.WriteLine( sTmpStr ) )
				return sal_False;
    	}
  	}
  	return sal_True;
}

/*****************************************************************************/
GenericInformationList *InformationParser::Execute(
								SvStream &rSourceStream,
								GenericInformationList *pExistingList )
/*****************************************************************************/
{
	GenericInformationList *pList;
	if ( pExistingList )
		pList = pExistingList;
	else
		pList = new GenericInformationList();

	pActStream = &rSourceStream;

	// read all infos out of current file
	while( !rSourceStream.IsEof()) {
		nLevel = 0;
		ReadKey( pList );
	}

	return pList;
}

/*****************************************************************************/
GenericInformationList *InformationParser::Execute( SvMemoryStream &rSourceStream,
						    GenericInformationList *pExistingList )
/*****************************************************************************/
{
  	sStreamName = UniString( "Memory", gsl_getSystemTextEncoding());
  	return Execute( (SvStream &)rSourceStream, pExistingList );
}

/*****************************************************************************/
GenericInformationList *InformationParser::Execute(
								SvFileStream &rSourceStream,
								GenericInformationList *pExistingList )
/*****************************************************************************/
{
	if ( !rSourceStream.IsOpen())
		return NULL;
	sStreamName = rSourceStream.GetFileName();
	return Execute( (SvStream &)rSourceStream, pExistingList );
}

/*****************************************************************************/
GenericInformationList *InformationParser::Execute( UniString &rSourceFile,
								GenericInformationList *pExistingList )
/*****************************************************************************/
{
	DirEntry aDirEntry( rSourceFile );
	if ( !aDirEntry.Exists())
		return NULL;

	GenericInformationList *pList;
	if ( pExistingList )
		pList = pExistingList;
	else
		pList = new GenericInformationList();

	// reset status
	nErrorCode = 0;
	nErrorLine = 0;
	nActLine = 0;

	SvFileStream aActStream;
	aActStream.Open( rSourceFile, STREAM_READ );
	if( aActStream.GetError())
		return NULL;

	pActStream = &aActStream;
	if ( !Execute( aActStream, pList )) {
		delete pList;
		pList = NULL;
	}

	// close the stream
	aActStream.Close();
 	pActStream = NULL;

	if ( !nErrorCode )
		return pList;

	return NULL;
}

/*****************************************************************************/
GenericInformationList *InformationParser::Execute( Dir &rDir,
								GenericInformationList *pExistingList )
/*****************************************************************************/
{
	GenericInformationList *pList;

	if ( pExistingList )
		pList = pExistingList;
	else
		pList = new GenericInformationList();

	for ( sal_uInt16 i = 0; i < rDir.Count(); i++ ) {

		// execute this dir
		UniString sNextFile( rDir[i].GetFull());
		GenericInformationList *pSubList = Execute( sNextFile );

		if ( !pSubList ) {
			// any errors ?
			delete pList;
			return NULL;
		}

		// create new info and insert it into list
		ByteString sFileKey( rDir[i].GetName(), RTL_TEXTENCODING_UTF8 );
		new GenericInformation(
											sFileKey,
											ByteString( "" ),
											pList, pSubList );
	}

	return pList;
}

/*****************************************************************************/
sal_Bool InformationParser::Save( SvFileStream &rSourceStream,
			      const GenericInformationList *pSaveList )
/*****************************************************************************/
{
	if ( !rSourceStream.IsOpen() || !Save( (SvStream &)rSourceStream, pSaveList, 0, sal_False ))
    {
        printf( "ERROR saving file \"%s\"\n",ByteString( rSourceStream.GetFileName(), gsl_getSystemTextEncoding()).GetBuffer() );
		return sal_False;
    }

	return sal_True;
}

/*****************************************************************************/
sal_Bool InformationParser::Save( SvMemoryStream &rSourceStream,
			      const GenericInformationList *pSaveList )
/*****************************************************************************/
{
    Time a;
    sal_Bool bRet = Save( (SvStream &)rSourceStream, pSaveList, 0, sal_True );
    Time b;
    b = b - a;
    return bRet;
}

/*****************************************************************************/
sal_Bool InformationParser::Save( const UniString &rSourceFile,
			      const GenericInformationList *pSaveList )
/*****************************************************************************/
{
  SvFileStream *pOutFile = new SvFileStream( rSourceFile, STREAM_STD_WRITE | STREAM_TRUNC );

  if ( !Save( *pOutFile, pSaveList )) {
    delete pOutFile;
    return sal_False;
  }
  delete pOutFile;
  return sal_True;
}

/*****************************************************************************/
sal_uInt16 InformationParser::GetErrorCode()
/*****************************************************************************/
{
	return nErrorCode;
}

/*****************************************************************************/
ByteString &InformationParser::GetErrorText()
/*****************************************************************************/
{
  //	sErrorText = pActStream->GetFileName();
    sErrorText = ByteString( sStreamName, gsl_getSystemTextEncoding());
	sErrorText += ByteString( " (" );
	sErrorText += ByteString::CreateFromInt64(nErrorLine);
	sErrorText += ByteString( "): " );

	switch ( nErrorCode ) {
	case IP_NO_ERROR:
		sErrorText += ByteString( "Keine Fehler aufgetereten" );
		break;
	case IP_UNEXPECTED_EOF:
		sErrorText += ByteString( "Ungültiges Dateiende!" );
		break;
	}

	return sErrorText;
}


