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

/*
	info.cpp
		
    FILE HISTORY:
        
*/

#include "stdafx.h"
#include "infoi.h"
#include "rtrstr.h"			// common router strings
#include "refresh.h"
#include "dvsview.h"
#include "machine.h"
#include "rtrutilp.h"


// Number of connections that we have made, this is used to
// generate the dwConnectionId
extern long		s_cConnections;

DEBUG_DECLARE_INSTANCE_COUNTER(RefreshItem);



/*!--------------------------------------------------------------------------
	RouterObjectRefreshTimerProc
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
void RouterRefreshObjectTimerProc(LPARAM lParam, DWORD dwTime)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	// The lParam passed in is a pointer to the RouterRefreshObject

	// Call through on the RouterRefreshObject to start the query
	// object
	((RouterRefreshObject *)lParam)->ExecuteRefresh();
}

/*---------------------------------------------------------------------------
	RouterRefreshObjectGroup implementation
 ---------------------------------------------------------------------------*/

DEBUG_DECLARE_INSTANCE_COUNTER(RouterRefreshObjectGroup);

RouterRefreshObjectGroup::~RouterRefreshObjectGroup()
{
	POSITION	p = NULL;
	RouterRefreshObject*	pObj = NULL;
	for(p = m_list.GetHeadPosition(); p != NULL; )
	{
		pObj = m_list.GetNext(p);
		Assert(pObj != NULL);
		pObj->SetGroup(NULL);
		pObj->Release();
	}

	m_list.RemoveAll();
    
	DEBUG_DECREMENT_INSTANCE_COUNTER(RouterRefreshObjectGroup);
}

/*!--------------------------------------------------------------------------
	RouterRefreshObjectGroup::Join
        -
	Author: WeiJiang
 ---------------------------------------------------------------------------*/
