//****************************************************************************
//  Module:     NMCHAT.EXE
//  File:       CLUTIL.CPP
//  Content:    
//              
//
//  Copyright (c) Microsoft Corporation 1997
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE.
//****************************************************************************

#include "precomp.h"


///////////////////////////////////////////////////////////////////////////
// RefCount

/*  R E F  C O U N T  */
/*-------------------------------------------------------------------------
    %%Function: RefCount
    
-------------------------------------------------------------------------*/
RefCount::RefCount(void)
{
	m_cRef = 1;
}


RefCount::~RefCount(void)
{
}


ULONG STDMETHODCALLTYPE RefCount::AddRef(void)
{
   ASSERT(m_cRef >= 0);

   InterlockedIncrement(&m_cRef);

   return (ULONG) m_cRef;
}


ULONG STDMETHODCALLTYPE RefCount::Release(void)
{
	if (0 == InterlockedDecrement(&m_cRef))
	{
		delete this;
		return 0;
	}

	ASSERT(m_cRef > 0);
	return (ULONG) m_cRef;
}



///////////////////////////////////////////////////////////////////////////
// CNotify

/*  C  N O T I F Y  */
/*-------------------------------------------------------------------------
    %%Function: CNotify
    
-------------------------------------------------------------------------*/
CNotify::CNotify() :
	m_pcnpcnt(NULL),
    m_pcnp(NULL),
    m_dwCookie(0),
    m_pUnk(NULL)
{
}

CNotify::~CNotify()
{
	Disconnect(); // Make sure we're disconnected
}


/*  C O N N E C T  */
/*-------------------------------------------------------------------------
    %%Function: Connect

-------------------------------------------------------------------------*/
HRESULT CNotify::Connect(IUnknown *pUnk, REFIID riid, IUnknown *pUnkN)
{
	HRESULT hr;

	ASSERT(0 == m_dwCookie);

	// Get the connection container
	hr = pUnk->QueryInterface(IID_IConnectionPointContainer, (void **)&m_pcnpcnt);
	if (SUCCEEDED(hr))
	{
		// Find an appropriate connection point
		hr = m_pcnpcnt->FindConnectionPoint(riid, &m_pcnp);
		if (SUCCEEDED(hr))
		{
			ASSERT(NULL != m_pcnp);
			// Connect the sink object
			hr = m_pcnp->Advise((IUnknown *)pUnkN, &m_dwCookie);
		}
	}

	if (FAILED(hr))
	{
		ERROR_OUT(("MNMSRVC: CNotify::Connect failed: %x", hr));
		m_dwCookie = 0;
	}
	else
	{
    	m_pUnk = pUnk; // keep around for caller
    }

	return hr;
}



/*  D I S C O N N E C T  */
/*-------------------------------------------------------------------------
    %%Function: Disconnect
    
-------------------------------------------------------------------------*/
HRESULT CNotify::Disconnect (void)
{
    if (0 != m_dwCookie)
    {
        // Disconnect the sink object
        m_pcnp->Unadvise(m_dwCookie);
        m_dwCookie = 0;

        m_pcnp->Release();
        m_pcnp = NULL;

        m_pcnpcnt->Release();
        m_pcnpcnt = NULL;

        m_pUnk = NULL;
    }

    return S_OK;
}



///////////////////////////////////////////////////////////////////////////
// COBLIST


COBLIST::~COBLIST()
{
    ASSERT(IsEmpty());
}


#ifdef DEBUG
VOID* COBLIST::GetHead()
{
	ASSERT(m_pHead);

	return m_pHead->pItem;
}
   
VOID* COBLIST::GetTail()
{
	ASSERT(m_pTail);

	return m_pTail->pItem;
}
#endif /* DEBUG */

VOID* COBLIST::GetNext(POSITION& rPos)
{
	ASSERT(rPos);
	
	VOID* pReturn = rPos->pItem;
	rPos = rPos->pNext;

	return pReturn;
}

