// stdcmpnt.cpp : Implementation of CComponent

#include "guidhelp.h" // ExtractData

// Note that m_pComponentData is still NULL during construction
CComponent::CComponent()
:	m_pConsole( NULL ),
	m_pConsoleVerb( NULL ),
	m_pHeader( NULL ),
	m_pResultData( NULL ),
	m_pConsoleNameSpace( NULL ),
	m_pRsltImageList( NULL ),
	m_pComponentData( NULL )
{
}

CComponent::~CComponent()
{
	VERIFY( SUCCEEDED(ReleaseAll()) );
}

/////////////////////////////////////////////////////////////////////
// CComponent::SetComponentDataPtr()
void CComponent::SetComponentDataPtr(
	CComponentData* pComponentData)
{
    ASSERT(NULL != pComponentData && NULL == m_pComponentData);
	(void) ((IComponentData*)pComponentData)->AddRef();
	m_pComponentData = pComponentData;
}

/////////////////////////////////////////////////////////////////////
// CComponent::IComponent::QueryDataObject()
STDMETHODIMP CComponent::QueryDataObject(MMC_COOKIE cookie, DATA_OBJECT_TYPES type, LPDATAOBJECT* ppDataObject)
{
    HRESULT hr = S_OK;
    MFC_TRY;
    // Delegate it to the IComponentData
    hr = QueryBaseComponentDataRef().QueryDataObject(cookie, type, ppDataObject);
    MFC_CATCH;
    return hr;
}

/////////////////////////////////////////////////////////////////////
// CComponent::IComponent::CompareObjects()
STDMETHODIMP CComponent::CompareObjects(
	LPDATAOBJECT lpDataObjectA,
	LPDATAOBJECT lpDataObjectB)
{
	return QueryBaseComponentDataRef().CompareObjects( lpDataObjectA, lpDataObjectB );
}

/////////////////////////////////////////////////////////////////////
// Virtual function called by CComponent::IComponent::Notify(MMCN_PROPERTY_CHANGE)
// OnPropertyChange() is generated by MMCPropertyChangeNotify( param )
HRESULT CComponent::OnPropertyChange( LPARAM /*param*/)
{
	return S_OK;
}

/////////////////////////////////////////////////////////////////////
// Virtual function called by CComponent::IComponent::Notify(MMCN_SELECT)
HRESULT CComponent::OnNotifySelect( LPDATAOBJECT /*lpDataObject*/, BOOL /*fSelected*/ )
{
	return S_OK;
}

/////////////////////////////////////////////////////////////////////
// Virtual function called by CComponent::IComponent::Notify(MMCN_ACTIVATE)
HRESULT CComponent::OnNotifyActivate( LPDATAOBJECT /*lpDataObject*/, BOOL /*fActivated*/ )
{
	return S_OK;
}

/////////////////////////////////////////////////////////////////////
// Virtual function called by CComponent::IComponent::Notify(MMCN_CLICK)
HRESULT CComponent::OnNotifyClick( LPDATAOBJECT /*lpDataObject*/ )
{
	TRACE0("CComponent::OnNotifyClick().\n");
	return S_OK;
}

/////////////////////////////////////////////////////////////////////
// Virtual function called by CComponent::IComponent::Notify(MMCN_DBLCLICK)
HRESULT CComponent::OnNotifyDblClick( LPDATAOBJECT /*lpDataObject*/ )
{
	// Returning S_FALSE allows MMC to do the default verb.
	return S_FALSE;
}

/////////////////////////////////////////////////////////////////////
// Virtual function called by CComponent::IComponent::Notify(MMCN_ADD_IMAGES)
HRESULT CComponent::OnNotifyAddImages( LPDATAOBJECT /*lpDataObject*/,
	                                   LPIMAGELIST /*lpImageList*/,
									   HSCOPEITEM /*hSelectedItem*/ )
{
	ASSERT(FALSE); // this should be redefined by all snapins
	return S_OK;
}

