//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
// 
// This program is free software.  The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
// 
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please see the GNU General Public License
// for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
// 
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
// 
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
///////////////////////////////////////////////////////////////////////////////
// fspropdisplayer.cpp
//

#include "stdfs.h"

//=========================================================================
// INCLUDES
//=========================================================================

#include "fspropdisplayer.h"
#include "fspropset.h"
#include "core/fsservices.h"
#include "core/serializer.h"
#include "core/timeconvert.h"
#include "fco/fconame.h"
#include "fco/fco.h"
#include "core/twlocale.h"
#include "core/displayencoder.h"
#include "fco/fcoundefprop.h"
#include "core/errorutil.h"

//=========================================================================
// STANDARD LIBRARY INCLUDES
//=========================================================================

#include <iomanip>

//=========================================================================
// DEFINES AND MACROS
//=========================================================================

//=========================================================================
// OTHER DIRECTIVES
//=========================================================================

#if !IS_AIX
// For some stupid linker reason, this object does not get constructed under
// AIX. The cheap fix is to move this call to fs.cpp.
IMPLEMENT_TYPEDSERIALIZABLE( cFSPropDisplayer, _T("FSPropDisplayer"), 0, 1 );
#endif

//=========================================================================
// GLOBALS
//=========================================================================

//=========================================================================
// UTIL FUNCTION PROTOTYES
//=========================================================================

//=========================================================================
// METHOD CODE
//=========================================================================

cFSPropDisplayer::cFSPropDisplayer() 
    : mbLazy( false ) 
{
    mpvPropsWeDisplay.AddItemAndGrow( cFSPropSet::PROP_UID );
    mpvPropsWeDisplay.AddItemAndGrow( cFSPropSet::PROP_GID );
    mpvPropsWeDisplay.AddItemAndGrow( cFSPropSet::PROP_ATIME );
    mpvPropsWeDisplay.AddItemAndGrow( cFSPropSet::PROP_MTIME );
    mpvPropsWeDisplay.AddItemAndGrow( cFSPropSet::PROP_CTIME );
    mpvPropsWeDisplay.AddItemAndGrow( cFSPropSet::PROP_MODE );

    mpvPropsWeDisplay.AddItemAndGrow( cFSPropSet::PROP_BLOCK_SIZE );
    mpvPropsWeDisplay.AddItemAndGrow( cFSPropSet::PROP_BLOCKS );
    mpvPropsWeDisplay.AddItemAndGrow( cFSPropSet::PROP_SIZE );
    mpvPropsWeDisplay.AddItemAndGrow( cFSPropSet::PROP_NLINK );
}

iFCOPropDisplayer* cFSPropDisplayer::Clone() const
{
    iFCOPropDisplayer* pNew = new cFSPropDisplayer;
    pNew->Merge( this );
    return pNew;
}

bool cFSPropDisplayer::IsMultiLineProp( int propID ) const
{
    // no FS props are multiline
    return false;
}

cFSPropDisplayer::~cFSPropDisplayer()
{
	cDebug d("~cFSPropDisplayer");
	d.TraceNever( "Destructor called on object %x\n", (ptr_size_type)this);
}

const cFCOPropVector& cFSPropDisplayer::GetPropsConverted() const
{
    return mpvPropsWeDisplay;
}

void cFSPropDisplayer::SetLazy( const bool bLazy /* = true */ )
{
    mbLazy = bLazy;
}

bool cFSPropDisplayer::GetLazy() const
{
    return mbLazy;
}

// adds all prop display info from rhs to this
void cFSPropDisplayer::Merge( const iFCOPropDisplayer* const ppd )
{
    ASSERT( ppd != 0);

    // should only merge two prop displayers of the same type
    if( ppd->GetType() != this->GetType() )
        ASSERT( false ); 

    const cFSPropDisplayer* const pfspd = static_cast<const cFSPropDisplayer* const >( ppd );

    // merge propvectors
    mpvPropsWeDisplay |= pfspd->GetPropsConverted();
    

    // merge mapping info    
    for( INT64_TO_STRING_MAP::const_iterator iterUID = pfspd->uidToUsername.begin();
         iterUID != pfspd->uidToUsername.end();
         iterUID++
       )
    {
        AddUsernameMapping( iterUID->first, iterUID->second );
    }
         
    for( INT64_TO_STRING_MAP::const_iterator iterGID = pfspd->gidToGroupname.begin();
         iterGID != pfspd->gidToGroupname.end();
         iterGID++
       )
    {
        AddGroupnameMapping( iterGID->first, iterGID->second );
    }

    // keep lazy flag the same
}


