/**************************************************************
 * 
 * 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.
 * 
 *************************************************************/

 

#include "prodmap.hxx"
#include <tools/geninfo.hxx>
#include <tools/fsys.hxx>
#include "minormk.hxx"

#include <stdio.h>

#define PRODUCT_KEY 	"TARGETDESCRIPTION/PRODUCTS"
#define DEPENDS_ON_KEY	"TARGETDESCRIPTION/DEPENDSON"
#define BASED_ON_KEY	"TARGETDESCRIPTION/BASEDON"

//
// class ProductMapper
//

/*****************************************************************************/
ProductMapper::ProductMapper()
/*****************************************************************************/
				: pVersionList( NULL ),
				pProductList( NULL )
{
}

/*****************************************************************************/
ProductMapper::ProductMapper( GenericInformationList *pVerList )
/*****************************************************************************/
				: pVersionList( pVerList ),
				pProductList( NULL )
{
	if ( pVerList )
		CreateProductList( pVerList );
}

/*****************************************************************************/
ProductMapper::~ProductMapper()
/*****************************************************************************/
{
	delete pProductList;
}

/*****************************************************************************/
void ProductMapper::CreateProductList( GenericInformationList *pVerList )
/*****************************************************************************/
{
	/*
	creates a list of the following format:

	ProductName Workspace                // 6.0 Final SRC641
	{                                    // {
		DependsOn                        //     DependsOn
		{                                //     {
			product1                     //
			product2                     //
			...                          //
		}                                //
		BasedOn                          //
		{                                //
			productX                     //
			productY                     //
			...                          //
		}                                //
	}                                    //
	*/

	delete pProductList;
	pProductList = NULL;

	pVersionList = pVerList;

	if ( pVersionList ) {
		ByteString sProductKey( PRODUCT_KEY );
		ByteString sDependsOnKey( DEPENDS_ON_KEY );
		ByteString sBasedOnKey( BASED_ON_KEY );

		for ( sal_uIntPtr i = 0; i < pVersionList->Count(); i++ ) {
			GenericInformation *pVersion = pVersionList->GetObject( i );

			GenericInformation *pProducts = pVersion->GetSubInfo( sProductKey, sal_True );
			if ( pProducts ) {
				ByteString sProducts = pProducts->GetValue();

				ByteString sDependsOn;
				GenericInformation *pDependsOn = pVersion->GetSubInfo( sDependsOnKey, sal_True );
				if ( pDependsOn )
					sDependsOn = pDependsOn->GetValue();

				ByteString sBasedOn;
				GenericInformation *pBasedOn = pVersion->GetSubInfo( sBasedOnKey, sal_True );
				if ( pBasedOn )
					sBasedOn = pBasedOn->GetValue();

				for ( sal_uInt16 x = 0; x < sProducts.GetTokenCount( ';' ); x++ ) {
					ByteString sProduct( sProducts.GetToken( x, ';' ));
					if( sProduct.Len()) {
						if ( !pProductList )
							pProductList = new GenericInformationList();

						pProductList->InsertInfo( sProduct, *pVersion, sal_True, sal_True );

						for ( sal_uInt16 y = 0; y < sDependsOn.GetTokenCount( ';' ); y++ ) {
							ByteString sDependsOnKey_l = sProduct;
							sDependsOnKey_l += "/DependsOn/";
							sDependsOnKey_l += sDependsOn.GetToken( y, ';' );

							pProductList->InsertInfo( sDependsOnKey_l, "", sal_True, sal_True );
						}
						for ( sal_uInt16 z = 0; z < sBasedOn.GetTokenCount( ';' ); z++ ) {
							ByteString sBasedOnKey_l = sProduct;
							sBasedOnKey_l += "/BasedOn/";
							sBasedOnKey_l += sBasedOn.GetToken( z, ';' );

							pProductList->InsertInfo( sBasedOnKey_l, "", sal_True, sal_True );
						}
					}
				}
			}
		}
	}
}

/*****************************************************************************/
sal_uInt16 ProductMapper::GetProductInformation(
	const ByteString &rProduct, GenericInformation *& pProductInfo )
/*****************************************************************************/
{
	pProductInfo = NULL;

	if ( !pVersionList )
		return PRODUCT_MAPPER_NO_VERSION_INFORMATION;

	if ( !pProductList )
		return PRODUCT_MAPPER_NO_PRODUCT;

	ByteString sProductKey( rProduct );
	pProductInfo = pProductList->GetInfo( sProductKey, sal_True );

	if ( !pProductInfo )
		return PRODUCT_MAPPER_NO_PRODUCT;

	return PRODUCT_MAPPER_OK;
}

