/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/util.h $
 * $Revision: /main/ms-d/15 $
 *     $Date: 2013-03-15 14:09:02 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/util.h$ 
 * 
 * /main/ms-d/15   2013-03-15 14:09:02   pmackent 
 * Start introducing Java update for EHS5
 *
 * /main/ms-d/14   2012-02-09 16:44:21   ctna0256
 * Check for missed SMS with AT+CRSM on EHS5, small bug fixes
 *
 * /main/ms-d/13   2012-01-05 10:26:58   ctna0256
 * Save PIN2 because of problem with AT+CLCK="FD" in PH8 Rel2 (IPIS100076746)
 *
 * /main/ms-d/12   2011-09-26 13:35:33   ctna0256
 * Comply with CE6 warning level 4
 *
 * /main/ms-d/11   2011-02-01 14:49:20   ctna0256
 * Allow combination of resilience and CSQ notification mode
 *
 * /main/ms-d/10   2010-12-15 10:44:12   ctna0256
 * Performed lint check
 *
 * /main/ms-d/9   2010-07-02 11:05:28   ctna0256
 * New handling of Customer SIM lock for HC25 R2 (IPIS100065826)
 *
 * /main/ms-d/8   2010-02-03 10:51:24   ctna0256
 * Introduction of WinCE6 support
 *
 * /main/ms-d/7   2008-09-18 15:21:46   ctna0256
 * Utility function to delete trailing commas
 *
 * /main/ms-d/6   2008-08-26 14:27:07   ctna0256
 * Merged RIL_RADIO_RESILIENCE option from Classic-RIL
 *
 * /main/ms-d/5   2008-08-22 10:28:24   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/4   2008-08-21 09:35:46   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/3   2008-04-03 14:42:06   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/2   2006-11-13 10:05:07   mp49210
 * New PIN2 handling in Quinn
 *
 * /main/ms-d/1   2006-10-25 16:52:30   wm36264
 * First transformation from classic RIL
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Gemalto M2M GmbH
// Copyright (C) 2006-2008 Siemens AG
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

/*++
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.
Copyright (c) 1995-1999  Microsoft Corporation
--*/

#pragma once

//
// Character mapping structure used for binary search
//
struct CHARMAP {
    char ch;
    WCHAR wch;
};

//
// Encoding types
//
enum ENCODING_TYPE {
    ENCODING_GSMDEFAULT = 0,// GSM Default alphabet
    ENCODING_GSMDEFAULT_HEX, // GSM Default alphabet, HEX encoded
    // Others to be added later
    ENCODING_UCS2 = 0x8000,
    ENCODING_ASCII
};
#define ENCODING_TECHARSET ENCODING_GSMDEFAULT


// Macro to round value to DWORD boundary (used to meet ARM memory alignment requirements)
#define ROUNDTODWORD(p)     (DWORD)(4 * (((DWORD) p + 3) / 4))

//
// Function declarations
//
BOOL FindString(DWORD dwStartPos, const char *pszString1, const char *pszString2, UINT uiLen, PDWORD pdwEndPos);
void ExtRILRetailTraceZone(DWORD dwZone, LPCTSTR szText, LPCTSTR szIn, LPCSTR szCrLf);
BOOL UnicodeCharToGSM(const WCHAR wch, char *pchRet);
BOOL GSMCharToUnicode(const char ch, WCHAR *pwchRet);
char *DeleteTrailingCommas(char *szEnd, char *szStart);
char SemiByteToChar(const BYTE bByte, const BOOL fHigh);
BYTE SemiByteCharsToByte(const char chHigh, const char chLow);
BOOL GSMHexToGSM(const LPCSTR sIn, const UINT cbIn, const LPSTR sOut, const UINT cbOut, UINT& rcbUsed);
BOOL GSMToGSMHex(const LPCSTR sIn, const UINT cbIn, const LPSTR sOut, const UINT cbOut, UINT& rcbUsed);
BOOL ConvertFromUnicode(const ENCODING_TYPE enc, const LPCWSTR wsIn, const UINT cchIn, const LPSTR sOut, const UINT cbOut,
                        UINT& rcbUsed);
BOOL ConvertToUnicode(const ENCODING_TYPE enc, const LPCSTR sIn, const UINT cbIn, const LPWSTR wsOut, const UINT cchOut,
                      UINT& rcchUsed);
