/**********************************************************************/
/**                       Microsoft Windows/NT                       **/
/**                Copyright(c) Microsoft Corporation, 1997 - 1999 **/
/**********************************************************************/

/*
	root.cpp
		Root node information (the root node is not displayed
		in the MMC framework but contains information such as 
		all of the subnodes in this snapin).
		
    FILE HISTORY:
        
*/

#include "stdafx.h"
#include "util.h"
#include "riproot.h"
#include "reg.h"
#include "ripview.h"	// RIP handlers
#include "ripstats.h"
#include "routprot.h"	// IP_BOOTP


/*---------------------------------------------------------------------------
	RipRootHandler implementation
 ---------------------------------------------------------------------------*/

DEBUG_DECLARE_INSTANCE_COUNTER(RipRootHandler)

extern const ContainerColumnInfo s_rgRIPParamsStatsColumnInfo[];

struct _ViewInfoColumnEntry
{
	UINT	m_ulId;
	UINT	m_cColumns;
	const ContainerColumnInfo *m_prgColumn;
};

static const struct _ViewInfoColumnEntry	s_rgViewColumnInfo[] =
{
	{ RIPSTRM_STATS_RIPPARAMS, MVR_RIPPARAMS_COUNT, s_rgRIPParamsStatsColumnInfo },
};

RipRootHandler::RipRootHandler(ITFSComponentData *pCompData)
	: RootHandler(pCompData)
{
	DEBUG_INCREMENT_INSTANCE_COUNTER(RipRootHandler)
			
	m_ConfigStream.Init(DimensionOf(s_rgViewColumnInfo));
	
    // This will initialize the view information for the statistics
    // dialogs.  (which is why the fConfigurableColumns is set to TRUE).
	for (int i=0; i<DimensionOf(s_rgViewColumnInfo); i++)
	{
		m_ConfigStream.InitViewInfo(s_rgViewColumnInfo[i].m_ulId,
                                    TRUE /*fConfigurableColumns*/,
									s_rgViewColumnInfo[i].m_cColumns,
									TRUE,
									s_rgViewColumnInfo[i].m_prgColumn);
	}
}


STDMETHODIMP RipRootHandler::QueryInterface(REFIID riid, LPVOID *ppv)
{
    // Is the pointer bad?
    if (ppv == NULL)
		return E_INVALIDARG;

    //  Place NULL in *ppv in case of failure
    *ppv = NULL;

    //  This is the non-delegating IUnknown implementation
    if (riid == IID_IUnknown)
		*ppv = (LPVOID) this;
	else if (riid == IID_IRtrAdviseSink)
		*ppv = &m_IRtrAdviseSink;
	else
		return RootHandler::QueryInterface(riid, ppv);

    //  If we're going to return an interface, AddRef it first
    if (*ppv)
	{
	((LPUNKNOWN) *ppv)->AddRef();
		return hrOK;
	}
    else
		return E_NOINTERFACE;	
}


///////////////////////////////////////////////////////////////////////////////
//// IPersistStream interface members

STDMETHODIMP RipRootHandler::GetClassID
(
	CLSID *pClassID
)
{
    ASSERT(pClassID != NULL);

    // Copy the CLSID for this snapin
    *pClassID = CLSID_IPXRipExtension;

    return hrOK;
}