/*****************************************************************************/
sal_uInt16 ProductMapper::PrintDependentTargets(
	const ByteString &rProduct, sal_uInt16 nLevel )
/*****************************************************************************/
{
	GenericInformation *pProductInfo;

	sal_uInt16 nReturn = GetProductInformation( rProduct, pProductInfo );

	if ( nReturn == PRODUCT_MAPPER_OK ) {
		for ( sal_uInt16 i = 0; i < nLevel; i++ )
			fprintf( stdout, "    " );
		fprintf( stdout, "%s (%s)\n", pProductInfo->GetBuffer(),
			pProductInfo->GetValue().GetBuffer());
		aPrintedList.PutString( new ByteString( *pProductInfo ));

		for ( sal_uIntPtr j = 0; j < pProductList->Count(); j++ ) {
			GenericInformation *pCandidate = pProductList->GetObject( j );
			ByteString sKey( "DEPENDSON/" );
			sKey += rProduct;
			GenericInformation *pDependsOn = pCandidate->GetSubInfo( sKey, sal_True );
			if ( pDependsOn )
				PrintDependentTargets( *pCandidate, nLevel + 1 );
		}
		if ( !nLevel ) {
			ByteString sKey( "BASEDON" );
			GenericInformation *pBasedOn = pProductInfo->GetSubInfo( sKey );
			if ( pBasedOn ) {
				GenericInformationList *pBases = pBasedOn->GetSubList();
				if ( pBases ) {
					for ( sal_uIntPtr k = 0; k < pBases->Count(); k++ ) {
						aBaseList.PutString( new ByteString( *pBases->GetObject( k )));
					}
				}
			}
		}
	}

	return nReturn;
}

/*****************************************************************************/
sal_uInt16 ProductMapper::PrintAndDeleteBaseList()
/*****************************************************************************/
{
	if ( aBaseList.Count()) {
		fprintf( stdout, "\nbased on\n" );
		while ( aBaseList.Count()) {
			ByteString sProduct( *aBaseList.GetObject(( sal_uIntPtr ) 0 ));
			if ( aPrintedList.IsString( aBaseList.GetObject(( sal_uIntPtr ) 0 )) == NOT_THERE ) {
				aPrintedList.PutString( aBaseList.GetObject(( sal_uIntPtr ) 0 ));
				PrintDependentTargets( sProduct );
			}
			else
				delete aBaseList.GetObject(( sal_uIntPtr ) 0 );

			aBaseList.Remove(( sal_uIntPtr ) 0 );
		}
		while ( aPrintedList.Count())
			delete aPrintedList.Remove(( sal_uIntPtr ) 0 );

		fprintf( stdout, "\n" );
	}
	return PRODUCT_MAPPER_OK;
}

/*****************************************************************************/
sal_uInt16 ProductMapper::PrintDependencies( const ByteString &rProduct )
/*****************************************************************************/
{
	sal_uInt16 nResult = PrintDependentTargets( rProduct );
	PrintAndDeleteBaseList();
	return nResult;
}

/*****************************************************************************/
sal_uInt16 ProductMapper::PrintProductList()
/*****************************************************************************/
{
	if ( !pVersionList )
		return PRODUCT_MAPPER_NO_VERSION_INFORMATION;

	if ( !pProductList || !pProductList->Count())
		return PRODUCT_MAPPER_NO_PRODUCT;

	if ( pProductList->Count()) {
		for ( sal_uIntPtr i = 0; i < pProductList->Count(); i++ )
			fprintf( stdout, "%s (%s)\n",
				pProductList->GetObject( i )->GetBuffer(),
				pProductList->GetObject( i )->GetValue().GetBuffer());
		fprintf( stdout, "\n" );
	}

	return PRODUCT_MAPPER_OK;
}

/*****************************************************************************/
SByteStringList *ProductMapper::GetMinorList(
	const ByteString &rVersion, const ByteString &rEnvironment )
/*****************************************************************************/
{
	SByteStringList *pList = NULL;

	if ( pVersionList ) {
		String sRoot( GetVersionRoot( pVersionList, rVersion ));
		if ( sRoot.Len()) {
			DirEntry aEntry( sRoot );
			aEntry += DirEntry( String( rEnvironment, RTL_TEXTENCODING_ASCII_US ));
			String sWildcard( String::CreateFromAscii( "inc.*" ));
			aEntry += DirEntry( sWildcard );

			Dir aDir( aEntry, FSYS_KIND_DIR );
			for ( sal_uInt16 i = 0; i < aDir.Count(); i++ ) {
				ByteString sInc( aDir[ i ].GetName(), RTL_TEXTENCODING_ASCII_US );
				if ( sInc.GetTokenCount( '.' ) > 1 ) {
					if ( !pList )
						pList = new SByteStringList();
					pList->PutString( new ByteString( sInc.GetToken( 1, '.' )));
				}
			}
		}
	}
	return pList;
}