HRESULT RILAddressToString(const RILADDRESS& rraAddress, const LPSTR szOut, const UINT cbOut, BYTE& rbTypeOfAddress);
BOOL StringToRILAddress(const LPCSTR szAddress, const BYTE bTypeOfAddress, RILADDRESS& rraAddress);
//HRESULT RILSubAddressToString(const RILSUBADDRESS& rrsaSubAddress, const LPSTR szOut, const UINT cbOut, BYTE& rbType);
BOOL StringToRILSubAddress(const LPCSTR szSubAddress, const BYTE bType, RILSUBADDRESS& rrsaSubAddress);
HRESULT DetermineSimResponseError(DWORD dwSW1, DWORD dwSW2);
BOOL ComposeCmdWithByteArray(const LPCSTR szPrefix, const BYTE* const pbBytes, const UINT cbBytes,
                             const LPCSTR szPostfix, LPSTR& rszCmd);
BOOL AllocateOrReallocateStorage(BYTE** const prgrData, const size_t stSize, const UINT nUsed, UINT* const pnAllocated,
                                 const UINT nGrowSize);
void UpdateStatStore(void);
void UpdateSIMState(DWORD dwSimState);

BOOL UCS2StringToRILAddress(const LPWSTR wszAddress, const BYTE bTypeOfAddress, RILADDRESS& rraAddress);
HRESULT RILAddressToUCS2String(const RILADDRESS& rraAddress, const LPWSTR wszOut, const UINT cbOut, BYTE& rbTypeOfAddress);
BOOL UCS2ToGSMHex(const LPWSTR pwszIn, const UINT cbIn, const LPSTR sOut, const UINT cbOut);

void ClearSIMLockPINSecure(void);
void SaveSIMLockPINSecure(LPCSTR szNewPIN);
BOOL FetchSIMLockPINSecure(LPSTR szPinBuffer, DWORD cbBufferSIze);
#ifdef RIL_RADIO_RESILIENCE
void ClearPINSecure();
void SavePINSecure(LPCSTR szNewPIN);
BOOL FetchPINSecure(LPSTR szPinBuffer, DWORD cbBufferSIze);
#endif
void ClearPIN2Secure();
void SavePIN2Secure(LPCSTR szNewPIN2);
BOOL FetchPIN2Secure(LPSTR szPinBuffer, DWORD cbBufferSIze);

DWORD CalcCrc(BYTE *bBuffer, DWORD dwLen);

//
// Class declarations
//

// Growable character buffer
class CBuffer
{
public:
            CBuffer();
            ~CBuffer();

    BOOL    Append(const LPCSTR szString, const UINT cbString);
    LPSTR   GetData(void) const     { return m_szData; };
    UINT    GetLength(void) const   { return m_cbLength; };
    LPSTR   GiveUpData(void);
    void    InheritData(CBuffer* pSrcBuffer);

protected:
    LPSTR   m_szData;
    UINT    m_cbLength;
    UINT    m_cbAlloc;
};


// Doubly-linked list element
class CListElem
{
public:
                CListElem() : m_pNext(NULL), m_pPrev(NULL) {};
                ~CListElem() {};

    CListElem*  GetNext(void) const         { return m_pNext; };
    CListElem*  GetPrev(void) const         { return m_pPrev; };

    void        SetNext(CListElem* p)   { m_pNext = p; };
    void        SetPrev(CListElem* p)   { m_pPrev = p; };

private:
    CListElem*  m_pNext;
    CListElem*  m_pPrev;
};


// Callback used when entering exclusive use mode
typedef void (*PFN_SHRDUSE_ACTION)(DWORD dwParam);


// Shared resource with readers/writers synchronization
class CSharedResource
{
public:
            CSharedResource();
            ~CSharedResource();

    BOOL    Init(const HANDLE hCancelEvent);
    BOOL    EnterSharedUse(void);
    BOOL    ExitSharedUse(void);
    BOOL    EnterExclusiveUse(PFN_SHRDUSE_ACTION pfnAction, const DWORD dwParam) const;
    BOOL    ExitExclusiveUse(void) const;

private:
    BOOL                m_fInited;
    CRITICAL_SECTION    m_cs;
    DWORD               m_dwSharedUsers;
    HANDLE              m_hSharedUseSph;
    HANDLE              m_hExclusiveUseSph;
    HANDLE              m_hCancelEvent;
};


// Function to be passed to CQueue::Enum().
//    This function should return TRUE for enumeration to stop.
typedef BOOL (*PFN_QUEUE_ENUM)(void* pItem, DWORD dwData);

// Function to be passed to CQueue::ConditionalGet().
//    This function should return TRUE for for the item to be removed from the queue.
typedef BOOL (*PFN_QUEUE_TEST)(void* pItem, DWORD dwData);

// Function to be used to destroy items in the queue
// when the queue is destroyed.
typedef void (*PFN_QUEUE_ITEMDTOR)(void *pItem);

