//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 2000.
//
//  File:       pidmap.cxx
//
//  Contents:   Maps pid <--> property name.
//
//  History:    31-Jan-93 KyleP     Created
//
//--------------------------------------------------------------------------

#include <pch.cxx>
#pragma hdrstop

#include <pidremap.hxx>
#include <coldesc.hxx>

//+-------------------------------------------------------------------------
//
//  Member:     CPidRemapper::CPidRemapper, public
//
//  Synopsis:   Creates a prop id remapper.  Translates input arguments
//              'fake' propids to real propids.
//
//  Arguments:  [pidmap]      - input prop ID mapping array
//              [xPropMapper] - property mapper for real pid lookup
//              [prst]        - optional restriction, pids will be mapped
//              [pcol]        - optional output columns, pids will be mapped
//              [pso]         - optional sort specification, pids will be mapped
//
//  History:    31-Jan-93 KyleP     Created
//
//--------------------------------------------------------------------------

CPidRemapper::CPidRemapper( const CPidMapper & pidmap,
                            XInterface<IPropertyMapper> & xPropMapper,
                            CRestriction * prst,
                            CColumnSet * pcol,
                            CSortSet * pso )
        : _xPropMapper( xPropMapper.Acquire() ),
          _fAnyStatProps( FALSE ),
          _fContentProp ( FALSE ),
          _fRankVectorProp( FALSE ),
          _cRefs( 1 )
{
    _cpidReal = pidmap.Count();
    _xaPidReal.Set( _cpidReal, new PROPID[ _cpidReal ] );

    //
    // Iterate through the list
    //

    for ( unsigned i = 0; i < _cpidReal; i++ )
    {
        PROPID pid;
        FULLPROPSPEC const * pFullPropSpec =  pidmap.Get(i)->CastToStruct();
        SCODE sc = _xPropMapper->PropertyToPropid( pFullPropSpec, TRUE, &pid );
        if ( FAILED( sc ) )
            THROW( CException( sc ) );

        _xaPidReal[i] = pid;

        if ( IsUserDefinedPid( _xaPidReal[i] ) )
        {
            //
            // Any user-defined property is a content property.
            // Note that the document characterization is a user-defined prop.
            //
            _fContentProp = TRUE;
        }
        else
        {
            if ( _xaPidReal[i] == pidContents )
                _fContentProp = TRUE;
            else
                _fAnyStatProps = TRUE;

            if ( _xaPidReal[i] == pidRankVector )
                _fRankVectorProp = TRUE;
        }

#if CIDBG == 1
        if ( vqInfoLevel & DEB_ITRACE )
        {
            CFullPropSpec const & ps = *pidmap.Get(i);

            GUID const & guid = ps.GetPropSet();

            char szGuid[50];

            sprintf( szGuid,
                     "%08lX-%04X-%04X-%02X%02X%02X%02X%02X%02X%02X%02X\\",
                     guid.Data1,
                     guid.Data2,
                     guid.Data3,
                     guid.Data4[0], guid.Data4[1],
                     guid.Data4[2], guid.Data4[3],
                     guid.Data4[4], guid.Data4[5],
                     guid.Data4[6], guid.Data4[7] );

            vqDebugOut(( DEB_ITRACE, szGuid ));

            if ( ps.IsPropertyName() )
                vqDebugOut(( DEB_ITRACE | DEB_NOCOMPNAME,
                             "%ws ",
                             ps.GetPropertyName() ));
            else
                vqDebugOut(( DEB_ITRACE | DEB_NOCOMPNAME,
                             "0x%x ",
                             ps.GetPropertyPropid() ));

            vqDebugOut(( DEB_ITRACE | DEB_NOCOMPNAME, " --> pid 0x%x\n",
                         _xaPidReal[i] ));

        }
#endif // CIDBG == 1

        CFullPropSpec * ppsFull = new CFullPropSpec( *pidmap.Get(i) );

        XPtr<CFullPropSpec> xpps(ppsFull);

        if ( xpps.IsNull() || !xpps->IsValid() )
        {
            THROW( CException( STATUS_NO_MEMORY ) );
        }

        _propNames.Add( ppsFull, i );
        xpps.Acquire();

        Win4Assert( _xaPidReal[i] != pidInvalid );
    }

    if ( prst )
        RemapPropid( prst );

    if ( pcol )
        RemapPropid( pcol );

    if ( pso )
        RemapPropid( pso );
}