/*!--------------------------------------------------------------------------
	RipRootHandler::OnExpand
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
HRESULT RipRootHandler::OnExpand(ITFSNode *pNode,
								 LPDATAOBJECT pDataObject,
								 DWORD dwType,
								 LPARAM arg,
								 LPARAM lParam)
{
	HRESULT	                hr = hrOK;
	SPITFSNode			    spNode;
	SPIRtrMgrProtocolInfo	spRmProt;
    SPIRtrMgrInfo           spRm;
    SPIRouterInfo           spRouterInfo;
    LONG_PTR               ulConnId;

	// Grab the router info from the dataobject
	spRm.Query(pDataObject);
	Assert(spRm);

	spRm->GetParentRouterInfo(&spRouterInfo);

	// Setup the advise on the RtrMgr (to see when BootP is added/removed)
	spRm->RtrAdvise(&m_IRtrAdviseSink, &ulConnId, 0);

    // add things to our map for later
    AddRtrObj(ulConnId, IID_IRtrMgrInfo, spRm);
    AddScopeItem(spRm->GetMachineName(), (HSCOPEITEM) lParam);

    hr = spRm->FindRtrMgrProtocol(IPX_PROTOCOL_RIP, &spRmProt);
	if (!FHrOK(hr))
	{
		// Treat this as an already expanded node, we depend on
		// the notification mechanism to let us know if something
		// changes
		goto Error;
	}

	CORg( AddProtocolNode(pNode, spRouterInfo) );

    SetProtocolAdded(ulConnId, TRUE);
	
Error:
	return hr;
}

/*!--------------------------------------------------------------------------
	RipRootHandler::OnCreateDataObject
		Implementation of ITFSNodeHandler::OnCreateDataObject
	Author: KennT
 ---------------------------------------------------------------------------*/
STDMETHODIMP RipRootHandler::OnCreateDataObject(MMC_COOKIE cookie, DATA_OBJECT_TYPES type, IDataObject **ppDataObject)
{
	HRESULT		    hr = hrOK;
    SPIRouterInfo   spRouterInfo;

    COM_PROTECT_TRY
	{
		// this will always be NULL
        if (spRouterInfo == NULL)
		{
			// If we haven't created the sub nodes yet, we still have to
			// create a dataobject.
			CDataObject *	pObject = NULL;
			SPIDataObject	spDataObject;
			SPITFSNode		spNode;
			SPITFSNodeHandler	spHandler;
			
			pObject = new CDataObject;
			spDataObject = pObject;	// do this so that it gets released correctly
			Assert(pObject != NULL);
			
			// Save cookie and type for delayed rendering
			pObject->SetType(type);
			pObject->SetCookie(cookie);
			
			// Store the coclass with the data object
			pObject->SetClsid(*(m_spTFSCompData->GetCoClassID()));
			
			pObject->SetTFSComponentData(m_spTFSCompData);
			
			hr = pObject->QueryInterface(IID_IDataObject, 
									 reinterpret_cast<void**>(ppDataObject));
			
		}
		else
			hr = CreateDataObjectFromRouterInfo(spRouterInfo,
												type, cookie, m_spTFSCompData,
												ppDataObject);
	}
	COM_PROTECT_CATCH;
	return hr;
}



ImplementEmbeddedUnknown(RipRootHandler, IRtrAdviseSink)

STDMETHODIMP RipRootHandler::EIRtrAdviseSink::OnChange(LONG_PTR ulConn,
	DWORD dwChangeType, DWORD dwObjectType, LPARAM lUserParam, LPARAM lParam)
{
 	InitPThis(RipRootHandler, IRtrAdviseSink);
	HRESULT		    hr = hrOK;
	SPITFSNode	    spNode;
    SPIRtrMgrInfo   spRm;
    SPIRouterInfo   spRouterInfo;

	if (dwObjectType != ROUTER_OBJ_RmProt)
		return hr;

	COM_PROTECT_TRY
	{
        CORg (pThis->GetRtrObj(ulConn, (IUnknown **) &spRm));

		if (dwChangeType == ROUTER_CHILD_ADD)
		{
			// check to see if BootP is in the current list
			if (spRm->FindRtrMgrProtocol(IPX_PROTOCOL_RIP, NULL) == hrOK)
			{
				// We found Bootp, add our child node if we
				// don't have a child node
				if (!pThis->IsProtocolAdded(ulConn))
				{
                	spRm->GetParentRouterInfo(&spRouterInfo);
					pThis->m_spNodeMgr->GetRootNode(&spNode);
					pThis->AddProtocolNode(spNode, spRouterInfo);
					pThis->SetProtocolAdded(ulConn, TRUE);
				}
			}
		}
		else if (dwChangeType == ROUTER_CHILD_DELETE)
		{
			if (spRm->FindRtrMgrProtocol(IPX_PROTOCOL_RIP, NULL) == hrFalse)
			{
				// couldn't find Bootp, delete all of our child nodes
				pThis->m_spNodeMgr->GetRootNode(&spNode);
				pThis->RemoveNode(spNode, spRm->GetMachineName());
			    pThis->SetProtocolAdded(ulConn, FALSE);
			}
		}
		
        COM_PROTECT_ERROR_LABEL;
	}
	COM_PROTECT_CATCH;
	
	return hr;
}