/////////////////////////////////////////////////////////////////////
// Virtual function called by CComponent::IComponent::Notify(MMCN_VIEW_CHANGE)
// OnViewChange is generated by UpdateAllViews( lpDataObject, data, hint )
HRESULT CComponent::OnViewChange( LPDATAOBJECT /*lpDataObject*/, LPARAM /*data*/, LPARAM /*hint*/ )
{
	return S_OK;
}

/////////////////////////////////////////////////////////////////////
// Virtual function called by CComponent::IComponent::Notify(MMCN_REFRESH)
// OnNotifyRefresh is generated by enabling the verb MMC_VERB_REFRESH.
// Typically this routine will be overriden.
HRESULT CComponent::OnNotifyRefresh( LPDATAOBJECT /*lpDataObject*/ )
{
	TRACE0("CComponent::OnNotifyRefresh() - You must implement your own refresh routine.\n");
	return S_OK;
}

/////////////////////////////////////////////////////////////////////
// Virtual function called by CComponent::IComponent::Notify(MMCN_DELETE)
HRESULT CComponent::OnNotifyDelete( LPDATAOBJECT /*lpDataObject*/ )
{
	return S_OK;
}

/////////////////////////////////////////////////////////////////////
// Virtual function called by CComponent::IComponent::Notify(MMCN_COLUMN_CLICK)
HRESULT CComponent::OnNotifyColumnClick( LPDATAOBJECT /*lpDataObject*/, LPARAM /*iColumn*/, LPARAM /*uFlags*/ )
{
	return S_OK;
}


/////////////////////////////////////////////////////////////////////
//	CComponent::ReleaseAll()
HRESULT CComponent::ReleaseAll()
{
    MFC_TRY;
	TRACE_METHOD(CComponent,Destructor);

	if (NULL != m_pHeader)
		m_pConsole->SetHeader(NULL);

    SAFE_RELEASE(m_pHeader);

    SAFE_RELEASE(m_pResultData);
    SAFE_RELEASE(m_pConsoleNameSpace);
    SAFE_RELEASE(m_pRsltImageList);
    SAFE_RELEASE(m_pConsole);
	SAFE_RELEASE(m_pConsoleVerb);

	if ( NULL != m_pComponentData )
	{
		((IComponentData*)m_pComponentData)->Release();
		m_pComponentData = NULL;
	}

    MFC_CATCH;
	return S_OK;
}


/////////////////////////////////////////////////////////////////////////////
// CComponent::IComponent::Initialize()
STDMETHODIMP CComponent::Initialize(LPCONSOLE lpConsole)
{
    MFC_TRY;
    TRACE_METHOD(CComponent,Create);
    TEST_NONNULL_PTR_PARAM(lpConsole);

    if (NULL == lpConsole)
    {
        ASSERT(FALSE);
        return E_POINTER;
    }

	ASSERT( NULL == m_pConsole );
	SAFE_RELEASE( m_pConsole ); // just in case

	lpConsole->AddRef();
    m_pConsole = lpConsole;

    HRESULT hr = m_pConsole->QueryInterface(IID_IHeaderCtrl, (void**)&m_pHeader);

    ASSERT(hr == S_OK);
    if (FAILED(hr))
        return E_FAIL;

    m_pConsole->SetHeader(m_pHeader);

    hr = m_pConsole->QueryConsoleVerb(OUT &m_pConsoleVerb);
    ASSERT(hr == S_OK);
	if (FAILED(hr))
        return hr;
	ASSERT(NULL != m_pConsoleVerb);

    hr = m_pConsole->QueryInterface(IID_IResultData, (void**)&m_pResultData);
    if (FAILED(hr))
        return hr;

    hr = m_pConsole->QueryInterface(IID_IConsoleNameSpace, (void**)&m_pConsoleNameSpace);
    if (FAILED(hr))
        return hr;

    hr = m_pConsole->QueryInterface(IID_IImageList, (void**)&m_pRsltImageList);
    if (FAILED(hr))
        return hr;

    // Load icons for the scope pane
    LPIMAGELIST pImageList;
    hr = m_pConsole->QueryScopeImageList(&pImageList);
    ASSERT(SUCCEEDED(hr));
//    LoadIconsIntoImageList(pImageList, FALSE);
    pImageList->Release();

    MFC_CATCH;
    return S_OK;
} // CComponent::Initialize()