//+-------------------------------------------------------------------------
//
//  Member:     CPidRemapper::CPidRemapper, public
//
//  Synopsis:   Creates a prop id remapper.
//
//  Arguments:  [xPropMapper] - Property mapper for real pid lookup
//
//  History:    12-Mar-95   DwightKr    Created
//
//--------------------------------------------------------------------------
CPidRemapper::CPidRemapper( XInterface<IPropertyMapper> & xPropMapper )
        : _xPropMapper( xPropMapper.Acquire() ),
          _cpidReal( 0 ),
          _cRefs( 1 )
{
}


//+-------------------------------------------------------------------------
//
//  Member:     CPidRemapper::~CPidRemapper, public
//
//  History:    15-Feb-93 KyleP     Created
//
//--------------------------------------------------------------------------

CPidRemapper::~CPidRemapper()
{
}


//+-------------------------------------------------------------------------
//
//  Member:     CPidRemapper::RealToName, public
//
//  Effects:    Convert a PROPID to a propspec
//
//  Arguments:  [pid] - Given pid
//
//  History:    22-Jan-97     SitaramR         Added header
//
//--------------------------------------------------------------------------

CFullPropSpec const * CPidRemapper::RealToName( PROPID pid ) const
{
    Win4Assert( ! _xaPidReal.IsNull() );

    //
    // Just linear search
    //
    for ( unsigned i = 0;
          i < _cpidReal && _xaPidReal[i] != pid;
          i++
        )
    {
        continue;
    }

    Win4Assert( i < _cpidReal );

    //
    // This can happen if a hacker tries to munge query requests
    //

    if ( i == _cpidReal )
        THROW( CException( E_ABORT ) );

    return( _propNames.Get( i ) );
}


//+-------------------------------------------------------------------------
//
//  Member:     CPidRemapper::NameToReal, public
//
//  Effects:    Convert a property which is not necessarily in the pid map
//              to a PROPID.
//
//  Arguments:  [pProperty] - pointer to the propspec.
//
//  Returns:    PROPID - the mapped property ID or pidInvalid.
//
//  History:    28 Jun 94       AlanW   Created
//
//--------------------------------------------------------------------------

PROPID CPidRemapper::NameToReal( CFullPropSpec const * pProperty )
{
    for (unsigned i = 0; i < _cpidReal; i++)
    {
        if (*pProperty == *_propNames.Get(i))
        {
            return _xaPidReal[ i ];
        }
    }

    //
    //  Property is not in the mapping array.  Add it.
    //
    PROPID Prop;
    FULLPROPSPEC const * pFullPropSpec =  pProperty->CastToStruct();
    SCODE sc = _xPropMapper->PropertyToPropid( pFullPropSpec, TRUE, &Prop );
    if ( FAILED( sc ) )
        THROW( CException( sc ) );

    if (Prop != pidInvalid)
    {
        PROPID *ppidReal = new PROPID[ _cpidReal+1 ];
        memcpy(ppidReal, _xaPidReal.GetPointer(), _cpidReal * sizeof (PROPID));

        //
        // No lock is needed here to prevent races with RealToName since in current
        // usage everything is added in the query path by a single thread before
        // reads happen from multiple threads.
        //

        PROPID *ppidTemp = _xaPidReal.Acquire();
        _xaPidReal.Set( _cpidReal+1, ppidReal );
        delete [] ppidTemp;

        CFullPropSpec * ppsFull = new CFullPropSpec( *pProperty );

        XPtr<CFullPropSpec> xpps(ppsFull);

        if ( xpps.IsNull() || !xpps->IsValid() )
        {
            THROW( CException( STATUS_NO_MEMORY ) );
        }

        _propNames.Add( ppsFull, _cpidReal );
        xpps.Acquire();

        Win4Assert(*_propNames.Get(_cpidReal) == *pProperty);
        _xaPidReal[_cpidReal] = Prop;

        _cpidReal++;
    }

    return Prop;
}