TSTRING& cFSPropDisplayer::GetDetailsHeader( TSTRING& strBuf, int iMargin ) const
{
    static cFSPropSet set;
    TOSTRINGSTREAM sstr;

    sstr << std::left;
    sstr << _T(" ") << std::setw( PROP_MODE_WIDTH )     << set.GetPropName( cFSPropSet::PROP_MODE );
    sstr << _T(" ") << std::setw( PROP_OWNER_WIDTH )    << set.GetPropName( cFSPropSet::PROP_UID );
    sstr << _T(" ") << std::setw( PROP_SIZE_WIDTH )     << set.GetPropName( cFSPropSet::PROP_SIZE );
    sstr << _T(" ") << std::setw( PROP_MTIME_WIDTH )    << set.GetPropName( cFSPropSet::PROP_MTIME );
    sstr << _T("\n");
    for( int i = 0; i < iMargin; i++ )
        sstr << _T(" ");
    sstr << _T(" ") << std::setw( PROP_MODE_WIDTH )     << _T("------");
    sstr << _T(" ") << std::setw( PROP_OWNER_WIDTH )    << _T("----------");
    sstr << _T(" ") << std::setw( PROP_SIZE_WIDTH )     << _T("----------");
    sstr << _T(" ") << std::setw( PROP_MTIME_WIDTH )    << _T("----------");

    strBuf = sstr.str();
    return strBuf;
}


////////////////////////////////////////////////////////////////////////
// Function name	: cFSPropDisplayer::GetDetails
// Description	    : takes a TSTRING and fills it with an "ls -l" representation
//                    of the object's permission bits ( e.g. "drwxr-x--x" ), UID,
//                    size, and last access time.  Does not add the name of the FSO
//
// Return type		: TSTRING& -- reference to input buffer
//
// Argument         : TSTRING& strBuf -- buffer.  GetDesription will use it to allocate space.
//
TSTRING& cFSPropDisplayer::GetDetails( const iFCO* const pfco, TSTRING& strBuf ) const 
{
    TOSTRINGSTREAM sstr;    
    const cFCOPropVector pv = pfco->GetPropSet()->GetValidVector();

    sstr << std::left;

    sstr << _T(" ") << std::setw( PROP_MODE_WIDTH );
    if( pv.ContainsItem( cFSPropSet::PROP_MODE ) )
        sstr << PropAsString( pfco,  cFSPropSet::PROP_MODE ).c_str();
    else
        sstr << _T("XXXXXXXXX");
    
    sstr << _T(" ") << std::setw( PROP_OWNER_WIDTH );    
    if( pv.ContainsItem( cFSPropSet::PROP_UID ) )
        sstr << PropAsString( pfco,  cFSPropSet::PROP_UID ).c_str();
    else
        sstr << _T("XXXXXXXXX");

    sstr << _T(" ") << std::setw( PROP_SIZE_WIDTH );    
    if( pv.ContainsItem( cFSPropSet::PROP_SIZE ) )
        sstr << PropAsString( pfco,  cFSPropSet::PROP_SIZE ).c_str();
    else
        sstr << _T("XXX");
    
    sstr << _T(" ") << std::setw( PROP_MTIME_WIDTH );    
    if( pv.ContainsItem( cFSPropSet::PROP_MTIME ) )
        sstr << PropAsString( pfco,  cFSPropSet::PROP_MTIME ).c_str();
    else
        sstr << _T("XXXXXXXXXXXXXXXXX");

    strBuf = sstr.str();
    return strBuf;
}



void cFSPropDisplayer::InitForFCO( const iFCO* const ifco )
{
    ASSERT( ifco != 0);

    cFCOPropVector v = ifco->GetPropSet()->GetValidVector();
    int vSize = v.GetSize();
    for( int i = 0; i < vSize; i++ )
    {
        if( v.ContainsItem( i ) )
        {            
            InitForProp( ifco, i );
        }
    }
}