/////////////////////////////////////////////////////////////////////////////
// CComponent::IComponent::Notify()
// Entry point for all the MMCN_ notification messages.
// The routine will then call virtual functions of the CComponent object.
STDMETHODIMP CComponent::Notify(LPDATAOBJECT lpDataObject, MMC_NOTIFY_TYPE event, LPARAM arg, LPARAM param)
{
    HRESULT hr = S_OK;
    MFC_TRY;
    TRACE_METHOD(CComponent,Notify);


    switch (event)
    {
    case MMCN_SHOW:
		// CODEWORK this is hacked together quickly
		{
			CCookie* pcookie = NULL;
			HRESULT hr = ExtractData( lpDataObject,
		                      CDataObject::m_CFRawCookie,
							  (PBYTE)&pcookie,
							  sizeof(pcookie) );
			ASSERT( SUCCEEDED(hr) );
			CCookie* pActiveCookie = ActiveBaseCookie (pcookie);
			// Save the scope item handle in the cookie
			pActiveCookie->m_hScopeItem = (HSCOPEITEM) param;
			hr = Show (pActiveCookie, arg, (HSCOPEITEM) param);
		}
        break;

    case MMCN_MINIMIZED:
        break;

	case MMCN_SELECT:
		hr = OnNotifySelect( lpDataObject, (BOOL)(HIWORD(arg)) );
		break;

	case MMCN_ACTIVATE:
		hr = OnNotifyActivate( lpDataObject, (BOOL)arg );
		break;

	case MMCN_ADD_IMAGES:
		hr = OnNotifyAddImages( lpDataObject,
		                        reinterpret_cast<IImageList*>(arg),
								(HSCOPEITEM)param );
		break;

	case MMCN_CLICK:
		hr = OnNotifyClick( lpDataObject );
		break;

	case MMCN_DBLCLICK:
		hr = OnNotifyDblClick( lpDataObject );
		break;

	case MMCN_PROPERTY_CHANGE:
		// CODEWORK arg is "fScopePane", should this be passed on?
		hr = OnPropertyChange( param );
		break;

	case MMCN_VIEW_CHANGE:
		hr = OnViewChange( lpDataObject, arg, param );
		break;

	case MMCN_REFRESH:
		hr = OnNotifyRefresh( lpDataObject );
		break;

	case MMCN_DELETE:
		hr = OnNotifyDelete( lpDataObject );
		break;

	case MMCN_COLUMN_CLICK:
		hr = OnNotifyColumnClick( lpDataObject, arg, param );
		break;

	case MMCN_CONTEXTHELP:
		hr = OnNotifyContextHelp( lpDataObject );
		break;

	case MMCN_SNAPINHELP:
		hr = OnNotifySnapinHelp( lpDataObject );
		break;

	default:
		TRACE1("INFO: CComponent::Notify() - Unknown Event %d.\n", event);
		break;
    }

    MFC_CATCH;
    return hr;
} // CComponent::Notify()


// parameter "MMC_COOKIE cookie" is reserved per MSDN
STDMETHODIMP CComponent::Destroy(MMC_COOKIE /*cookie*/)
{
    MFC_TRY;
	TRACE_METHOD(CComponent,Destroy);

	VERIFY( SUCCEEDED( ReleaseAll() ) );

    MFC_CATCH;
	return S_OK;
}


HRESULT CComponent::InsertResultCookies( CCookie& refparentcookie )
{
	ASSERT( NULL != m_pResultData );

    RESULTDATAITEM tRDItem;
	::ZeroMemory( &tRDItem, sizeof(tRDItem) );
	tRDItem.nCol = 0;
	tRDItem.mask = RDI_STR | RDI_IMAGE | RDI_PARAM;
	tRDItem.str = MMC_CALLBACK;
	// CODEWORK should use MMC_ICON_CALLBACK here

	HRESULT hr = S_OK;
	POSITION pos = refparentcookie.m_listResultCookieBlocks.GetHeadPosition();
	while (NULL != pos)
	{
		CBaseCookieBlock* pblock = refparentcookie.m_listResultCookieBlocks.GetNext( pos );
		ASSERT( NULL != pblock );
		for (INT i = 0; i < pblock->QueryNumCookies(); i++)
		{
			CCookie* pbasecookie = pblock->QueryBaseCookie(i);
			tRDItem.nImage = QueryBaseComponentDataRef().QueryImage( *pbasecookie, FALSE );
			// WARNING cookie cast
			tRDItem.lParam = reinterpret_cast<LPARAM>(pbasecookie);
	        hr = m_pResultData->InsertItem(&tRDItem);
			if ( FAILED(hr) )
			{
				ASSERT(FALSE);
				break;
			}
		}
	}
	return hr;
}