//+-------------------------------------------------------------------------
//
//  Member:     CPidRemapper::RemapPropid, public
//
//  Effects:    Traverses [pRst], converting 'fake' propid to 'real'
//
//  Arguments:  [pRst] -- Restriction
//
//  History:    15-Feb-93 KyleP     Created
//
//--------------------------------------------------------------------------

void CPidRemapper::RemapPropid( CRestriction * pRst )
{
    Win4Assert ( pRst != 0 );

    switch( pRst->Type() )
    {
    case RTInternalProp:
    {
        CInternalPropertyRestriction * pPropRst =
            (CInternalPropertyRestriction *)pRst;

        pPropRst->SetPid( FakeToReal( pPropRst->Pid() ) );
        if ( 0 != pPropRst->GetContentHelper() )
            RemapPropid( pPropRst->GetContentHelper() );
        break;
    }

    case RTWord:
    {
        CWordRestriction * pWordRst = (CWordRestriction *)pRst;
        pWordRst->SetPid( FakeToReal( pWordRst->Pid() ) );
        break;
    }

    case RTSynonym:
    {
        CSynRestriction * pSynRst = (CSynRestriction *)pRst;
        CKeyArray & keys = pSynRst->GetKeys();

        for ( int i = keys.Count() - 1; i >= 0; i-- )
        {
            CKey & key = keys.Get(i);
            key.SetPid( FakeToReal( key.Pid() ) );
        }
        break;
    }

    case RTNot:
    {
        CNotRestriction * pnrst = (CNotRestriction *)pRst;

        RemapPropid( pnrst->GetChild() );
        break;
    }

    case RTAnd:
    case RTOr:
    case RTVector:
    case RTProximity:
    case RTPhrase:
    {
        CNodeRestriction * pNodeRst = pRst->CastToNode();

        for ( int i = pNodeRst->Count() - 1; i >= 0; i-- )
        {
            RemapPropid( pNodeRst->GetChild( i ) );
        }
        break;
    }
    case RTRange:
    case RTNone:   // probably a noise word in a vector query
        break;

    default:
        Win4Assert( !"RemapPropid: Unknown type." );
        break;

    }
}

//+-------------------------------------------------------------------------
//
//  Member:     CPidRemapper::RemapPropid, public
//
//  Effects:    Traverses [pColumns], converting 'fake' propid to 'real'
//
//  Arguments:  [pColumns] -- Output columns
//
//  History:    22-Jun-93 KyleP     Created
//
//--------------------------------------------------------------------------

void CPidRemapper::RemapPropid( CColumnSet * pColumns )
{
    for ( unsigned i = 0; i < pColumns->Count(); i++ )
    {
        pColumns->Get(i) = FakeToReal( pColumns->Get(i) );
    }
}


//+-------------------------------------------------------------------------
//
//  Member:     CPidRemapper::RemapPropid, public
//
//  Effects:    Traverses [pSort], converting 'fake' propid to 'real'
//
//  Arguments:  [pSort] -- Restriction
//
//  History:    15-Feb-93 KyleP     Created
//
//--------------------------------------------------------------------------

void CPidRemapper::RemapPropid( CSortSet * pSort )
{
    for ( unsigned i = 0; i < pSort->Count(); i++ )
    {
        pSort->Get(i).pidColumn = FakeToReal( pSort->Get(i).pidColumn );
    }
}


//+-------------------------------------------------------------------------
//--------------------------------------------------------------------------
void CPidRemapper::ReBuild( const CPidMapper & pidmap )
{
    if ( _cpidReal < pidmap.Count() )
    {
        delete [] _xaPidReal.Acquire();

        _cpidReal = 0;

        _xaPidReal.Set( pidmap.Count(), new PROPID[ pidmap.Count() ] );
    }

    _cpidReal = pidmap.Count();

    //
    // Iterate through the list
    //

    for ( unsigned i = 0; i < _cpidReal; i++ )
    {
        PROPID pid;
        FULLPROPSPEC const * pFullPropSpec =  pidmap.Get(i)->CastToStruct();
        SCODE sc = _xPropMapper->PropertyToPropid( pFullPropSpec, TRUE, &pid );
        if ( FAILED( sc ) )
            THROW( CException( sc ) );

        _xaPidReal[i] = pid;

        Win4Assert( _xaPidReal[i] != pidInvalid );
    }
}