VOID* COBLIST::RemoveAt(POSITION Pos)
{
	VOID* pReturn = NULL;

	if (m_pHead)
	{
		if (m_pHead == Pos)
		{
			// Removing the first element in the list
			
			m_pHead = Pos->pNext;
			pReturn = Pos->pItem;
			delete Pos;
			m_cItem--;
			ASSERT(0 <= m_cItem);

			if (NULL == m_pHead)
			{
				// Removing the only element!
				m_pTail = NULL;
			}
		}
		else
		{
			POSITION pCur = m_pHead;

			while (pCur && pCur->pNext)
			{
				if (pCur->pNext == Pos)
				{
					// Removing 
					
					pCur->pNext = Pos->pNext;
					if (m_pTail == Pos)
					{
						m_pTail = pCur;
					}
					pReturn = Pos->pItem;
					delete Pos;

					m_cItem--;
					ASSERT(0 <= m_cItem);
				}

				pCur = pCur->pNext;
			}
		}
	}

	return pReturn;
}

POSITION COBLIST::AddTail(VOID* pItem)
{
	POSITION posRet = NULL;

	if (m_pTail)
	{
		if (m_pTail->pNext = new COBNODE)
		{
			m_pTail = m_pTail->pNext;
			m_pTail->pItem = pItem;
			m_pTail->pNext = NULL;
			m_cItem++;
		}
	}
	else
	{
		ASSERT(!m_pHead);
		if (m_pHead = new COBNODE)
		{
			m_pTail = m_pHead;
			m_pTail->pItem = pItem;
			m_pTail->pNext = NULL;
			m_cItem++;
		}
	}

	return m_pTail;
}

void COBLIST::EmptyList()
{
    while (!IsEmpty()) {
        RemoveAt(GetHeadPosition());
    }
}


#ifdef DEBUG
VOID* COBLIST::RemoveTail()
{
	ASSERT(m_pHead);
	ASSERT(m_pTail);
	
	return RemoveAt(m_pTail);
}

VOID* COBLIST::RemoveHead()
{
	ASSERT(m_pHead);
	ASSERT(m_pTail);
	
	return RemoveAt(m_pHead);
}

void * COBLIST::GetFromPosition(POSITION Pos)
{
    void * Result = SafeGetFromPosition(Pos);
	ASSERT(Result);
	return Result;
}
#endif /* DEBUG */

POSITION COBLIST::GetPosition(void* _pItem)
{
    POSITION    Position = m_pHead;

    while (Position) {
        if (Position->pItem == _pItem) {
            break;
        }
		GetNext(Position);
    }
    return Position;
}

POSITION COBLIST::Lookup(void* pComparator)
{
    POSITION    Position = m_pHead;

    while (Position) {
        if (Compare(Position->pItem, pComparator)) {
            break;
        }
		GetNext(Position);
    }
    return Position;
}

void * COBLIST::SafeGetFromPosition(POSITION Pos)
{
	// Safe way to validate that an entry is still in the list,
	// which ensures bugs that would reference deleted memory,
	// reference a NULL pointer instead
	// (e.g. an event handler fires late/twice).
	// Note that versioning on entries would provide an additional 
	// safeguard against re-use of a position.
	// Walk	list to find entry.

	POSITION PosWork = m_pHead;
	
	while (PosWork) {
		if (PosWork == Pos) {
			return Pos->pItem;
		}
		GetNext(PosWork);
	}
	return NULL;
}

/////////////////////////////
// COBLIST Utility routines

/*  A D D  N O D E  */
/*-------------------------------------------------------------------------
    %%Function: AddNode

    Add a node to a list.
    Initializes the ObList, if necessary.
    Returns the position in the list or NULL if there was a problem.
-------------------------------------------------------------------------*/
POSITION AddNode(PVOID pv, COBLIST ** ppList)
{
	ASSERT(NULL != ppList);
	if (NULL == *ppList)
	{
		*ppList = new COBLIST();
		if (NULL == *ppList)
			return NULL;
	}

	return (*ppList)->AddTail(pv);
}


/*  R E M O V E  N O D E  */
/*-------------------------------------------------------------------------
    %%Function: RemoveNode

    Remove a node from a list.
    Sets pPos to NULL
-------------------------------------------------------------------------*/
PVOID RemoveNode(POSITION * pPos, COBLIST *pList)
{
	if ((NULL == pList) || (NULL == pPos))
		return NULL;

	PVOID pv = pList->RemoveAt(*pPos);
	*pPos = NULL;
	return pv;
}


