//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1991 - 2000.
//
//  File:       VMap.hxx
//
//  Contents:   Virtual <--> physical path map.
//
//  Classes:    CVMap
//
//  History:    05-Feb-96   KyleP       Created
//
//----------------------------------------------------------------------------

#pragma once

#include <catalog.hxx>
#include <prcstob.hxx>
#include <smatch.hxx>
#include <cimbmgr.hxx>

const unsigned INVALID_VMAP_INDEX = 0xFFFFFFFF;

//+-------------------------------------------------------------------------
//
//  Class:      CVMapDesc
//
//  Purpose:    Description of single scope mapping
//
//  History:    05-Feb-96   KyleP       Created
//
//--------------------------------------------------------------------------

class CVMapDesc
{
public:

    inline CVMapDesc();

    void Init( WCHAR const * pVRoot,
               ULONG ccVRoot,
               WCHAR const * pPRoot,
               ULONG ccPRoot,
               ULONG idParent,
               BOOL fAutomatic,
               CiVRootTypeEnum eType,
               BOOL fIsIndexed,
               BOOL fNonIndexedVDir );

    inline unsigned PhysicalMatchLen( WCHAR const * path ) const;
    inline BOOL     IsInPhysicalScope( WCHAR const * root, unsigned cc ) const;

    BOOL IsVirtualMatch( WCHAR const * pVPath, unsigned ccVPath ) const;
    BOOL IsPhysicalMatch( WCHAR const * pVPath, unsigned ccVPath ) const;

    inline ULONG Parent() const              { return _idParent; }
    inline void  SetParent( ULONG idParent ) { _idParent = idParent; }

    inline BOOL  IsFree()                    { return (0 == _ccVScope); }
    inline void  Delete()                    { _ccVScope = 0; }

    inline WCHAR const * PhysicalPath() const { return _wcPScope; }
    inline ULONG PhysicalLength() const       { return _ccPScope; }

    inline WCHAR const * VirtualPath() const  { return _wcVScope; }
    inline ULONG VirtualLength() const        { return _ccVScope; }

    inline void SetAutomatic()                { _Type |= PCatalog::AutomaticRoot; }
    inline void ClearAutomatic()              { _Type &= ~PCatalog::AutomaticRoot; }
    inline BOOL IsAutomatic() const           { return (_Type & PCatalog::AutomaticRoot); }

    inline void SetManual()                   { _Type |= PCatalog::ManualRoot; }
    inline void ClearManual()                 { _Type &= ~PCatalog::ManualRoot; }
    inline BOOL IsManual() const              { return (_Type & PCatalog::ManualRoot); }

    inline void ClearInUse()                  { _Type &= ~PCatalog::UsedRoot; }
    inline BOOL IsInUse() const               { return (_Type & PCatalog::UsedRoot); }

    inline BOOL IsNNTP() const                { return 0 != (_Type & PCatalog::NNTPRoot); }
    inline BOOL IsIMAP() const                { return 0 != (_Type & PCatalog::IMAPRoot); }
    inline BOOL IsW3() const                  { return !IsNNTP() && !IsIMAP(); }

    inline BOOL IsNonIndexedVDir() const      { return 0 != (_Type & PCatalog::NonIndexedVDir); }
    inline void SetNonIndexedVDir()           { _Type |= PCatalog::NonIndexedVDir; }
    inline void ClearNonIndexedVDir()         { _Type &= ~PCatalog::NonIndexedVDir; }

    inline ULONG RootType()                   { return _Type; }

private:

    ULONG _ccVScope;
    WCHAR _wcVScope[MAX_PATH];       // Virtual scope

    // IIS currently doesn't allow paths greater
    // than MAX_PATH. This is not a good reason for we to also not
    // support, but there are lot of complications right now, like 
    // in place serialization of variable data, so currently we have the
    // MAX_PATH restriction of VPaths and for scopes.

    ULONG _ccPScope;
    WCHAR _wcPScope[MAX_PATH];       // Physical scope

    ULONG _idParent;                 // Link to parent (INVALID_VMAP_INDEX --> top level)

    ULONG _Type;
};

//+-------------------------------------------------------------------------
//
//  Class:      CVMap
//
//  Purpose:    Virtual/Physical path map
//
//  History:    05-Feb-96   KyleP       Created
//
//--------------------------------------------------------------------------

class CVMap
{
public:

    CVMap();