//+-------------------------------------------------------------------------
//--------------------------------------------------------------------------
void CPidRemapper::Set( XArray<PROPID> & aPids )
{
    delete [] _xaPidReal.Acquire();

    _cpidReal = aPids.Count();
    _xaPidReal.Set( _cpidReal, aPids.Acquire() );
}

//
// This has to go somewhere...
//

UNICODECALLOUTS UnicodeCallouts = { WIN32_UNICODECALLOUTS };



//+-------------------------------------------------------------------------
//
//  Method:     CPidRemapper::AddRef
//
//  Synopsis:   Increments refcount
//
//  History:    22-Jan-1997      SitaramR       Created
//
//--------------------------------------------------------------------------

ULONG STDMETHODCALLTYPE CPidRemapper::AddRef()
{
    return InterlockedIncrement( (long *) &_cRefs );
}

//+-------------------------------------------------------------------------
//
//  Method:     CPidRemapper::Release
//
//  Synopsis:   Decrement refcount.  Delete if necessary.
//
//  History:    22-Jan-1997     SitaramR        Created
//
//--------------------------------------------------------------------------

ULONG STDMETHODCALLTYPE CPidRemapper::Release()
{
    Win4Assert( _cRefs > 0 );

    ULONG uTmp = InterlockedDecrement( (long *) &_cRefs );

    if ( 0 == uTmp )
        delete this;

    return(uTmp);
}



//+-------------------------------------------------------------------------
//
//  Method:     CPidRemapper::QueryInterface
//
//  Synopsis:   Rebind to other interface
//
//  Arguments:  [riid]      -- IID of new interface
//              [ppvObject] -- New interface * returned here
//
//  Returns:    S_OK if bind succeeded, E_NOINTERFACE if bind failed
//
//  History:    22-Jan-1997     SitaramR   Created
//
//--------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CPidRemapper::QueryInterface(
    REFIID riid,
    void ** ppvObject)
{
    if ( IID_ICiQueryPropertyMapper == riid )
        *ppvObject = (ICiQueryPropertyMapper *)this;
    else if ( IID_IUnknown == riid )
        *ppvObject = (IUnknown *)this;
    else
    {
        *ppvObject = 0;
        return E_NOINTERFACE;
    }

    AddRef();
    return S_OK;
}

//+-------------------------------------------------------------------------
//
//  Method:     CPidRemapper::PropertyToPropid
//
//  Synopsis:   Convert propspec to pid
//
//  Arguments:  [pFullPropSpec] -- propspec to convert
//              [pPropId]       -- pid returned here
//
//  History:    22-Jan-1997     SitaramR   Created
//
//--------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CPidRemapper::PropertyToPropid( const FULLPROPSPEC *pFullPropSpec,
                                                        PROPID *pPropId)
{
    SCODE sc = S_OK;

    TRY
    {
        CFullPropSpec const * pProperty = (CFullPropSpec const *) pFullPropSpec;
        *pPropId = NameToReal( pProperty );
    }
    CATCH( CException, e )
    {
        sc = e.GetErrorCode();

        vqDebugOut(( DEB_ERROR,
                     "CPidRemapper:PropertyToPropid - Exception caught 0x%x\n",
                     sc ));
    }
    END_CATCH;

    return sc;
}



//+-------------------------------------------------------------------------
//
//  Method:     CPidRemapper::PropidToProperty
//
//  Synopsis:   Convert pid to propspec
//
//  Arguments:  [pPropId]       -- pid to convert
//              [pFullPropSpec] -- propspec returned here
//
//  Notes:      *ppFullPropSpec is owned by CPidRemapper
//
//  History:    22-Jan-1997     SitaramR   Created
//
//--------------------------------------------------------------------------

SCODE STDMETHODCALLTYPE CPidRemapper::PropidToProperty( PROPID propId,
                                                        FULLPROPSPEC const **ppFullPropSpec )
{
    SCODE sc = S_OK;

    TRY
    {
        *ppFullPropSpec = RealToName( propId )->CastToStruct();
    }
    CATCH( CException, e )
    {
        sc = e.GetErrorCode();

        vqDebugOut(( DEB_ERROR,
                     "CPidRemapper:PropidToProperty - Exception caught 0x%x\n",
                     sc ));
    }
    END_CATCH;

    return sc;
}