 /*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: prodmap.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: vg $ $Date: 2007-10-22 14:42:22 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#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 ( ULONG i = 0; i < pVersionList->Count(); i++ ) {
            GenericInformation *pVersion = pVersionList->GetObject( i );

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

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

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

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

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

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

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

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

/*****************************************************************************/
USHORT 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, TRUE );

    if ( !pProductInfo )
        return PRODUCT_MAPPER_NO_PRODUCT;

    return PRODUCT_MAPPER_OK;
}

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

    USHORT nReturn = GetProductInformation( rProduct, pProductInfo );

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

        for ( ULONG j = 0; j < pProductList->Count(); j++ ) {
            GenericInformation *pCandidate = pProductList->GetObject( j );
            ByteString sKey( "DEPENDSON/" );
            sKey += rProduct;
            GenericInformation *pDependsOn = pCandidate->GetSubInfo( sKey, 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 ( ULONG k = 0; k < pBases->Count(); k++ ) {
                        aBaseList.PutString( new ByteString( *pBases->GetObject( k )));
                    }
                }
            }
        }
    }

    return nReturn;
}

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

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

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

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

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

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

    if ( pProductList->Count()) {
        for ( ULONG 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 ( USHORT 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, TRUE );
        ByteString sPath;
        if ( pUnixVolume )
            sPath = pUnixVolume->GetValue();
        sPath += "/";
#else
        ByteString sPath( "o:\\" );
#endif
        sKey = "settings/path";
        GenericInformation *pPath = pVersion->GetSubInfo( sKey, 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, USHORT 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 ( ULONG k = 0; k < pBasesInfo->Count(); k++ ) {
                    GenericInformation *pBaseProduct;
                    if ( GetProductInformation( *pBasesInfo->GetObject( k ), pBaseProduct ) == PRODUCT_MAPPER_OK )
                        GetBases( pBaseProduct, ++ nLevel, pBases );
                }
            }
        }
        BOOL bFound = FALSE;
        ByteString sUpperCandidate( sCandidate );
        sUpperCandidate.ToUpperAscii();
        for ( USHORT i = 0; i < pBases->Count() && !bFound; i++ ) {
            ByteString sTest( *pBases->GetObject( i ));
            if ( sTest.ToUpperAscii() == sUpperCandidate )
                bFound = TRUE;
        }
        if ( !bFound )
            pBases->Insert( new ByteString( sCandidate ), ( ULONG ) 0 );
    }
    return pBases;
}

/*****************************************************************************/
USHORT 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(( ULONG ) 0 )->GetBuffer());
        else
            fprintf( stdout, "Product \"%s\" based on no other products", pBases->GetObject(( ULONG ) 0 )->GetBuffer());

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

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

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

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

    return nResult;
}

/*****************************************************************************/
USHORT 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;
    BOOL bUnknownMinor = FALSE;
    for ( ULONG i = 0; i < pMinors->Count(); i++ ) {
        ByteString sOutput;
        ByteString sProductVersion;

        for ( ULONG 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 = 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 ( ULONG 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 ( ULONG l = 0; l < pMinors->Count(); l++ )
        delete pMinors->GetObject( l );
    delete pMinors;

    return PRODUCT_MAPPER_OK;
}