/*****************************************************************************/
String ProductMapper::GetVersionRoot(
	GenericInformationList *pList, const ByteString &rVersion )
/*****************************************************************************/
{
	ByteString sKey( rVersion );
	GenericInformation *pVersion = pList->GetInfo( sKey );
	if ( pVersion ) {
#ifdef UNX
		sKey = "drives/o:/unixvolume";
		GenericInformation *pUnixVolume = pVersion->GetSubInfo( sKey, sal_True );
		ByteString sPath;
		if ( pUnixVolume )
			sPath = pUnixVolume->GetValue();
		sPath += "/";
#else
		ByteString sPath( "o:\\" );
#endif
		sKey = "settings/path";
		GenericInformation *pPath = pVersion->GetSubInfo( sKey, sal_True );
		if ( pPath ) {
			sPath += pPath->GetValue().GetToken( 0, '\\' );
			sPath += "/";
		}
#ifdef UNX
		sPath.SearchAndReplaceAll( "\\", "/" );
		while( sPath.SearchAndReplace( "//", "/" ) != STRING_NOTFOUND ) {};
#else
		sPath.SearchAndReplaceAll( "/", "\\" );
		while( sPath.SearchAndReplace( "\\\\", "\\" ) != STRING_NOTFOUND ) {};
#endif

		return String( sPath, RTL_TEXTENCODING_ASCII_US );
	}
	return String();
}

/*****************************************************************************/
BaseProductList *ProductMapper::GetBases(
	GenericInformation *pProductInfo, sal_uInt16 nLevel,
	BaseProductList *pBases )
/*****************************************************************************/
{
	if ( !pBases )
		pBases = new BaseProductList();

	if ( pProductInfo ) {
		ByteString sCandidate( *pProductInfo );
		sCandidate += " (";
		sCandidate += pProductInfo->GetValue();
		sCandidate += ")";

		ByteString sKey( "BASEDON" );
		GenericInformation *pBasedOn = pProductInfo->GetSubInfo( sKey );
		if ( pBasedOn ) {
			GenericInformationList *pBasesInfo = pBasedOn->GetSubList();
			if ( pBasesInfo ) {
				for ( sal_uIntPtr k = 0; k < pBasesInfo->Count(); k++ ) {
					GenericInformation *pBaseProduct;
					if ( GetProductInformation( *pBasesInfo->GetObject( k ), pBaseProduct ) == PRODUCT_MAPPER_OK )
						GetBases( pBaseProduct, ++ nLevel, pBases );
				}
			}
		}
		sal_Bool bFound = sal_False;
		ByteString sUpperCandidate( sCandidate );
		sUpperCandidate.ToUpperAscii();
		for ( sal_uInt16 i = 0; i < pBases->Count() && !bFound; i++ ) {
			ByteString sTest( *pBases->GetObject( i ));
			if ( sTest.ToUpperAscii() == sUpperCandidate )
				bFound = sal_True;
		}
		if ( !bFound )
			pBases->Insert( new ByteString( sCandidate ), ( sal_uIntPtr ) 0 );
	}
	return pBases;
}

/*****************************************************************************/
sal_uInt16 ProductMapper::PrintMinorList(
	const ByteString rProduct, const ByteString rEnvironment )
/*****************************************************************************/
{
	if ( !pVersionList )
		return PRODUCT_MAPPER_NO_VERSION_INFORMATION;

	if ( !pProductList || !pProductList->Count())
		return PRODUCT_MAPPER_NO_PRODUCT;

	GenericInformation *pProductInfo;
	GetProductInformation( rProduct, pProductInfo );
	if ( !pProductInfo )
		return PRODUCT_MAPPER_NO_PRODUCT;

	BaseProductList *pBases = GetBases( pProductInfo );
	if ( pBases->Count()) {
		if ( pBases->Count() > 1 )
			fprintf( stdout, "Product \"%s\" based on ", pBases->GetObject(( sal_uIntPtr ) 0 )->GetBuffer());
		else
			fprintf( stdout, "Product \"%s\" based on no other products", pBases->GetObject(( sal_uIntPtr ) 0 )->GetBuffer());

		for ( sal_uIntPtr i = 1; i < pBases->Count(); i++ ) {
			fprintf( stdout, "\"%s\"", pBases->GetObject( i )->GetBuffer());
			if ( i < pBases->Count() - 1 )
				fprintf( stdout, ", " );
		}
		fprintf( stdout, "\n\n" );
	}
	sal_uInt16 nResult = PRODUCT_MAPPER_OK;

	if ( rEnvironment.Len())
		nResult = PrintSingleMinorList( pProductInfo, pBases, rEnvironment );
	else {
		ByteString sEnvKey( pProductInfo->GetValue());
		sEnvKey += "/Environments";

		GenericInformation *pEnvironmentInfo = pVersionList->GetInfo( sEnvKey, sal_True );
		if ( pEnvironmentInfo ) {
			GenericInformationList *pEnvironmentList = pEnvironmentInfo->GetSubList();
			if ( pEnvironmentList ) {
				for ( sal_uIntPtr i = 0; i < pEnvironmentList->Count(); i++ ) {
					sal_uInt16 nTmp = PrintSingleMinorList( pProductInfo, pBases, *pEnvironmentList->GetObject( i ));
					if ( nTmp != PRODUCT_MAPPER_OK )
						nResult = nTmp;
				}
			}
		}
	}

	for ( sal_uIntPtr m = 0; m < pBases->Count(); m++ )
		delete pBases->GetObject( m );
	delete pBases;

	return nResult;
}