void cFSPropDisplayer::InitForProp( const iFCO* const pFCO, const int propIdx)
{
    ASSERT( pFCO != 0);

    switch( propIdx )
    {
        case cFSPropSet::PROP_UID:
        {
            const int64& i64UID = static_cast<const cFCOPropInt64* const>( pFCO->GetPropSet()->GetPropAt( cFSPropSet::PROP_UID ) )->GetValue();

            // check if prop is in table.  if it is, then don't hit the FS            
            TSTRING tstrDummy;
            if( ! GetUsername( i64UID, tstrDummy ) )
            {
                TSTRING tstrUsername;
                if( iFSServices::GetInstance()->GetOwnerForFile( pFCO->GetName().AsString(), tstrUsername ) )
                    AddUsernameMapping ( i64UID, tstrUsername  ); 
                else                    
                    AddUsernameMapping ( i64UID, _T("") ); // on failure, enter error value into mapping so we don't search for this value again.
            }
        }
        break;
        case cFSPropSet::PROP_GID:
        {
            const int64& i64GID = static_cast<const cFCOPropInt64* const>( pFCO->GetPropSet()->GetPropAt( cFSPropSet::PROP_GID ) )->GetValue();

            // check if prop is in table.  if it is, then don't hit the FS            
            TSTRING tstrDummy;
            if( ! GetGroupname( i64GID, tstrDummy ) )
            {
                TSTRING tstrGroupname;
                if( iFSServices::GetInstance()->GetGroupForFile( pFCO->GetName().AsString(), tstrGroupname ) )
                    AddGroupnameMapping( i64GID, tstrGroupname ); 
                else                    
                    AddGroupnameMapping( i64GID, _T("") );  // on failure, enter error value into mapping so we don't search for this value again.
            }
        }
        break;
        default:
            // right now nothing else needs to be done for the other props
        break;
    }
}

TSTRING cFSPropDisplayer::PropAsString( const iFCO* const pFCO, const int propIdx, int iOffset, int iWidth )
{
    if( mpvPropsWeDisplay.ContainsItem( propIdx ) && mbLazy )
    {
		InitForProp( pFCO, propIdx );
	}
	// run the const version of PropAsString
	return ((const cFSPropDisplayer*)this)->PropAsString(pFCO, propIdx, iOffset, iWidth);
}

TSTRING cFSPropDisplayer::PropAsString( const iFCO* const pFCO, const int propIdx, int iOffset, int iWidth ) const
{
    TSTRING strProp = _T("");

    ASSERT( pFCO != 0);

    // get prop pointer
    const iFCOProp* pProp = pFCO->GetPropSet()->GetPropAt( propIdx );

    // if we know how to display prop
    if( pProp->GetType() != cFCOUndefinedProp::GetInstance()->GetType() &&
        mpvPropsWeDisplay.ContainsItem( propIdx ) )
    {
        switch( propIdx )
        {
            case cFSPropSet::PROP_BLOCK_SIZE:
            case cFSPropSet::PROP_BLOCKS:
            case cFSPropSet::PROP_SIZE:
            case cFSPropSet::PROP_NLINK:
            {
                const cFCOPropInt64* const pTypedProp = static_cast<const cFCOPropInt64* const>(pProp);
                cTWLocale::FormatNumber( pTypedProp->GetValue(), strProp );
            }
            break;
            case cFSPropSet::PROP_ATIME:
            case cFSPropSet::PROP_MTIME:
            case cFSPropSet::PROP_CTIME:
            {
                const cFCOPropInt64* const pTypedProp = static_cast<const cFCOPropInt64* const>(pProp);
                int64 i64 = pTypedProp->GetValue();
                cTWLocale::FormatTime( i64, strProp );
            }
            break;
            case cFSPropSet::PROP_MODE:
            {
                const cFCOPropUint64* const pTypedProp = static_cast<const cFCOPropUint64* const>(pProp);
                ASSERT( pTypedProp != 0);

                iFSServices::GetInstance()->ConvertModeToString( pTypedProp->GetValue(), strProp );
            }
            break;
            case cFSPropSet::PROP_UID:
            {
                const cFCOPropInt64* const pTypedProp = static_cast<const cFCOPropInt64* const>(pProp);
                ASSERT( pTypedProp != 0);
                if( GetUsername( pTypedProp->GetValue(), strProp ) )
                {
                    TSTRINGSTREAM ostr;
                    ostr << strProp << _T(" (") << (int32) pTypedProp->GetValue() << _T(")");
                    strProp = ostr.str();
                }
                else
                    strProp = pProp->AsString();
            }
            break;
            case cFSPropSet::PROP_GID:                
            {
                const cFCOPropInt64* const pTypedProp = static_cast<const cFCOPropInt64* const>(pProp);
                ASSERT( pTypedProp != 0);
                if( GetGroupname( pTypedProp->GetValue(), strProp ) )
                {
                    TSTRINGSTREAM ostr;
                    ostr << strProp << _T(" (") << (int32) pTypedProp->GetValue() << _T(")");
                    strProp = ostr.str();
                }
                else
                    strProp = pProp->AsString();
            }
            break;
            default:
                ASSERT( false ); // mpvPropsWeDisplay should contain one of the above
            break;
        }
    }
    else // we don't know how to display prop, so let prop display itself
    {
        strProp = pProp->AsString();
    }

    return( strProp );
}