template <class Type, UINT Size>
class CBoundedQueue
{
private:
    CEvent              m_eventSpace;
    CUniquelyNamedEvent m_eventItems;
    Type *              m_rgpItems[Size];
    UINT                m_nUsed;
    PFN_QUEUE_ITEMDTOR  m_pfnItemDtor;
    CRITICAL_SECTION    m_cs;

public:
            CBoundedQueue(PFN_QUEUE_ITEMDTOR pfnItemDtor = NULL);
    virtual ~CBoundedQueue();

    BOOL    Ready(void) const { return m_eventSpace.Ready() && m_eventItems.Ready(); }
    BOOL    Init(void);
    BOOL    Put(Type* const pItem);
    BOOL    Get(Type** ppItem);
    BOOL    Peek(Type** ppItem);
    HRESULT ConditionalGet(const PFN_QUEUE_TEST pfnTest, const DWORD dwData, Type** rpItem);

    const
    TCHAR * GetItemsEventName(void) { return m_eventItems.GetName(); }
    HANDLE  GetItemsEvent(void) { return m_eventItems.GetHandle(); }
    HANDLE  GetSpaceEvent(void) { return m_eventSpace.GetHandle(); }
};

template <class Type, UINT Size>
CBoundedQueue<Type, Size>::CBoundedQueue(PFN_QUEUE_ITEMDTOR pfnItemDtor)
:   m_nUsed(0),
    m_pfnItemDtor(pfnItemDtor)
{
    InitializeCriticalSection(&m_cs);

    for (UINT i = 0; i < Size; i++)
        m_rgpItems[i] = NULL;
}

template <class Type, UINT Size>
CBoundedQueue<Type, Size>::~CBoundedQueue()
{
    // In case someone is still waiting on
    // this queue, try to avoid conflicts...
    if (m_eventItems.Ready())
        m_eventItems.Reset();   // no items to dequeue
    if (m_eventSpace.Ready())
        m_eventSpace.Reset();   // no space for enqueue

    if (m_pfnItemDtor)
    {
        for (UINT i = 0; i < m_nUsed; i++)
        {
            m_pfnItemDtor(m_rgpItems[i]);
            m_rgpItems[i] = NULL;
        }
    }

    m_nUsed = 0;

    DeleteCriticalSection(&m_cs);
}

template <class Type, UINT Size>
BOOL CBoundedQueue<Type, Size>::Init()
{
    return
        m_eventSpace.Init(TRUE, TRUE) &&
        m_eventItems.Init(TRUE, FALSE, TEXT("CBoundedQueue_"));
}