/*****************************************************************************/
sal_uInt16 ProductMapper::PrintSingleMinorList(
	GenericInformation *pProductInfo, BaseProductList *pBases,
	const ByteString rEnvironment )
/*****************************************************************************/
{
	DirEntry aRoot( GetVersionRoot( pVersionList, pProductInfo->GetValue()));
	aRoot += DirEntry( String( rEnvironment, RTL_TEXTENCODING_ASCII_US ));
	if ( !aRoot.Exists())
		return PRODUCT_MAPPER_OK;

	SByteStringList *pMinors = GetMinorList( pProductInfo->GetValue(), rEnvironment );
	if ( !pMinors )
		pMinors = new SByteStringList();
	pMinors->Insert( new ByteString( "" ), LIST_APPEND );

	SByteStringList aOutputList;
	sal_Bool bUnknownMinor = sal_False;
	for ( sal_uIntPtr i = 0; i < pMinors->Count(); i++ ) {
		ByteString sOutput;
		ByteString sProductVersion;

		for ( sal_uIntPtr j = 0; j < pBases->Count(); j++ ) {
			ByteString sCurProduct( *pBases->GetObject( j ));
			ByteString sVersion( sCurProduct.GetToken( sCurProduct.GetTokenCount( '(' ) - 1, '(' ).GetToken( 0, ')' ));
			if ( !j )
				sProductVersion = sVersion;

			MinorMk *pMinorMk = new MinorMk(
				pVersionList, sProductVersion, sVersion, rEnvironment, *pMinors->GetObject( i ));

			ByteString sMinor( pMinorMk->GetLastMinor().GetBuffer());
			if ( !sMinor.Len()) {
				sMinor = "!";
				bUnknownMinor = sal_True;
			}
			if ( j == 0 ) {
				sOutput += pMinorMk->GetBuildNr();
				sOutput += " ";

				if ( i == pMinors->Count() - 1 )
					sOutput += "flat: ";
				else
					sOutput += "      ";
			}
			sOutput += sVersion;
			sOutput += ".";
			sOutput += sMinor;
			sOutput += "(";
			sOutput += pMinorMk->GetBuildNr();
			sOutput += ")  ";
		}
		aOutputList.PutString( new ByteString( sOutput ));
	}
	ByteString sOldMinor;

	if ( aOutputList.Count())
		fprintf( stdout, "Available builds on %s:\n", rEnvironment.GetBuffer());

	for ( sal_uIntPtr o = 0; o < aOutputList.Count(); o++ ) {
		ByteString sOutput( *aOutputList.GetObject( o ));
		sOutput = sOutput.Copy( sOutput.GetToken( 0, ' ' ).Len() + 1 );

		ByteString sCurMinor( sOutput.GetToken( 1, '.' ).GetToken( 0, '(' ));
		if ( sOldMinor.Len() && sCurMinor < sOldMinor ) {
			fprintf( stdout, "      ----------\n" );
		}
		sOldMinor = sCurMinor;

		fprintf( stdout, "%s\n", sOutput.GetBuffer());
		delete aOutputList.GetObject( o );
	}
	if ( bUnknownMinor )
		fprintf( stdout, "Symbol ! indcates that at least one minor could not be found\n\n" );
	else if ( aOutputList.Count())
		fprintf( stdout, "\n" );

	for ( sal_uIntPtr l = 0; l < pMinors->Count(); l++ )
		delete pMinors->GetObject( l );
	delete pMinors;

	return PRODUCT_MAPPER_OK;
}