/*!--------------------------------------------------------------------------
	RipRootHandler::DestroyHandler
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
STDMETHODIMP RipRootHandler::DestroyHandler(ITFSNode *pNode)
{
    RemoveAllNodes(pNode);
    RemoveAllRtrObj();
	return hrOK;
}

/*!--------------------------------------------------------------------------
	RipRootHandler::AddProtocolNode
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
HRESULT RipRootHandler::AddProtocolNode(ITFSNode *pNode, IRouterInfo * pRouterInfo)
{
	SPITFSNodeHandler	spHandler;
	RipNodeHandler *    pHandler = NULL;
	HRESULT				hr = hrOK;
	SPITFSNode			spNode;
    HSCOPEITEM          hScopeItem, hOldScopeItem;
    
    // Windows NT Bug : 246822
    // Due to the server list programming model, we need to setup
    // the proper scopeitem (so that MMC adds this to the proper
    // node).

    
    // Get the proper scope item for this node.
    // ----------------------------------------------------------------
    Verify( GetScopeItem(pRouterInfo->GetMachineName(), &hScopeItem) == hrOK);

    
    // Get the old one and save it.  place the new one in the node.
    // ----------------------------------------------------------------
    hOldScopeItem = pNode->GetData(TFS_DATA_SCOPEID);
    pNode->SetData(TFS_DATA_SCOPEID, hScopeItem);

	pHandler = new RipNodeHandler(m_spTFSCompData);
	spHandler = pHandler;
	CORg( pHandler->Init(pRouterInfo, &m_ConfigStream) );

	CreateContainerTFSNode(&spNode,
						   &GUID_IPXRipNodeType,
						   static_cast<ITFSNodeHandler *>(pHandler),
						   static_cast<ITFSResultHandler *>(pHandler),
						   m_spNodeMgr);

	// Call to the node handler to init the node data
	pHandler->ConstructNode(spNode);
				
	// Make the node immediately visible
	spNode->SetVisibilityState(TFS_VIS_SHOW);
	pNode->AddChild(spNode);

Error:
    // Restore the scope item
    pNode->SetData(TFS_DATA_SCOPEID, hOldScopeItem);
	return hr;
}


/*!--------------------------------------------------------------------------
	RipRootHandler::CompareNodeToMachineName
		This function is used by the RemoveNode() function.

        Returns hrOK if this node is a DHCP relay node and corresponds
        to the pszMachineName.
        Returns hrFalse if this is not the indicated node.
        Returns errors otherwise.
	Author: KennT
 ---------------------------------------------------------------------------*/
HRESULT RipRootHandler::CompareNodeToMachineName(ITFSNode *pNode,
    LPCTSTR pszMachineName)
{
    HRESULT     hr = hrFalse;

    // Should check that this is a RIP node
    if (*(pNode->GetNodeType()) != GUID_IPXRipNodeType)
        hr = hrFalse;
    else
    {
        IPXConnection *  pIPXConn;
        
        pIPXConn = GET_RIP_NODEDATA(pNode);
        if (StriCmp(pszMachineName, pIPXConn->GetMachineName()) == 0)
            hr = hrOK;
    }

    return hr;
}