    BOOL Init( PRcovStorageObj * obj );
    void Empty();

    BOOL Add( WCHAR const * vroot,
              WCHAR const * root,
              BOOL fAutomatic,
              ULONG & idNew,
              CiVRootTypeEnum eType,
              BOOL fVRoot,
              BOOL fIsIndexed );

    BOOL Remove( WCHAR const * vroot,
                 BOOL fOnlyIfAutomatic,
                 ULONG & idOld,
                 ULONG & idNew,
                 CiVRootTypeEnum eType,
                 BOOL fVRoot );

    void MarkClean();
    BOOL IsClean() const { return !_fDirty; }

    inline BOOL IsInPhysicalScope( ULONG id, WCHAR const * root, unsigned cc );

    ULONG PhysicalPathToId( WCHAR const * path );

    BOOL VirtualToPhysicalRoot( WCHAR const * pwcVRoot,
                                unsigned ccVRoot,
                                CLowerFunnyPath & lcaseFunnyPRoot,
                                unsigned & ccPRoot );

    BOOL VirtualToPhysicalRoot( WCHAR const * pwcVPath,
                                unsigned ccVPath,
                                XGrowable<WCHAR> & xwcsVRoot,
                                unsigned & ccVRoot,
                                CLowerFunnyPath & lcaseFunnyPRoot,
                                unsigned & ccPRoot,
                                unsigned & iBmk );
                                
    BOOL VirtualToAllPhysicalRoots( WCHAR const * pwcVPath,
                                    unsigned ccVPath,
                                    XGrowable<WCHAR> & xwcsVRoot,
                                    unsigned & ccVRoot,
                                    CLowerFunnyPath & lcaseFunnyPRoot,
                                    unsigned & ccPRoot,
                                    ULONG & ulType,
                                    unsigned & iBmk );
                                    
    ULONG EnumerateRoot( XGrowable<WCHAR> & xwcVRoot,
                         unsigned & ccVRoot,
                         CLowerFunnyPath & lcaseFunnyPRoot,
                         unsigned & ccPRoot,
                         unsigned & iBmk );

    BOOL DoesPhysicalRootExist( WCHAR const * pwcPRoot );

    inline CVMapDesc const & GetDesc( ULONG id ) const;

    //
    // Path chaining.
    //

    inline ULONG Parent( ULONG id ) const;

    inline ULONG FindNthRemoved( ULONG id, unsigned cSkip ) const;

    BOOL IsNonIndexedVDir( ULONG id ) const
    {
        if ( INVALID_VMAP_INDEX == id )
            return FALSE;

        return _aMap[id].IsNonIndexedVDir();
    }

    ULONG GetExcludeParent( ULONG id ) const
    {
        Win4Assert( id < _aExcludeParent.Count() );
        return _aExcludeParent[ id ];
    }

    BOOL IsAnExcludeParent( ULONG id ) const
    {
        if ( INVALID_VMAP_INDEX == id )
            return FALSE;

        for ( unsigned x = 0; x < _aExcludeParent.Count(); x++ )
            if ( _aExcludeParent[x] == id )
                return TRUE;

        return FALSE;
    }

private:

    ULONG PhysicalPathToId( WCHAR const * path, BOOL fNonIndexedVDirs );

    void DumpVMap();
    void RecomputeNonIndexedInfo();

    BOOL                        _fDirty;     // TRUE if vmap went down dirty

    CMutexSem                   _mutex;

    CDynArrayInPlace<CVMapDesc> _aMap;

    CDynArrayInPlace<ULONG>     _aExcludeParent;

    XPtr<PRcovStorageObj>       _xrsoMap;
};

//+-------------------------------------------------------------------------
//
//  Member:     CVMapDesc::PhysicalMatchLen, private
//
//  Synopsis:   Compares input with physical path
//
//  Arguments:  [pPPath] -- Physical path.
//
//  Returns:    0 if [pPPath] does not match physical path complete to
//              the end of the short of the two.  On success returns
//              length of physical path.
//
//  History:    07-Feb-96   KyleP       Created.
//
//--------------------------------------------------------------------------