HRESULT	RouterRefreshObjectGroup::Join(RouterRefreshObject* pRefresh)
{
	Assert(pRefresh);
    HRESULT     hr = hrOK;

    COM_PROTECT_TRY
    {
        m_list.AddTail(pRefresh);
        pRefresh->AddRef();
        pRefresh->SetGroup(this);
    }
    COM_PROTECT_CATCH;
	return hr;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObjectGroup::Leave
		-
	Author: WeiJiang
 ---------------------------------------------------------------------------*/
HRESULT	RouterRefreshObjectGroup::Leave(RouterRefreshObject* pRefresh)
{
	POSITION p = m_list.Find(pRefresh);

	if (p)
	{
		Assert(pRefresh == m_list.GetAt(p));	
		m_list.RemoveAt(p);
		pRefresh->SetGroup(NULL);
		pRefresh->Release();	
	}

	return S_OK;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObjectGroup::Refresh
        call each member in the group to DoRefresh
	Author: WeiJiang
 ---------------------------------------------------------------------------*/
HRESULT	RouterRefreshObjectGroup::Refresh()
{
	POSITION	p = NULL;
	RouterRefreshObject*	pObj = NULL;
	for(p = m_list.GetHeadPosition(); p != NULL; )
	{
		pObj = m_list.GetNext(p);
		Assert(pObj != NULL);
		pObj->DoRefresh();
	}

	return S_OK;
}

/*---------------------------------------------------------------------------
	RouterRefreshObject implementation
 ---------------------------------------------------------------------------*/

IMPLEMENT_ADDREF_RELEASE(RouterRefreshObject);

DEBUG_DECLARE_INSTANCE_COUNTER(RouterRefreshObject);

STDMETHODIMP RouterRefreshObject::QueryInterface(REFIID iid,void **ppv)
{ 
	*ppv = 0; 
	if (iid == IID_IUnknown)
		*ppv = (IUnknown *) (IRouterRefresh *) this;
	else if (iid == IID_IRouterRefresh)
		*ppv = (IRouterRefresh *) this;
    else if (iid == IID_IRouterRefreshModify)
        *ppv = (IRouterRefreshModify *) this;
	else
		return ThreadHandler::QueryInterface(iid, ppv);
	
	((IUnknown *) *ppv)->AddRef(); 
	return hrOK;
}


RouterRefreshObject::RouterRefreshObject(HWND hWndSync)
	: m_hWndSync(hWndSync),
	m_dwSeconds(DEFAULT_REFRESH_INTERVAL),
	m_iEventId(-1),
	m_pRefreshGroup(NULL),
	m_fStarted(FALSE),
	m_fInRefresh(FALSE)
{
	DEBUG_INCREMENT_INSTANCE_COUNTER(RouterRefreshObject);
	InitializeCriticalSection(&m_critsec);
}

RouterRefreshObject::~RouterRefreshObject()
{
	// Shut down the timer if its started
	Stop();

	DEBUG_DECREMENT_INSTANCE_COUNTER(RouterRefreshObject);

	DeleteCriticalSection(&m_critsec);
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::GetRefreshInterval
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
STDMETHODIMP RouterRefreshObject::GetRefreshInterval(DWORD *pdwSeconds)
{
	RtrCriticalSection	rtrCritSec(&m_critsec);
	HRESULT	hr = hrOK;

	if (pdwSeconds == NULL)
		return E_INVALIDARG;
	
	COM_PROTECT_TRY
	{
		if (pdwSeconds)
			*pdwSeconds = m_dwSeconds;
	}
	COM_PROTECT_CATCH;
	return hr;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::GetRefreshInterval
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
STDMETHODIMP RouterRefreshObject::SetRefreshInterval(DWORD dwSeconds)
{
	HRESULT	hr = hrOK;
	if (IsRefreshStarted() == hrOK)
	{
		Stop();
		Start(dwSeconds);
	}
	else
	{
		RtrCriticalSection	rtrCritSec(&m_critsec);
		m_dwSeconds = dwSeconds;
	}

	return hr;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::IsInRefresh
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
STDMETHODIMP RouterRefreshObject::IsInRefresh()
{
	RtrCriticalSection	rtrCritSec(&m_critsec);
	HRESULT	hr = hrOK;
	COM_PROTECT_TRY
	{
		hr = (m_fInRefresh) ? hrOK : hrFalse;
	}
	COM_PROTECT_CATCH;
	return hr;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::Refresh
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
STDMETHODIMP RouterRefreshObject::Refresh()
{
	if (m_pRefreshGroup)
	{
		return m_pRefreshGroup->Refresh();
	}
	else
	{
		return DoRefresh();
	}
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::DoRefresh
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
HRESULT RouterRefreshObject::DoRefresh()
{
	RtrCriticalSection	rtrCritSec(&m_critsec);
	HRESULT	hr = hrOK;
	COM_PROTECT_TRY
	{
		// If we are in a refresh cycle, return hrOK
		if (m_fInRefresh)
			goto Error;

		// If we are not in a refresh cycle, then we start one
		ExecuteRefresh();


		COM_PROTECT_ERROR_LABEL;
	}
	COM_PROTECT_CATCH;
	return hr;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::Start
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
STDMETHODIMP RouterRefreshObject::Start(DWORD dwSeconds)
{
	RtrCriticalSection	rtrCritSec(&m_critsec);
	HRESULT	hr = hrOK;
	COM_PROTECT_TRY
	{
		// If we are already started then end
		if (m_fStarted)
			goto Error;

		// Start the timer going
		m_fStarted = TRUE;
		m_dwSeconds = dwSeconds;
		m_iEventId = g_timerMgr.AllocateTimer(RouterRefreshObjectTimerProc,
											  (LPARAM) this,
											  dwSeconds * 1000);
		if (m_iEventId == -1)
		{
			m_fStarted = FALSE;
			hr = HRESULT_FROM_WIN32(::GetLastError());
		}
		
		COM_PROTECT_ERROR_LABEL;
	}
	COM_PROTECT_CATCH;
	return hr;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::Stop
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
STDMETHODIMP RouterRefreshObject::Stop()
{
	RtrCriticalSection	rtrCritSec(&m_critsec);
	HRESULT	hr = hrOK;
	COM_PROTECT_TRY
	{
		if (!m_fStarted)
		{
			Assert(m_iEventId == -1);
			goto Error;
		}

		// Stop the timer
		if (m_iEventId != -1)
			g_timerMgr.FreeTimer(m_iEventId);
		m_iEventId = -1;

		ReleaseThreadHandler();
		WaitForThreadToExit();
		
		m_fStarted = FALSE;

		COM_PROTECT_ERROR_LABEL;
	}
	COM_PROTECT_CATCH;
	return hr;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::IsRefreshStarted
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
STDMETHODIMP RouterRefreshObject::IsRefreshStarted()
{
	RtrCriticalSection	rtrCritSec(&m_critsec);
	HRESULT	hr = hrOK;
	COM_PROTECT_TRY
	{
		hr = m_fStarted ? hrOK : hrFalse;
		Assert((m_fStarted == FALSE) || (m_iEventId != -1));
	}
	COM_PROTECT_CATCH;
	return hr;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::AdviseRefresh
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
STDMETHODIMP RouterRefreshObject::AdviseRefresh(IRtrAdviseSink *pRtrAdviseSink,
												LONG_PTR *pdwConnection,
												LPARAM lUserParam)
{
	Assert(pRtrAdviseSink);
	Assert(pdwConnection);

	RtrCriticalSection	rtrCritSec(&m_critsec);
	DWORD	dwConnId;
	HRESULT	hr = hrOK;
	
	COM_PROTECT_TRY
	{
		dwConnId = (DWORD) InterlockedIncrement(&s_cConnections);

		CORg( m_AdviseList.AddConnection(pRtrAdviseSink, dwConnId, lUserParam) );
		
		*pdwConnection = dwConnId;

		COM_PROTECT_ERROR_LABEL;
	}
	COM_PROTECT_CATCH;
	return hr;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::AddRouter
		-
	Author: WeiJiang
 ---------------------------------------------------------------------------*/
STDMETHODIMP RouterRefreshObject::AddRouterObject(REFIID riid, IUnknown *pUnk)
{
	HRESULT	hr = S_OK;
    IRouterInfo *   pRouterInfo;

    if (riid != IID_IRouterInfo)
        return E_NOINTERFACE;

    pRouterInfo = reinterpret_cast<IRouterInfo *>(pUnk);

    COM_PROTECT_TRY
    {    
		CRouterInfoRefreshItem* pRefreshItem =
                                    new CRouterInfoRefreshItem(pRouterInfo);

        if (pRefreshItem)
        {
            // check for duplicates
            if (S_FALSE == m_listElements.AddRefreshItem(pRefreshItem))
                delete pRefreshItem;
        }
    }
    COM_PROTECT_CATCH;

	return hr;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::RemoveRouterObject
		-
	Author: WeiJiang
 ---------------------------------------------------------------------------*/
STDMETHODIMP RouterRefreshObject::RemoveRouterObject(REFIID riid, IUnknown *pUnk)
{
    if (riid != IID_IRouterInfo)
        return E_NOINTERFACE;
    
    IRouterInfo * pRouterInfo = reinterpret_cast<IRouterInfo *>(pUnk);
    
	CRouterInfoRefreshItem	RefreshItem(pRouterInfo);

	return m_listElements.RemoveRefreshItem(RefreshItem);
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::AddStatusNode
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
HRESULT RouterRefreshObject::AddStatusNode(DomainStatusHandler* pStatusHandler, ITFSNode *pServerNode)
{
	HRESULT	hr = S_OK;

    COM_PROTECT_TRY
    {
		CStatusNodeRefreshItem* pRefreshItem = new CStatusNodeRefreshItem(pStatusHandler, pServerNode);

		if (pRefreshItem)
		{
            // Check for duplicates
			if (S_FALSE == m_listElements.AddRefreshItem(pRefreshItem))
				delete pRefreshItem;
		}
    }
    COM_PROTECT_CATCH;
    
	return hr;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::RemoveRouter
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
HRESULT RouterRefreshObject::RemoveStatusNode(ITFSNode *pServerNode)
{
	CStatusNodeRefreshItem	RefreshItem((DomainStatusHandler *)0x1, pServerNode);

	return m_listElements.RemoveRefreshItem(RefreshItem);
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::NotifyRefresh
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
STDMETHODIMP RouterRefreshObject::NotifyRefresh()
{
	RtrCriticalSection	rtrCritSec(&m_critsec);
	HRESULT	hr = hrOK;
	COM_PROTECT_TRY
	{
		m_AdviseList.NotifyChange(ROUTER_REFRESH, 0, 0);
	}
	COM_PROTECT_CATCH;
	return hr;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::UnadviseRefresh
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
STDMETHODIMP RouterRefreshObject::UnadviseRefresh(LONG_PTR dwConnection)
{
	RtrCriticalSection	rtrCritSec(&m_critsec);
	HRESULT	hr = hrOK;
	COM_PROTECT_TRY
	{
		hr = m_AdviseList.RemoveConnection(dwConnection);
	}
	COM_PROTECT_CATCH;
	return hr;
}

/*!--------------------------------------------------------------------------
	RouterRefreshObject::ExecuteRefresh
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
void RouterRefreshObject::ExecuteRefresh()
{
	SPITFSQueryObject	spQuery;
	RouterRefreshQueryObject *	pQuery;

//  Trace0("Refresh started\n");
	
	if (m_fInRefresh)
		return;
	
	m_fInRefresh = TRUE;
	
	// Create the Query Object
	pQuery = new RouterRefreshQueryObject;
	spQuery = pQuery;

	pQuery->Init(&m_listElements);

	// Need to start the background thread
	Verify( StartBackgroundThread(NULL, m_hWndSync, spQuery) );
	
}

HRESULT RouterRefreshObject::OnNotifyError(LPARAM lParam)
{
	return hrOK;
}

HRESULT RouterRefreshObject::OnNotifyHaveData(LPARAM lParam)
{
	LONG_PTR 							RefreshItemKey  = (LONG_PTR)lParam;
	RouterRefreshQueryElement* 			pCur = NULL;
	SPRouterRefreshQueryElement		 	spPre;
	HRESULT								hr = S_OK;

	// notify every one on the list, till lParam == Key of the refresh item 
	// enumerate and call TryNotify ...
	if (RefreshItemKey)
	{
		do
        {
            pCur = m_listElements.Next(spPre);
            if (pCur)
                pCur->TryNotifyQueryResult();

			spPre.Free();
			spPre = pCur;
		} while(pCur && pCur->GetRefreshItem()->GetKey() != RefreshItemKey);
	}
	return hrOK;
}

HRESULT RouterRefreshObject::OnNotifyExiting(LPARAM lParam)
{
//  Trace0("RouterRefreshObject::OnNotifyExiting()\n");
	
	// need to do the various actions at this point
	// Merge the tree with the already existing tree
	IRouterInfo* 	pRouter = (IRouterInfo*)lParam;
	SPRouterRefreshQueryElement		 	spPre;
	RouterRefreshQueryElement* 			pCur = NULL;
	HRESULT			hr = S_OK;

	// notify every one on the list, till lParam == IRouterInfo* 
	// enumerate and call TryNotify ...
	do
	{
		pCur = m_listElements.Next(spPre);
		if (pCur)
			pCur->TryNotifyQueryResult();

		spPre.Free();
		spPre = pCur;
	} while(pCur);

	// Now notify all of the registered handlers
	NotifyRefresh();
	
	ReleaseThreadHandler();
	WaitForThreadToExit();
		
	m_fInRefresh = FALSE;
	return hrOK;
}


/*!--------------------------------------------------------------------------
	RtrRefreshTimerProc
		This is used by the CTimerMgr as its callback proc.  We then call
		the Refresh code.
	Author: KennT
 ---------------------------------------------------------------------------*/
void CALLBACK RtrRefreshTimerProc(HWND hWnd, UINT uMsg, UINT_PTR nIdEvent,
								  DWORD dwTime)
{
	RtrCriticalSection	rtrCritSec(&g_timerMgr.m_critsec);

	CTimerDesc *pDesc = g_timerMgr.GetTimerDesc(nIdEvent);
	if (pDesc)
	{
		(*(pDesc->refreshProc))(pDesc->lParam, dwTime);
	}		
}



/*---------------------------------------------------------------------------
	Global variable:	g_timerMgr
 ---------------------------------------------------------------------------*/
CTimerMgr	g_timerMgr;

DEBUG_DECLARE_INSTANCE_COUNTER(CTimerMgr);


/*!--------------------------------------------------------------------------
	CTimerMgr::CTimerMgr
		-
	Author: EricDav
 ---------------------------------------------------------------------------*/
CTimerMgr::CTimerMgr()
{
	InitializeCriticalSection(&m_critsec);
}

/*!--------------------------------------------------------------------------
	CTimerMgr::~CTimerMgr
		-
	Author: EricDav
 ---------------------------------------------------------------------------*/
CTimerMgr::~CTimerMgr()
{
    CTimerDesc * pTimerDesc;

    for (int i = GetUpperBound(); i >= 0; --i)
    {
        pTimerDesc = GetAt(i);
        if (pTimerDesc->uTimerId != 0)
            FreeTimer(i);

        delete pTimerDesc;
    }
	DeleteCriticalSection(&m_critsec);
}

/*!--------------------------------------------------------------------------
	CTimerMgr::AllocateTimer
		-
	Author: EricDav
 ---------------------------------------------------------------------------*/
int
CTimerMgr::AllocateTimer
(
	REFRESHPROC		RefreshProc,
	LPARAM			lParam,
	UINT			uTimerInterval
)
{
	RtrCriticalSection	rtrCritSec(&m_critsec);

    CTimerDesc * pTimerDesc = NULL;

    // look for an empty slot
    for (int i = GetUpperBound(); i >= 0; --i)
    {
        pTimerDesc = GetAt(i);
        if (pTimerDesc->uTimerId == 0)
            break;
    }

    // did we find one?  if not allocate one
    if (i < 0)
    {
        pTimerDesc = new CTimerDesc;
		pTimerDesc->lParam = 0;
		pTimerDesc->uTimerInterval = 0;
		pTimerDesc->refreshProc = NULL;
		pTimerDesc->uTimerId = 0;
		
        Add(pTimerDesc);
        i = GetUpperBound();
    }
    
    pTimerDesc->uTimerId = SetTimer(NULL, 0, uTimerInterval, RtrRefreshTimerProc);
    if (pTimerDesc->uTimerId == 0)
        return -1;

	pTimerDesc->lParam = lParam;
	pTimerDesc->uTimerInterval = uTimerInterval;
	pTimerDesc->refreshProc = RefreshProc;
 
    return i;
}

/*!--------------------------------------------------------------------------
	CTimerMgr::FreeTimer
		-
	Author: EricDav
 ---------------------------------------------------------------------------*/
void 
CTimerMgr::FreeTimer
(
    int uEventId
)
{
	RtrCriticalSection	rtrCritSec(&m_critsec);

    CTimerDesc * pTimerDesc;

    Assert(uEventId <= GetUpperBound());
    if (uEventId > GetUpperBound())
        return;

    pTimerDesc = GetAt(uEventId);
    ::KillTimer(NULL, pTimerDesc->uTimerId);

	pTimerDesc->lParam = 0;
	pTimerDesc->uTimerId = 0;
	pTimerDesc->uTimerInterval = 0;
	pTimerDesc->refreshProc = NULL;
}

/*!--------------------------------------------------------------------------
	CTimerMgr::GetTimerDesc
		-
	Author: EricDav
 ---------------------------------------------------------------------------*/
CTimerDesc *
CTimerMgr::GetTimerDesc
(
    INT_PTR uTimerId
)
{
	RtrCriticalSection	rtrCritSec(&m_critsec);

    CTimerDesc * pTimerDesc;

    for (int i = GetUpperBound(); i >= 0; --i)
    {
        pTimerDesc = GetAt(i);
        if (pTimerDesc->uTimerId == (UINT) uTimerId)
            return pTimerDesc;
    }

    return NULL;
}

/*!--------------------------------------------------------------------------
	CTimerMgr::ChangeInterval
		-
	Author: EricDav
 ---------------------------------------------------------------------------*/
void
CTimerMgr::ChangeInterval
(
    int     uEventId,
    UINT    uNewInterval
)
{
	RtrCriticalSection	rtrCritSec(&m_critsec);

    Assert(uEventId <= GetUpperBound());
    if (uEventId > GetUpperBound())
        return;

    CTimerDesc   tempTimerDesc;
    CTimerDesc * pTimerDesc;

    pTimerDesc = GetAt(uEventId);

    // kill the old timer
    ::KillTimer(NULL, pTimerDesc->uTimerId);

    // set a new one with the new interval
    pTimerDesc->uTimerId = ::SetTimer(NULL, 0, uNewInterval, RtrRefreshTimerProc);
}



/*---------------------------------------------------------------------------
	RouterRefreshQueryObject implementation
 ---------------------------------------------------------------------------*/

DEBUG_DECLARE_INSTANCE_COUNTER(RouterRefreshQueryObject);

RouterRefreshQueryObject::RouterRefreshQueryObject()
{
}

STDMETHODIMP RouterRefreshQueryObject::Execute()
{
	// for each router info in the queue, run load
	// after each load of router info, post message
	// WM_HIDDENWND_INDEX_HAVEDATA
	SPRouterRefreshQueryElement		 	spPre;
	RouterRefreshQueryElement* 			pCur = NULL;
	HRESULT			hr = S_OK;

	// notify every one on the list, till lParam == IRouterInfo* 
	// enumerate and call TryNotify ...
	Assert(m_plistElements);
	do
	{
		pCur = m_plistElements->Next(spPre);
		if (pCur)
			pCur->DoQuery(m_hHiddenWnd, m_uMsgBase, m_spHandler);

		spPre.Free();
		spPre = pCur;
	}while(pCur);

	return hrFalse;
}

STDMETHODIMP RouterRefreshQueryObject::OnThreadExit()
{
	::PostMessage(m_hHiddenWnd, m_uMsgBase + WM_HIDDENWND_INDEX_EXITING,
				  (WPARAM)(ITFSThreadHandler *)m_spHandler, 0);
//  Trace0("Exiting RouterRefreshQueryObject::Execute()\n");
	return hrOK;
}



//=========================================
// CRouterInfoRefreshItem implementation
//

DEBUG_DECLARE_INSTANCE_COUNTER(CRouterInfoRefreshItem);

/*!--------------------------------------------------------------------------
	CRouterInfoRefreshItem::NotifyQueryResult
		-
	Author: WeiJiang
 ---------------------------------------------------------------------------*/
HRESULT	CRouterInfoRefreshItem::NotifyQueryResult()
{
	// get the flag to see if need to notify, if not return S_FALSE
//  TRACE(_T("\nAUTOREFRESH!!RouterInfoRefreshItem!!Merge on %8x\n"), GetKey());
	// need to do the various actions at this point
	// Merge the tree with the already existing tree
	HRESULT hr = S_OK;
	m_cs.Lock();

    COM_PROTECT_TRY
    {
        hr = m_pRouter->Merge(m_spRouterNew);
        m_spRouterNew->DoDisconnect();
    }
    COM_PROTECT_CATCH;
    
	m_cs.Unlock();

	return hr;
};


/*!--------------------------------------------------------------------------
	CRouterInfoRefreshItem::DoQuery
		-
	Author: WeiJiang
 ---------------------------------------------------------------------------*/
HRESULT	CRouterInfoRefreshItem::DoQuery(HWND hwndHidden, UINT uMsgBase, ITFSThreadHandler* pHandler)	// this happens in background worker thread
{
//  TRACE(_T("\nAUTOREFRESH!!RouterInfoRefreshItem!!Do query on %8x\n"), GetKey());
	// create new RouterInfo, if newRouterInfo is NULL
	// Create the dummy router info
	HRESULT	hr = S_OK;

	m_cs.Lock();

    COM_PROTECT_TRY
    {
		if (!m_spRouterNew)
			hr = CreateRouterInfo(&m_spRouterNew, NULL, m_pRouter->GetMachineName());

		// do query on newRouterInfo
		Assert(m_pRouter);
		if (hr == S_OK)
        {
			TransferCredentials ( m_pRouter, m_spRouterNew );
			m_pRouter->DoDisconnect();
			hr = m_spRouterNew->Load(m_pRouter->GetMachineName(), NULL);

        }
    }
    COM_PROTECT_CATCH;

	m_cs.Unlock();
	
	return hr; 
};


//=========================================
// CMachineNodeDataRefreshItem implementation

DEBUG_DECLARE_INSTANCE_COUNTER(CStatusNodeRefreshItem);

CStatusNodeRefreshItem::CStatusNodeRefreshItem(DomainStatusHandler* pStatusHandler, ITFSNode *pServerNode)
		: 	m_pNode(pServerNode) , 
			m_pStatusHandler(pStatusHandler),
			m_pData(NULL)
{ 
	Assert(pStatusHandler);
	Assert(pServerNode);

	DMVNodeData     *pData;
	MachineNodeData *pMachineData;
    
    pData = GET_DMVNODEDATA(m_pNode);
    Assert(pData);
	pMachineData = pData->m_spMachineData;
	Assert(pMachineData);

	m_strMachineName = pMachineData->m_stMachineName;
}


CStatusNodeRefreshItem::~CStatusNodeRefreshItem()
{ 
	TerminateBlockingThread();
	if (m_pData)
	{
		m_pData->Release();
		m_pData = NULL;
	}
};


HRESULT	CStatusNodeRefreshItem::NotifyQueryResult()
{
	// get the flag to see if need to notify, if not return S_FALSE
//  TRACE(_T("\nAUTOREFRESH!!RouterInfoRefreshItem!!Sync node data on %8x\n"), GetKey());
	HRESULT hr = S_OK;
	
	m_cs.Lock();
    
    COM_PROTECT_TRY
    {
		// set the new node data
		DMVNodeData     *pData;
    	pData = GET_DMVNODEDATA(m_pNode);
	
		hr = pData->MergeMachineNodeData(m_pData);
		if (hr == S_OK)
			hr = m_pStatusHandler->UpdateSubItemUI(m_pNode);
    }
    COM_PROTECT_CATCH;

	m_cs.Unlock();

	// ssync on the node

	return hr;
};

HRESULT	CStatusNodeRefreshItem::DoQuery(HWND hwndHidden, UINT uMsgBase, ITFSThreadHandler* pHandler)	// this happens in background worker thread
{
//  TRACE(_T("\nAUTOREFRESH!!RouterInfoRefreshItem!!Do query on %8x\n"), GetKey());

	// create a new machine node data, load informaiton, 
	HRESULT	hr = S_OK;

	m_cs.Lock();

    COM_PROTECT_TRY
    {
        if (!m_pData)
        {
            m_pData = new MachineNodeData;
            m_pData->Init(m_strMachineName);
        }
        m_pData->Load();
    }
    COM_PROTECT_CATCH;

    m_cs.Unlock();

	return hr; 
};

//=========================================
// RouterRefreshQueryElement implementation

DEBUG_DECLARE_INSTANCE_COUNTER(RouterRefreshQueryElement);

HRESULT RouterRefreshQueryElement::SetRefreshItem(RefreshItem* pItem)
{
	if (m_cs.Lock() == 0) 	return E_FAIL;

	m_pItem = pItem;

	m_cs.Unlock();
	return S_OK;
};

RefreshItem* RouterRefreshQueryElement::GetRefreshItem()
{	
	RefreshItem* 	pItem;
	m_cs.Lock();

	pItem = m_pItem;

	m_cs.Unlock();
	return pItem;
};


/*!--------------------------------------------------------------------------
	RouterRefreshQueryElement::TryNotifyQueryResult
        to detect if the query done, yet to Notify
	Author: WeiJiang
 ---------------------------------------------------------------------------*/
HRESULT	RouterRefreshQueryElement::TryNotifyQueryResult()	
{
	HRESULT		hr = S_OK;
	RefreshItem*	pItem = NULL;

	// get the flag to see if need to notify, if not return S_FALSE

	if (GetStatus() == RouterQuery_ToNotify)
	{
//  	TRACE(_T("\nAUTOREFRESH!!TryNotifyQueryResult on %8x\n"), m_pItem->GetKey());
		// need to do the various actions at this point
		// Merge the tree with the already existing tree
		pItem = GetRefreshItem();
	}

	if(pItem)
	{
		hr = pItem->NotifyQueryResult();

			// after notify, set the flag, return S_OK
		SetStatus(RouterQuery_NoAction);
	}

	return hr;
};

void RouterRefreshQueryElement::PostNotify(HWND hwndHidden, UINT uMsgBase, ITFSThreadHandler* pHandler)	// this happens in background worker thread
{
	// set ready to notify flag
	SetStatus(RouterQuery_ToNotify);
	// Post done to hidden window
	::PostMessage(hwndHidden, uMsgBase + WM_HIDDENWND_INDEX_HAVEDATA,
		  (WPARAM)pHandler, (LPARAM)m_pItem->GetKey());
}

HRESULT	RouterRefreshQueryElement::DoQuery(HWND hwndHidden, UINT uMsgBase, ITFSThreadHandler* pHandler)	// this happens in background worker thread
{
	HRESULT	hr = S_OK;
	
//  TRACE(_T("\nAUTOREFRESH!!Do query on %8x\n"), m_pItem->GetKey());
	RefreshItem*	pItem = GetRefreshItem();
    
    COM_PROTECT_TRY
    {
		// asked to do query, do it anyway, no matter what's the status

		// set blocking current thread, in case, this query blocks 
		pItem->SetBlockingThread(GetCurrentThread());
		
		hr = pItem->DoQuery(hwndHidden, uMsgBase, pHandler);

		if (hr == S_OK)
		{
			PostNotify(hwndHidden, uMsgBase, pHandler);
		}	

		// it's not blocked, reset it
		pItem->ResetBlockingThread();
	}
    COM_PROTECT_CATCH;

	return hr;
};

RouterRefreshQueryElementList::~RouterRefreshQueryElementList()
{
	POSITION	p = NULL;
	RouterRefreshQueryElement*	pEle = NULL;
	
	m_cs.Lock();
	p = m_list.GetHeadPosition();
	for(p = m_list.GetHeadPosition(); p != NULL; )
	{
		pEle = m_list.GetNext(p);
		pEle->Release();
	}
	m_list.RemoveAll();
	m_cs.Unlock();
}

HRESULT	RouterRefreshQueryElementList::AddRefreshItem(RefreshItem* pItem)	// no ref on IRouterInfo
{
	POSITION	p = NULL;
	RouterRefreshQueryElement*	pE = NULL;
	HRESULT		hr = S_OK;

	m_cs.Lock();
	try{
		for (p = m_list.GetHeadPosition(); p != NULL; )
		{
			pE = m_list.GetNext(p);
            
            // already added, so only addRef
			if (pItem->GetKey() == pE->GetRefreshItem()->GetKey())
			{
				break;
			}
		}

		if (p != NULL)	// found
		{
			pE->AddRef();
			hr = S_FALSE;	// we are not keeping the pItem
		}
		else
		{
			CComObject<RouterRefreshQueryElement>*	pEle = NULL;
			hr = CComObject<RouterRefreshQueryElement>::CreateInstance(&pEle);
            if ( FHrSucceeded(hr) )
            {
                Assert(pEle);
				pEle->SetRefreshItem(pItem);
				pEle->AddRef();
				m_list.AddTail(pEle);
			}
		}
	}
	catch(CMemoryException&)
	{
		hr = E_OUTOFMEMORY;
	}
	catch(...)
	{
		m_cs.Unlock();
		throw;
	}

	m_cs.Unlock();

	
	return hr;
}

HRESULT	RouterRefreshQueryElementList::RemoveRefreshItem(RefreshItem& Item)		// no ref on IRouterInfo
{
	HRESULT	hr = hrOK;
	POSITION	p = NULL;
	POSITION	cp = NULL;
	RouterRefreshQueryElement*	pE = NULL;

	m_cs.Lock();
	try{
		for(p = m_list.GetHeadPosition(); p != NULL; )
		{
			cp = p;
			pE = m_list.GetNext(p);
			if (Item.GetKey() == pE->GetRefreshItem()->GetKey())	// already added, will release on Ele object
			{
				break;
			}
            
            // This is not the one we are looking for.
            cp = NULL;
		}

		if (cp != NULL)	// found
		{
			pE->Release();   //remove from the refresh list
			m_list.RemoveAt(cp);
		}
		else
			hr = S_FALSE;
	}
	catch(...)
	{
		m_cs.Unlock();
		throw;
	}

	m_cs.Unlock();
	
	return hr;
}


RouterRefreshQueryElement* 	RouterRefreshQueryElementList::Next(RouterRefreshQueryElement* pEle)	// AddRef on Ele Object
{
	RouterRefreshQueryElement*	pNext = NULL;
	m_cs.Lock();
	if (pEle == NULL)
	{
		if (m_list.GetCount() != 0)	// asking for the first element
			pNext = m_list.GetHead();
	}
	else
	{
		POSITION		p;
		// find the current one
		for(p = m_list.GetHeadPosition(); p != NULL; )
		{
			if (pEle == m_list.GetNext(p))
			{
				if (p != NULL)
					pNext = m_list.GetAt(p);
				break;
			}
		}
	}

	m_cs.Unlock();

	if (pNext)
		pNext->AddRef();
		
	return pNext;
}