////////////////////////////////////////////////////////////////////////////
// BSTRING

// We don't support construction from an ANSI string in the Unicode build.
#if !defined(UNICODE)

BSTRING::BSTRING(LPCSTR lpcString)
{
	m_bstr = NULL;

	// Compute the length of the required BSTR, including the null
	int cWC =  MultiByteToWideChar(CP_ACP, 0, lpcString, -1, NULL, 0);
	if (cWC <= 0)
		return;

	// Allocate the BSTR, including the null
	m_bstr = SysAllocStringLen(NULL, cWC - 1); // SysAllocStringLen adds another 1

	ASSERT(NULL != m_bstr);
	if (NULL == m_bstr)
	{
		return;
	}

	// Copy the string
	MultiByteToWideChar(CP_ACP, 0, lpcString, -1, (LPWSTR) m_bstr, cWC);

	// Verify that the string is null terminated
	ASSERT(0 == m_bstr[cWC - 1]);
}

#endif // !defined(UNICODE)


///////////////////////////
// BTSTR

BTSTR::BTSTR(BSTR bstr)
{
	m_psz = PszFromBstr(bstr);
}

BTSTR::~BTSTR()
{
	if (NULL != m_psz)
		LocalFree(m_psz);
}

LPTSTR PszFromBstr(BSTR bstr)
{
	if (NULL == bstr)
		return NULL;
	int cch =  WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, NULL, 0, NULL, NULL);
	if (cch <= 0)
		return NULL;

	LPTSTR psz = (LPTSTR)LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * (cch+1) );
	if (NULL == psz)
		return NULL;

	WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, psz, cch+1, NULL, NULL);
	return psz;
}


/*  B  S  T  R _ T O _  L  P  T  S  T  R  */
/*-------------------------------------------------------------------------
    %%Function: BSTR_to_LPTSTR
    
-------------------------------------------------------------------------*/
HRESULT BSTR_to_LPTSTR(LPTSTR *ppsz, BSTR bstr)
{
#ifndef UNICODE
	// compute the length of the required BSTR
	int cch =  WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, NULL, 0, NULL, NULL);
	if (cch <= 0)
	{
		ERROR_OUT(("WideCharToMultiByte failed"));
		return E_FAIL;
	}

	// cch is the number of BYTES required, including the null terminator
	*ppsz = (LPTSTR) new char[cch];
	if (*ppsz == NULL)
	{
		ERROR_OUT(("WideCharToMultiByte out of memory"));
		return E_OUTOFMEMORY;
	}

	WideCharToMultiByte(CP_ACP, 0, (LPWSTR)bstr, -1, *ppsz, cch, NULL, NULL);
	return S_OK;
#else
	return E_NOTIMPL;
#endif // UNICODE
}

/////////////////////////////////////////////////////////////////////////////
// Connection Point Helpers

HRESULT NmAdvise(IUnknown* pUnkCP, IUnknown* pUnk, const IID& iid, LPDWORD pdw)
{
	IConnectionPointContainer *pCPC;
	IConnectionPoint *pCP;
	HRESULT hRes = pUnkCP->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);
	if (SUCCEEDED(hRes))
	{
		hRes = pCPC->FindConnectionPoint(iid, &pCP);
		pCPC->Release();
	}
	if (SUCCEEDED(hRes))
	{
		hRes = pCP->Advise(pUnk, pdw);
		pCP->Release();
	}
	return hRes;
}

HRESULT NmUnadvise(IUnknown* pUnkCP, const IID& iid, DWORD dw)
{
	IConnectionPointContainer *pCPC;
	IConnectionPoint *pCP;
	HRESULT hRes = pUnkCP->QueryInterface(IID_IConnectionPointContainer, (void**)&pCPC);
	if (SUCCEEDED(hRes))
	{
		hRes = pCPC->FindConnectionPoint(iid, &pCP);
		pCPC->Release();
	}
	if (SUCCEEDED(hRes))
	{
		hRes = pCP->Unadvise(dw);
		pCP->Release();
	}
	return hRes;
}