inline unsigned CVMapDesc::PhysicalMatchLen( WCHAR const * pPPath ) const
{
    Win4Assert( *pPPath );

    //
    // Compare strings.
    //

    unsigned i = 0;

    while ( *pPPath && i < _ccPScope && *pPPath == _wcPScope[i] )
    {
        i++;
        pPPath++;
    }

    //
    // Adjust for possible lack of terminating backslash.
    //

    if ( 0 == *pPPath )
    {
        if ( *(pPPath - 1) == L'\\' )
            return _ccPScope;
        else
        {
            if ( _wcPScope[i] == L'\\' )
                return _ccPScope;
            else
                return 0;
        }
    }
    else
    {
        if ( i >= _ccPScope )
            return _ccPScope;
        else
            return 0;
    }
}

//+-------------------------------------------------------------------------
//
//  Member:     CVMapDesc::CVMapDesc, private
//
//  Synopsis:   Constructor
//
//  History:    07-Feb-96   KyleP       Created.
//
//--------------------------------------------------------------------------

inline CVMapDesc::CVMapDesc()
        : _ccVScope( 0 ),
          _ccPScope( 0 )
{
}

//+-------------------------------------------------------------------------
//
//  Member:     CVMapDesc::IsInPhysicalScope, public
//
//  Synopsis:   Compares input with physical path
//
//  Arguments:  [root] -- Physical path.
//              [cc]   -- Size, in chars, of [root]
//
//  Returns:    TRUE if [root] is within physical scope.
//
//  History:    07-Feb-96   KyleP       Created.
//
//--------------------------------------------------------------------------

inline BOOL CVMapDesc::IsInPhysicalScope( WCHAR const * root, unsigned cc ) const
{
    Win4Assert( root[cc-1] != L'\\' );

    CScopeMatch Match( _wcPScope, _ccPScope );

    //
    // Be careful not to match root of scope.
    //

    return Match.IsInScope( root, cc ) && (_ccPScope != cc + 1);
}

//+-------------------------------------------------------------------------
//
//  Member:     CVMap::IsInPhysicalScope, public
//
//  Synopsis:   Compares input with physical path
//
//  Arguments:  [id]   -- Id of virtual/physical scope
//              [root] -- Physical path.
//              [cc]   -- Size, in chars, of [root]
//
//  Returns:    TRUE if [root] is within physical scope.
//
//  History:    07-Feb-96   KyleP       Created.
//
//--------------------------------------------------------------------------

inline BOOL CVMap::IsInPhysicalScope( ULONG id, WCHAR const * root, unsigned cc )
{
    CLock   lock(_mutex);

    Win4Assert( id < _aMap.Count() );

    return _aMap[id].IsInPhysicalScope( root, cc );
}

//+-------------------------------------------------------------------------
//
//  Member:     CVMap::Parent, public
//
//  Arguments:  [id]   -- Id of virtual/physical scope
//
//  Returns:    Virtual/physical id of parent.
//
//  History:    07-Feb-96   KyleP       Created.
//
//--------------------------------------------------------------------------

inline ULONG CVMap::Parent( ULONG id ) const
{
    Win4Assert( id < _aMap.Count() );

    return _aMap[id].Parent();
}

//+-------------------------------------------------------------------------
//
//  Member:     CVMap::FindNthRemoved, public
//
//  Synopsis:   Walks up virtual root chain to the Nth link.
//
//  Arguments:  [id]    -- Id of starting virtual/physical scope
//              [cSkip] -- Number of links to skip
//
//  Returns:    Virtual/physical id of Nth parent.
//
//  History:    07-Feb-96   KyleP       Created.
//
//--------------------------------------------------------------------------

inline ULONG CVMap::FindNthRemoved( ULONG id, unsigned cSkip ) const
{
    Win4Assert( id == INVALID_VMAP_INDEX || id < _aMap.Count() );

    while ( id != INVALID_VMAP_INDEX && !_aMap[id].IsFree() && cSkip > 0 )
    {
        id = _aMap[id].Parent();

        cSkip--;
    }

    if ( INVALID_VMAP_INDEX != id && _aMap[id].IsFree() )
        id = INVALID_VMAP_INDEX;

    return id;
}

//+-------------------------------------------------------------------------
//
//  Member:     CVMap::GetDesc, public
//
//  Arguments:  [id]    -- Id of virtual/physical scope
//
//  Returns:    Scope descriptor for [id]
//
//  History:    07-Feb-96   KyleP       Created.
//
//--------------------------------------------------------------------------

inline CVMapDesc const & CVMap::GetDesc( ULONG id ) const
{
    Win4Assert( id < _aMap.Count() );

    return _aMap[id];
}