void cFSPropDisplayer::Write( iSerializer* pSerializer ) const
{
	mpvPropsWeDisplay.Write( pSerializer );
    pSerializer->WriteInt32( mbLazy ? 1 : 0 );
    

    //  stuff
    {
        // write UID mapping
        pSerializer->WriteInt32( uidToUsername.size() );
        for( INT64_TO_STRING_MAP::const_iterator iterUid = uidToUsername.begin(); iterUid != uidToUsername.end(); iterUid++ )
        {
            pSerializer->WriteInt64( iterUid->first );
            pSerializer->WriteString( iterUid->second );
        }
    
        // write GID mapping
        pSerializer->WriteInt32( gidToGroupname.size() );
        for( INT64_TO_STRING_MAP::const_iterator iterGid = gidToGroupname.begin(); iterGid != gidToGroupname.end(); iterGid++ )
        {
            pSerializer->WriteInt64( iterGid->first );
            pSerializer->WriteString( iterGid->second );
        }
    }
}

void cFSPropDisplayer::Read(iSerializer* pSerializer, int32 version)
{
	if (version > Version())
		ThrowAndAssert(eSerializerVersionMismatch(_T("Property Displayer Read")));
    
    mpvPropsWeDisplay.Read( pSerializer );

    int32 iDummy;
    pSerializer->ReadInt32( iDummy );
    mbLazy = iDummy == 0 ? false : true;

    //  stuff
    {
        int32 nValues;
        int64 key;
        TSTRING val;
	    int i;

        // read UID hashtable
        pSerializer->ReadInt32( nValues );
        for(  i = 0; i < nValues; i++ )
        {
            pSerializer->ReadInt64( key );
            pSerializer->ReadString( val );
            AddUsernameMapping( key, val );
        }
    
        // read GID hashtable
        pSerializer->ReadInt32( nValues );
        for( i = 0; i < nValues; i++ )
        {
            pSerializer->ReadInt64( key );
            pSerializer->ReadString( val );
            AddGroupnameMapping( key, val );
        }
    }
}





//////////////////////////////////////////////
// Lookup functions
//////////////////////////////////////////////

bool cFSPropDisplayer::GetUsername( const int64& i64uid, TSTRING& tstrUsername ) const
{    
    bool fFound = false;

    INT64_TO_STRING_MAP::const_iterator iter = uidToUsername.find( i64uid );

    if( iter != uidToUsername.end() )
    {
        fFound = true;
        tstrUsername = iter->second;
    }

    return( fFound && !tstrUsername.empty() );
}

bool cFSPropDisplayer::GetGroupname( const int64& i64gid, TSTRING& tstrGroupname ) const
{    
    bool fFound = false;

    INT64_TO_STRING_MAP::const_iterator iter = gidToGroupname.find( i64gid );

    if( iter != gidToGroupname.end() )
    {
        fFound = true;
        tstrGroupname = iter->second;
    }

    return( fFound && !tstrGroupname.empty() );
}

//////////////////////////////////////////////
// Addition functions
//////////////////////////////////////////////

bool cFSPropDisplayer::AddUsernameMapping( const int64& i64uid, const TSTRING& tstrUsername )
{
    std::pair<INT64_TO_STRING_MAP::iterator, bool> ret = uidToUsername.insert( INT64_TO_STRING_MAP::value_type( i64uid, tstrUsername ) );
    return( ret.second = false ); // returns true if key didn't exist before
}

bool cFSPropDisplayer::AddGroupnameMapping( const int64& i64gid, const TSTRING& tstrGroupname )
{
    std::pair<INT64_TO_STRING_MAP::iterator, bool> ret = gidToGroupname.insert( INT64_TO_STRING_MAP::value_type( i64gid, tstrGroupname ) );
    return( ret.second = false ); // returns true if key didn't exist before
}