template <class Type, UINT Size>
BOOL CBoundedQueue<Type, Size>::Put(Type * const pItem)
{
    ASSERT(Ready());

    SYNCBLOCK(m_cs);

    if (m_nUsed < Size)
    {
        // Put the element in our queue.
        m_rgpItems[m_nUsed++] = pItem;

        // If this filled the queue buffer,
        // then reset the space avail event.
        if (m_nUsed == Size)
            m_eventSpace.Reset();

        // There's something new in the queue,
        // so signal waiting threads.
        m_eventItems.Set();

        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

template <class Type, UINT Size>
BOOL CBoundedQueue<Type, Size>::Get(Type ** ppItem)
{
    return ConditionalGet(NULL, 0, ppItem);
}

template <class Type, UINT Size>
BOOL CBoundedQueue<Type, Size>::Peek(Type ** ppItem)
{
    ASSERT(Ready());

    ASSERT(ppItem);
    if (!ppItem)
        return FALSE;

    SYNCBLOCK(m_cs);

    if (m_nUsed > 0)
    {
        // Copy the element from the queue.
        *ppItem = m_rgpItems[0];

        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

template <class Type, UINT Size>
HRESULT CBoundedQueue<Type, Size>::ConditionalGet(const PFN_QUEUE_TEST pfnTest,
                                                  const DWORD dwData,
                                                  Type** ppItem)
{
    ASSERT(Ready());

    ASSERT(ppItem);
    if (!ppItem)
        return S_FALSE;

    SYNCBLOCK(m_cs);

    if (m_nUsed > 0)
    {
        // Copy the element from the queue.
        Type * pItem = m_rgpItems[0];

        // If there is a condition, make sure
        // it is satisfied.
        if (pfnTest && !pfnTest(pItem, dwData))
            return E_FAIL;

        // One less in queue now...
        m_nUsed--;

        if (m_nUsed)
        {
            // Slide remaining items over to the beginning of the buffer.
            memmove(m_rgpItems, (BYTE*)m_rgpItems + sizeof(Type*), sizeof(Type*) * m_nUsed);

            // If we just made space, then set this event.
            if (Size - 1 == m_nUsed)
                m_eventSpace.Set();
        }
        else
        {
            // The last item was copied from the queue,
            // so reset this event.
            m_eventItems.Reset();
        }

        // NULL out the space that was freed up.
        m_rgpItems[m_nUsed] = NULL;

        // Set the output parameter.
        *ppItem = pItem;

        return S_OK;
    }
    else
    {
        return S_FALSE;
    }
}

template <class Type>
class CUnboundedQueue
{
private:
    CUniquelyNamedEvent m_eventItems;
    Type **             m_rgpItems;
    DWORD               m_dwInitialSize;
    DWORD               m_dwCurrentSize;
    UINT                m_nUsed;
    PFN_QUEUE_ITEMDTOR  m_pfnItemDtor;
    CRITICAL_SECTION    m_cs;

protected:
    BOOL    GrowQueue(void);

public:
            CUnboundedQueue(PFN_QUEUE_ITEMDTOR pfnItemDtor = NULL);
    virtual ~CUnboundedQueue();

    BOOL    Ready(void) const { return m_rgpItems != NULL && m_eventItems.Ready(); }
    BOOL    Init(DWORD dwInitialSize);
    BOOL    Put(Type* const pItem);
    BOOL    Get(Type** ppItem);
    BOOL    Peek(Type** ppItem);
    HRESULT ConditionalGet(const PFN_QUEUE_TEST pfnTest, const DWORD dwData, Type** rpItem);

    const
    TCHAR * GetItemsEventName(void) { return m_eventItems.GetName(); }
    HANDLE  GetItemsEvent(void) { return m_eventItems.GetHandle(); }

    UINT    GetSize(void) { return m_nUsed; }
    DWORD   GetCurrentBufferSize(void) { return m_dwCurrentSize; }
};

template <class Type>
CUnboundedQueue<Type>::CUnboundedQueue(PFN_QUEUE_ITEMDTOR pfnItemDtor)
:   m_rgpItems(NULL),
    m_dwInitialSize(0),
    m_dwCurrentSize(0),
    m_nUsed(0),
    m_pfnItemDtor(pfnItemDtor)
{
    InitializeCriticalSection(&m_cs);
}

template <class Type>
CUnboundedQueue<Type>::~CUnboundedQueue()
{
    // In case someone is still waiting on
    // this queue, try to avoid conflicts...
    if (m_eventItems.Ready())
        m_eventItems.Reset();   // no items to dequeue

    if (m_rgpItems)
    {
        if (m_pfnItemDtor)
        {
            for (UINT i = 0; i < m_nUsed; i++)
            {
                m_pfnItemDtor(m_rgpItems[i]);
                m_rgpItems[i] = NULL;
            }
        }

        delete[] m_rgpItems;
    }

    m_nUsed = 0;

    DeleteCriticalSection(&m_cs);
}

template <class Type>
BOOL CUnboundedQueue<Type>::Init(DWORD dwInitialSize)
{
    if (m_rgpItems || dwInitialSize < 1)
        return FALSE;

    if (!m_eventItems.Init(TRUE, FALSE, TEXT("CUnboundedQueue_")))
        return FALSE;

    m_rgpItems = new Type*[dwInitialSize];
    if (!m_rgpItems)
        return FALSE;

    m_dwInitialSize = dwInitialSize;
    m_dwCurrentSize = dwInitialSize;

    return TRUE;
}

template <class Type>
BOOL CUnboundedQueue<Type>::GrowQueue()
{
    ASSERT(Ready());

    DWORD dwNewSize = m_dwCurrentSize + m_dwInitialSize;

    Type ** rgpNewBuffer = new Type*[dwNewSize];
    if (!rgpNewBuffer)
        return FALSE;

    memcpy(rgpNewBuffer, m_rgpItems, sizeof(Type*) * m_nUsed);

    delete[] m_rgpItems;

    m_rgpItems = rgpNewBuffer;
    m_dwCurrentSize = dwNewSize;

    return TRUE;
}

template <class Type>
BOOL CUnboundedQueue<Type>::Put(Type * const pItem)
{
    ASSERT(Ready());

    SYNCBLOCK(m_cs);

    ASSERT(m_nUsed <= m_dwCurrentSize);

    if (m_nUsed >= m_dwCurrentSize)
    {
        if (!GrowQueue())
            return FALSE;
    }

    ASSERT(m_nUsed < m_dwCurrentSize);

    // Put the element in our queue.
    m_rgpItems[m_nUsed++] = pItem;

    // There's something new in the queue,
    // so signal waiting threads.
    m_eventItems.Set();

    return TRUE;
}

template <class Type>
BOOL CUnboundedQueue<Type>::Get(Type ** ppItem)
{
    return ConditionalGet(NULL, 0, ppItem);
}

template <class Type>
BOOL CUnboundedQueue<Type>::Peek(Type ** ppItem)
{
    ASSERT(Ready());

    ASSERT(ppItem);
    if (!ppItem)
        return FALSE;

    SYNCBLOCK(m_cs);

    if (m_nUsed > 0)
    {
        // Copy the element from the queue.
        *ppItem = m_rgpItems[0];

        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

template <class Type>
HRESULT CUnboundedQueue<Type>::ConditionalGet(const PFN_QUEUE_TEST pfnTest,
                                              const DWORD dwData,
                                              Type** ppItem)
{
    ASSERT(Ready());

    ASSERT(ppItem);
    if (!ppItem)
        return S_FALSE;

    SYNCBLOCK(m_cs);

    if (m_nUsed > 0)
    {
        // Copy the element from the queue.
        Type * pItem = m_rgpItems[0];

        // If there is a condition, make sure
        // it is satisfied.
        if (pfnTest && !pfnTest(pItem, dwData))
            return E_FAIL;

        // One less in queue now...
        m_nUsed--;

        if (m_nUsed)
        {
            // Slide remaining items over to the beginning of the buffer.
            memmove(m_rgpItems, (BYTE*)m_rgpItems + sizeof(Type*), sizeof(Type*) * m_nUsed);
        }
        else
        {
            // The last item was copied from the queue,
            // so reset this event.
            m_eventItems.Reset();
        }

        // NULL out the space that was freed up.
        m_rgpItems[m_nUsed] = NULL;

        // Set the output parameter.
        *ppItem = pItem;

        return S_OK;
    }
    else
    {
        return S_FALSE;
    }
}

// Generic queue
template <class Type, UINT Size>
class CQueue
{
public:
                CQueue(const BOOL fDontCallDestructors = FALSE);
    virtual     ~CQueue();

    BOOL        Init(const HANDLE hEvent);
    BOOL        Put(Type* const pItem, const DWORD dwTimeout);
    HRESULT     Get(Type*& rpItem, const DWORD dwTimeout);
    BOOL        Peek(Type*& rpItem);
    HRESULT     ConditionalGet(const PFN_QUEUE_TEST pfnTest, const DWORD dwData, Type*& rpItem,
                               const DWORD dwTimeout);
    HRESULT     WaitForNextItem(const DWORD dwTimeout);
    HANDLE      GetPutEvent(void) { return m_hPutEvent; }
    void        Enum(const PFN_QUEUE_ENUM pfnEnum, const DWORD dwData, const BOOL fClear);
    BOOL        FEmpty(void);
    BOOL        SetReadLock(BOOL fLock);

protected:
    HRESULT     GetInternal(Type*& rpItem, const DWORD dwTimeout);
    BOOL        PeekInternal(Type*& rpItem) const;
    BOOL        WaitForEmptySpaceInternal(const DWORD dwTimeout);
    HRESULT     WaitForNextItemInternal(const DWORD dwTimeout);

    Type*               m_rgpItems[Size];
    UINT                m_nUsed;
    HANDLE              m_hGetLockEvent;
    HANDLE              m_hGetEvent;
    HANDLE              m_hPutEvent;
    HANDLE              m_hCancelEvent;
    CRITICAL_SECTION    m_cs;
    BOOL                m_fDontCallDestructors;
    BOOL                m_fInited;
};


// Priority queue
template <class Type, UINT Size>
class CPriorityQueue : public CQueue<Type, Size>
{
public:
                 CPriorityQueue();
                 ~CPriorityQueue();

    BOOL         Put(Type* const pItem, const DWORD dwTimeout);
    HRESULT      Get(Type*& rpItem, const DWORD dwTimeout);
    BOOL         SetPriorityLock(const DWORD dwPriority = MAXDWORD);

protected:
    static BOOL  CheckPriority(void* pItem, DWORD dwInstance) { return (((Type*)pItem)->GetPriority() <= ((CPriorityQueue<Type, Size>*)dwInstance)->m_dwPriorityLock); };

    DWORD        m_dwPriorityLock;
};


// Function to be passed to CDblList::Enum().
//    This function should return TRUE for enumeration to stop.
typedef BOOL (*PFN_LIST_ENUM)(void* pItem, DWORD dwData);


// Generic doubly-linked list
template <class Type>
class CDblList
{
public:
            CDblList();
            ~CDblList();

    BOOL    Add(Type* const pAdd);
    BOOL    Remove(const Type* const pRemove);
    void    Enum(const PFN_LIST_ENUM pfnEnum, const DWORD dwData);

private:
    Type*               m_pElems;
    CRITICAL_SECTION    m_cs;
};



//
// Template definitions
//

//
// Queue ctor
//
template <class Type, UINT Size>
CQueue<Type, Size>::CQueue(const BOOL fDontCallDestructors /* = FALSE */)
:  m_nUsed(0),
   m_hGetLockEvent(NULL),
   m_hGetEvent(NULL),
   m_hPutEvent(NULL),
   m_hCancelEvent(NULL),
   m_fDontCallDestructors(fDontCallDestructors),
   m_fInited(FALSE)
{
    // Initialize the critical section
    InitializeCriticalSection(&m_cs);

    for (UINT i = 0; i < Size; i++) {
        m_rgpItems[i] = NULL;
    }
}


//
// Queue dtor
//
template <class Type, UINT Size>
CQueue<Type, Size>::~CQueue()
{
    // Get rid of the events
    if (m_hPutEvent) {
        (void)CloseHandle(m_hPutEvent);
    }

    if (m_hGetEvent) {
        (void)CloseHandle(m_hGetEvent);
    }

    if (m_hGetLockEvent) {
        (void)CloseHandle(m_hGetLockEvent);
    }

    // Delete the elements still in the queue
    for (UINT i = 0; i < m_nUsed; i++) {
        if (m_fDontCallDestructors) {
            // NOTE: We use this to avoid useless asserts when this destructor (called from RIL driver) frees
            //       memory allocated by RIL proxy
            LocalFree(m_rgpItems[i]);
        } else {
            delete m_rgpItems[i];
        }
    }

    // Get rid of the critical section
    DeleteCriticalSection(&m_cs);
}


//
// Initalize the queue
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::Init(const HANDLE hEvent)
{
    // If the queue is already initialized, skip this
    if (m_fInited) {
        goto Success;
    }

    // Create the events
    m_hGetLockEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if (!m_hGetLockEvent) {
        goto Error;
    }

    m_hGetEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (!m_hGetEvent) {
        goto Error;
    }

    m_hPutEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (!m_hPutEvent) {
        goto Error;
    }

    m_hCancelEvent = hEvent;
    m_fInited = TRUE;

Success:
    return TRUE;

Error:
    if (m_hGetEvent) {
        (void)CloseHandle(m_hGetEvent);
    }
    return FALSE;
}


//
// Enqueue an element
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::Put(Type* const pNew, const DWORD dwTimeout)
{
    SYNCBLOCK(m_cs);

    BOOL fRet = FALSE;

    if (m_fInited) {
        if (!WaitForEmptySpaceInternal(dwTimeout)) {
            goto Error;
        }

        // We have space in the queue
        m_rgpItems[m_nUsed] = pNew;
        m_nUsed++;

        // Signal the Put event
        (void)SetEvent(m_hPutEvent);
    }

    fRet = TRUE;

Error:
    return fRet;
}


//
// Dequeue an element
//
template <class Type, UINT Size>
HRESULT CQueue<Type, Size>::Get(Type*& rpItem, const DWORD dwTimeout)
{
    SYNCBLOCK(m_cs);
    return GetInternal(rpItem, dwTimeout);
}


//
// Dequeue an element (internal version)
//
template <class Type, UINT Size>
HRESULT CQueue<Type, Size>::GetInternal(Type*& rpItem, const DWORD dwTimeout)
{
    HRESULT hr = S_OK;

    // Initialize the returned pointer in case we fail
    rpItem = NULL;

    if (m_fInited) {
        hr = WaitForNextItemInternal(dwTimeout);
        if (FAILED(hr)) {
            goto Error;
        }

        // Got an item in the queue
        rpItem = m_rgpItems[0];
        m_nUsed--;
        memmove(m_rgpItems, (BYTE*)m_rgpItems + sizeof(Type*), sizeof(Type*) * m_nUsed);
        m_rgpItems[m_nUsed] = NULL;

        // Signal the Get event
        (void)SetEvent(m_hGetEvent);
    }

Error:
    return hr;
}


//
// Retrieve a pointer to the first element in the queue
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::Peek(Type*& rpItem)
{
    SYNCBLOCK(m_cs);
    return PeekInternal(rpItem);
}


//
// Retrieve a pointer to the first element in the queue (internal version)
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::PeekInternal(Type*& rpItem) const
{
    BOOL fRet = FALSE;

    rpItem = NULL;

    if (!m_nUsed) {
        goto Error;
    }

    rpItem = m_rgpItems[0];
    fRet = TRUE;

Error:
    return fRet;
}


//
// Dequeue an element from the queue, if it satisfies a condition
//    (will wait for the next element for a specified timeout)
//
template <class Type, UINT Size>
HRESULT CQueue<Type, Size>::ConditionalGet(const PFN_QUEUE_TEST pfnTest, const DWORD dwData, Type*& rpItem,
                                           const DWORD dwTimeout)
{
    SYNCBLOCK(m_cs);

    HRESULT hr = S_OK;

    // Wait for an element to appear in the queue
    hr = WaitForNextItemInternal(dwTimeout);
    if (FAILED(hr)) {
        goto Error;
    }

    // Peek the first element
    if (!PeekInternal(rpItem)) {
        ;
    }

    // See if the first element satisfies the condition
    if (!pfnTest(rpItem, dwData)) {
        rpItem = NULL;
        hr = E_FAIL;
        goto Error;
    }

    // Retrieve the first element
    if (FAILED(GetInternal(rpItem, INFINITE))) {
        ;
    }

Error:
    return hr;
}


//
// Wait until an element appears in the queue
//
template <class Type, UINT Size>
HRESULT CQueue<Type, Size>::WaitForNextItem(const DWORD dwTimeout)
{
    SYNCBLOCK(m_cs);
    return WaitForNextItemInternal(dwTimeout);
}


//
// Wait until an item appears in the queue (internal version)
//
template <class Type, UINT Size>
HRESULT CQueue<Type, Size>::WaitForNextItemInternal(const DWORD dwTimeout)
{
    HANDLE rghEvents[3] = { m_hPutEvent, m_hCancelEvent, m_hGetLockEvent };
    DWORD dwWait;
    HRESULT hr = S_OK;

    if (m_fInited) {
        for(;;) {
            // Are there any items in the queue?
            if (m_nUsed) {
                // Yes -- proceed
                break;
            } else {
                // No - need to wait for Put to happen
                UNSYNCBLOCK(m_cs);

                dwWait = WaitForMultipleObjects(3, rghEvents, FALSE, dwTimeout);
                if (WAIT_OBJECT_0 + 2 == dwWait) {
                    // We hit the terminate event -> quit
                    hr = RIL_E_RADIOOFF;
                    goto Error;
                } else if (WAIT_OBJECT_0 + 1 == dwWait) {
                    // We hit the terminate event -> quit
                    hr = RIL_E_CANCELLED;
                    goto Error;
                } else if (WAIT_TIMEOUT == dwWait) {
                    // We timed out -> quit
                    hr = RIL_E_TIMEDOUT;
                    goto Error;
                }
            }
        }

    }

Error:
    return hr;
}


//
// Wait until an empty slot appears in the queue (internal version)
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::WaitForEmptySpaceInternal(const DWORD dwTimeout)
{
    HANDLE rghEvents[2] = { m_hGetEvent, m_hCancelEvent };
    DWORD dwWait;
    BOOL fRet = FALSE;

    if (m_fInited) {
        for (;;) {
            // Is there space in the queue?
            if (m_nUsed < Size) {
                // Yes -- proceed
                break;
            } else {

                // No -- need to wait for Get to happen
                UNSYNCBLOCK(m_cs);

                dwWait = WaitForMultipleObjects(2, rghEvents, FALSE, dwTimeout);
                if (WAIT_OBJECT_0 != dwWait) {
                    // We hit the terminate event or timed out
                    goto Error;
                }
            }
        }
    }

    fRet = TRUE;

Error:
    return fRet;
}


//
// Enum all queue elements, calling the provided routine for each item
//
template <class Type, UINT Size>
void CQueue<Type, Size>::Enum(const PFN_QUEUE_ENUM pfnEnum, const DWORD dwData, const BOOL fClear)
{
    SYNCBLOCK(m_cs);

    if (m_fInited) {
        for (UINT i = 0; i < m_nUsed; i++) {
            if (pfnEnum((void*)m_rgpItems[i], dwData)) {
                break;
            }
        }

        if (fClear) {
            // We also need to clear the queue
            for (i = 0; i < m_nUsed; i++) {
                delete m_rgpItems[i];
                m_rgpItems[i] = NULL;
            }
            m_nUsed = 0;
        }
    }
}


//
// Lock or unlock the Get() function
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::SetReadLock(BOOL fLock)
{
    SYNCBLOCK(m_cs);
    return fLock ? ResetEvent(m_hGetLockEvent) : SetEvent(m_hGetLockEvent);
}


//
// Determine if the queue is empty
//
template <class Type, UINT Size>
BOOL CQueue<Type, Size>::FEmpty()
{
    SYNCBLOCK(m_cs);
    return !m_nUsed;
}


//
// Priority queue ctor
//
template <class Type, UINT Size>
CPriorityQueue<Type, Size>::CPriorityQueue()
: CQueue<Type, Size>(FALSE)
{
    m_dwPriorityLock = MAXDWORD;
}


//
// Priority queue dtor
//
template <class Type, UINT Size>
CPriorityQueue<Type, Size>::~CPriorityQueue()
{
}


//
// Enqueue an element
//
template <class Type, UINT Size>
BOOL CPriorityQueue<Type, Size>::Put(Type* const pNew, const DWORD dwTimeout)
{
    SYNCBLOCK(m_cs);

    UINT i;
    BOOL fRet = FALSE;

    if (m_fInited) {
        if (!WaitForEmptySpaceInternal(dwTimeout)) {
            goto Error;
        }

        // We have space in the queue -- find the correct spot for this item
        for (i = 0; i < m_nUsed; i++) {
            if (pNew->GetPriority() < m_rgpItems[i]->GetPriority()) {
                // We found the first item whose pri is lower (i.e., greater) than the one for the new item --
                //     shift other items in the queue
                memmove(&m_rgpItems[i + 1], &m_rgpItems[i], sizeof(Type*) * (m_nUsed - i));
                break;
            }
        }

        // Insert the new item
        m_rgpItems[i] = pNew;
        m_nUsed++;

        // Signal the Put event
        (void)SetEvent(m_hPutEvent);
    }

    fRet = TRUE;

Error:
    return fRet;
}


//
// Dequeue an element
//
template <class Type, UINT Size>
HRESULT CPriorityQueue<Type, Size>::Get(Type*& rpItem, const DWORD dwTimeout)
{
    HRESULT hr;
    while ((hr = ConditionalGet(CheckPriority, (DWORD)this, rpItem, dwTimeout)) == E_FAIL) {
        // Wait for next put event
        HANDLE rghEvents[3] = { m_hPutEvent, m_hCancelEvent, m_hGetLockEvent };
        switch (WaitForMultipleObjects(3, rghEvents, FALSE, dwTimeout)) {
            case (WAIT_OBJECT_0 + 2):
                // We hit the terminate event -> quit
                return RIL_E_RADIOOFF;
            case (WAIT_OBJECT_0 + 1):
                // We hit the terminate event -> quit
                return RIL_E_CANCELLED;
            case WAIT_TIMEOUT:
                // We timed out -> quit
                return RIL_E_TIMEDOUT;
        }
    }
    return hr;
}


//
// Set the priority lock (block all elements with lower priority)
//
template <class Type, UINT Size>
BOOL CPriorityQueue<Type, Size>::SetPriorityLock(const DWORD dwPriority)
{
    SYNCBLOCK(m_cs);
    m_dwPriorityLock = dwPriority;
    // Signal the Put event
    if (!FEmpty()) {
        (void)SetEvent(m_hPutEvent);
    }
    return TRUE;
}


//
// List ctor
//
template <class Type>
CDblList<Type>::CDblList()
: m_pElems(NULL)
{
    InitializeCriticalSection(&m_cs);
}


//
// List dtor
//
template <class Type>
CDblList<Type>::~CDblList()
{
    DeleteCriticalSection(&m_cs);
}


//
// Add an item to the list
//
template <class Type>
BOOL CDblList<Type>::Add(Type* const pAdd)
{
    SYNCBLOCK(m_cs);

    BOOL fRet = FALSE;

    // Check thaat the new element exists
    if (!pAdd) {
        goto Error;
    }

    // Add the new element at the front
    pAdd->SetNext(m_pElems);
    pAdd->SetPrev(NULL);

    if (pAdd->GetNext()) {
        pAdd->GetNext()->SetPrev(pAdd);
    }

    m_pElems = pAdd;
    fRet = TRUE;

Error:
    return fRet;
}


//
// Remove an item from the list
//
template <class Type>
BOOL CDblList<Type>::Remove(const Type* const pRemove)
{
    SYNCBLOCK(m_cs);

    BOOL fRet = FALSE;

    // Check that the element to be removed exists
    if (!pRemove) {
        goto Error;
    }

    // Is this element head of the list?
    if (pRemove == m_pElems) {
        // Yes
        m_pElems = (Type*)pRemove->GetNext();
    } else {
        // No
        if (pRemove->GetPrev())
            pRemove->GetPrev()->SetNext(pRemove->GetNext());
    }

    // Re-route the links
    if (pRemove->GetNext()) {
        pRemove->GetNext()->SetPrev(pRemove->GetPrev());
    }

    fRet = TRUE;

Error:
    return fRet;
}


//
// Enum all list elements, calling the provided routine for each item
//
template <class Type>
void CDblList<Type>::Enum(const PFN_LIST_ENUM pfnEnum, const DWORD dwData)
{
    SYNCBLOCK(m_cs);

    Type* pNext;
    for (Type* pWalk = m_pElems; pWalk; pWalk = pNext) {
        pNext = (Type*)pWalk->GetNext();
        if (pfnEnum((void*)pWalk, dwData)) {
            break;
        }
    }
}