STDMETHODIMP CComponent::GetResultViewType(MMC_COOKIE /*cookie*/,
										   BSTR* ppViewType,
										   long* pViewOptions)
{
    *ppViewType = NULL;
    *pViewOptions = MMC_VIEW_OPTIONS_NONE;
    return S_FALSE;
}

STDMETHODIMP CComponent::GetDisplayInfo(RESULTDATAITEM* pResultDataItem)
{
    MFC_TRY;
	CCookie* pcookie = ActiveBaseCookie(
		reinterpret_cast<CCookie*>(pResultDataItem->lParam));
	ASSERT( NULL != pResultDataItem ); // result items never have NULL cookie
	if (RDI_STR & pResultDataItem->mask)
	{
		pResultDataItem->str = QueryBaseComponentDataRef().QueryResultColumnText(
			*pcookie,
			pResultDataItem->nCol );
		if ( NULL == pResultDataItem->str )
			pResultDataItem->str = L""; // just in case
	}
	if ( RDI_IMAGE & pResultDataItem->mask )
	{
		pResultDataItem->nImage = QueryBaseComponentDataRef().QueryImage(
			*pcookie, FALSE );
	}
    MFC_CATCH;
	return S_OK;
}

// CODEWORK These should be parameters rather than globals
// CODEWORK figure out correct const'ing
extern UINT** g_aColumns;
extern int** g_aColumnWidths;

HRESULT CComponent::LoadColumnsFromArrays(
	INT objecttype )
{
	ASSERT( NULL != m_pHeader );

	CString str;
	const UINT* pColumns = g_aColumns[objecttype];
	const int* pColumnWidths = g_aColumnWidths[objecttype];
	ASSERT( NULL != pColumns && NULL != pColumnWidths );
	for ( INT i = 0; 0 != pColumns[i]; i++)
	{
		VERIFY( str.LoadString( pColumns[i] ) );
		m_pHeader->InsertColumn(i, const_cast<LPTSTR>((LPCTSTR)str), LVCFMT_LEFT,
			pColumnWidths[i]);
	}

	return S_OK;
}

HRESULT CComponent::OnNotifySnapinHelp (LPDATAOBJECT /*pDataObject*/)
{
	return ShowHelpTopic( NULL ); // snapins should redefine this
}

HRESULT CComponent::OnNotifyContextHelp (LPDATAOBJECT pDataObject)
{
	return OnNotifySnapinHelp( pDataObject ); // snapins should redefine this
}

HRESULT CComponent::ShowHelpTopic( LPCWSTR lpcwszHelpTopic )
{
    HRESULT hr = S_OK;
    MFC_TRY;
	CComQIPtr<IDisplayHelp,&IID_IDisplayHelp>	spDisplayHelp = m_pConsole;
	if ( !spDisplayHelp )
	{
		ASSERT(FALSE);
		return E_UNEXPECTED;
	}

	CString strHelpTopic;
	hr = QueryBaseComponentDataRef().GetHtmlHelpFilePath( strHelpTopic );
	if ( FAILED(hr) )
		return hr;
	if (NULL != lpcwszHelpTopic && L'\0' != *lpcwszHelpTopic)
	{
		strHelpTopic += L"::/";
		strHelpTopic += lpcwszHelpTopic;
	}
	hr = spDisplayHelp->ShowTopic (T2OLE ((LPWSTR)(LPCWSTR) strHelpTopic));
	ASSERT (SUCCEEDED (hr));

    MFC_CATCH;
	return hr;
}

