#include "precomp.h"
#include "events.hpp"
#include "ernccm.hpp"
#include "erncconf.hpp"
#include "erncvrsn.hpp"
#include "nccglbl.hpp"

extern PController  g_pMCSController;

CWorkItem::~CWorkItem(void) { } // pure virtual
BOOL GetSecurityInfo(ConnectionHandle connection_handle, PBYTE pInfo, PDWORD pcbInfo);


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Implementation of Methods for CInviteIndWork
//
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


CInviteIndWork::
CInviteIndWork
(
    PCONFERENCE             _pConference,
    LPCWSTR                 _wszCallerID,
    CLogicalConnection        * _pConEntry
)
:
    CWorkItem(_pConference),
    m_pConf(_pConference),
    m_fSecure(_pConEntry->IsConnectionSecure())
{
    DebugEntry(CInviteIndWork::CInviteIndWork);

    // Take copy of caller ID.
    // Note memory allocation failure proceeds with NULL ID.
    m_pwszCallerID = ::My_strdupW(_wszCallerID);

    DebugExitVOID(CInviteIndWork::CInviteIndWork);
}


CInviteIndWork::
~CInviteIndWork(void)
{
    DebugEntry(CInviteIndWork::~CInviteIndWork);

    //
    // If we substituted transport security data for roster data,
    // free that buffer now
    //

    delete m_pwszCallerID;

    DebugExitVOID(CInviteIndWork::~CInviteIndWork);
}


void CInviteIndWork::
DoWork(void)
{
    DebugEntry(CInviteIndWork::DoWork);

    // Now we are actually processing the invite, validate that there
    // are no other conferences of the same name, and, if not, block
    // a conference of the same name by setting the conference to be active,
    // and give invite request up to the UI.
    PCONFERENCE pOtherConf = g_pNCConfMgr->GetConferenceFromName(m_pConf->GetName());
    if (NULL == pOtherConf)
    {
        m_pConf->SetNotifyToDo(TRUE);
        g_pCallbackInterface->OnIncomingInviteRequest((CONF_HANDLE) m_pConf,
                                                      GetCallerID(),
                                                      m_fSecure);
    }
    else
    {
        m_pConf->InviteResponse(UI_RC_CONFERENCE_ALREADY_EXISTS);
    }

    DebugExitVOID(CInviteIndWork::DoWork);
}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Implementation of Methods for CJoinIndWork
//
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


CJoinIndWork::
CJoinIndWork
(
    GCCResponseTag         Tag,
    PCONFERENCE            _pConference,
    LPCWSTR                _wszCallerID,
    CLogicalConnection    *_pConEntry,
    HRESULT               *pRetCode
)
:
    CWorkItem(_pConference),
    m_nResponseTag(Tag),
    m_pConf(_pConference),
    m_pConEntry(_pConEntry)
{
    DebugEntry(CJoinIndWork::CJoinIndWork);

    *pRetCode = NO_ERROR;

#ifdef DEBUG
    SOCKET socket_number;
    g_pMCSController->FindSocketNumber(m_pConEntry->GetConnectionHandle(),&socket_number);
#endif

    // Take copy of caller ID because T120 
    // implementation is not keeping its copy valid
    // until the join response.
    // Note that memory allocation failure proceeds with 
    // NULL caller ID.
    m_pwszCallerID = ::My_strdupW(_wszCallerID);

    DebugExitVOID(CJoinIndWork::CJoinIndWork);
}


CJoinIndWork::
~CJoinIndWork(void)
{
    DebugEntry(CJoinIndWork::~CJoinIndWork);

    delete m_pwszCallerID;

    DebugExitVOID(CJoinIndWork::~CJoinIndWork);
}


void CJoinIndWork::
DoWork(void)
{
    DebugEntry(CJoinIndWork::DoWork);

    // Notify the core.
    g_pCallbackInterface->OnIncomingJoinRequest((CONF_HANDLE) m_pConf,
                                                m_pwszCallerID);
    DebugExitVOID(CJoinIndWork::DoWork);
}


HRESULT CJoinIndWork::
Respond ( GCCResult _Result )
{
    DebugEntry(CJoinIndWork::Respond);

    // It is a response from the core.
    HRESULT hr = ::GCCJoinResponseWrapper(m_nResponseTag,
                                          NULL,
                                          _Result,
                                          m_pConf->GetID(),
                                          0, NULL);

    DebugExitHRESULT(CJoinIndWork::Respond, hr);
    return hr;
}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Implementation of Methods for CSequentialWorkList
//
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


void CSequentialWorkList::
AddWorkItem ( CWorkItem *pWorkItem )
{
    DebugEntry(CSequentialWorkList::AddWorkItem);

    Append(pWorkItem);

    // If first entry in list, then kick off handler.
    if (1 == GetCount())
    {
        pWorkItem->DoWork();
    }

    DebugExitVOID(CSequentialWorkList::AddWorkItem);
}


void CSequentialWorkList::
RemoveWorkItem ( CWorkItem *pWorkItem )
{
    DebugEntry(CSequentialWorkList::RemoveWorkItem);

    if (pWorkItem)
    {
        // Make a note as to whether we are going to remove the head
        // work item in the list.
        BOOL bHeadItemRemoved = (pWorkItem == PeekHead());

        // Remove work item from list and destroy it.
        if (Remove(pWorkItem))
        {
            delete pWorkItem;

            // If there are more entries in the list, and we removed the
            // first one, then start the work of the next one in line.
            // Note that before doing this, the pointer to the workitem
            // was NULLed out (above) to prevent reentracy problems.
            if (bHeadItemRemoved && !IsEmpty())
            {
                PeekHead()->DoWork();
            }
        }
        else
        {
            ASSERT(! bHeadItemRemoved);
        }
    }

    DebugExitVOID(CSequentialWorkList::RemoveWorkItem);
}


void CSequentialWorkList::
PurgeListEntriesByOwner ( DCRNCConference *pOwner )
{
    CWorkItem   *pWorkItem;

    DebugEntry(CSequentialWorkList::PurgeListEntriesByOwner);

    if (NULL != pOwner)
    {
        // Note that head entry is removed last to stop work being started
        // on other entries in the list that are owned by pOwner.

        // Check to ensure there is a head item in the list.
        if (NULL != (pWorkItem = PeekHead()))
        {
            // Remember we are going to remove the head.
            BOOL    fHeadToRemove = pWorkItem->IsOwnedBy(pOwner);

            // Walk remaining entries in the list removing them.
            BOOL fMoreToRemove;
            do
            {
                fMoreToRemove = FALSE;
                Reset();
                while (NULL != (pWorkItem = Iterate()))
                {
                    if (pWorkItem->IsOwnedBy(pOwner))
                    {
                        Remove(pWorkItem);
                        delete pWorkItem;
                        fMoreToRemove = TRUE;
                        break;
                    }
                }
            }
            while (fMoreToRemove);

            // Now done removing all entries, including the head if needed...
            if (fHeadToRemove && ! IsEmpty())
            {
                PeekHead()->DoWork();
            }
        }
    }

    DebugExitVOID(CSequentialWorkList::PurgeListEntriesByOwner);
}


void CSequentialWorkList::
DeleteList(void)
{
    CWorkItem *pWorkItem;
    while (NULL != (pWorkItem = Get()))
    {
        delete pWorkItem;
    }
}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// Implementation of Methods for CQueryRemoteWork
//
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


CQueryRemoteWork::
CQueryRemoteWork
(
    LPVOID              pCallerContext,
    GCCAsymmetryType    eAsymType,
    LPCSTR              pcszNodeAddress,
	BOOL				fSecure,
    HRESULT             *pRetCode
)
: 
    CWorkItem(pCallerContext),
    m_hGCCConnHandle(NULL),
    m_apConfNames(NULL),
    m_fRemoteIsMCU(FALSE),
    m_eAsymType(eAsymType),
    m_fSecure(fSecure),
    m_apConfDescriptors(NULL)
{
    DebugEntry(CQueryRemoteWork::CQueryRemoteWork);

    char szAddress[RNC_MAX_NODE_STRING_LEN];
    ::BuildAddressFromNodeDetails((LPSTR) pcszNodeAddress, &szAddress[0]);
    m_pszAddress = ::My_strdupA(&szAddress[0]);
    m_hr = (NULL != m_pszAddress) ? NO_ERROR : UI_RC_OUT_OF_MEMORY;
    *pRetCode = m_hr;
    
    DebugExitVOID(CQueryRemoteWork::CQueryRemoteWork);
}


CQueryRemoteWork::
~CQueryRemoteWork(void)
{
    LPWSTR *ppTempTargetName;
    LPWSTR *ppTempTargetDescriptor;

    DebugEntry(CQueryRemoteWork::~CQueryRemoteWork);

    // Clean up memory allocated.
    if (m_apConfNames)
    {
        ppTempTargetName = m_apConfNames;
        while (*ppTempTargetName)
        {
            delete *(ppTempTargetName++);
        }
        delete [] m_apConfNames;
    }

    if (m_apConfDescriptors)
    {
        ppTempTargetDescriptor = m_apConfDescriptors;
        while (*ppTempTargetDescriptor)
        {
            delete *(ppTempTargetDescriptor++);
        }
        delete [] m_apConfDescriptors;
    }   
    delete m_pszAddress;

    DebugExitVOID(CQueryRemoteWork::~CQueryRemoteWork);
}


void CQueryRemoteWork::
DoWork(void)
{
    GCCError                GCCrc;
    GCCNodeType             nodeType;
    GCCAsymmetryIndicator   asymmetry_indicator;

    DebugEntry(CQueryRemoteWork::DoWork);

    ::LoadAnnouncePresenceParameters(&nodeType, NULL, NULL, NULL);

    asymmetry_indicator.asymmetry_type = m_eAsymType;
    asymmetry_indicator.random_number = 0;
    if (asymmetry_indicator.asymmetry_type == GCC_ASYMMETRY_UNKNOWN)
    {
        m_nRandSeed = (int) ::GetTickCount();
        m_LocalAsymIndicator.random_number = ((GenerateRand() << 16) + GenerateRand());
        asymmetry_indicator.random_number = m_LocalAsymIndicator.random_number;
        m_LocalAsymIndicator.asymmetry_type = GCC_ASYMMETRY_UNKNOWN;
        m_fInUnknownQueryRequest = TRUE;
    }

    GCCrc = g_pIT120ControlSap->ConfQueryRequest(
                nodeType,
                &asymmetry_indicator,
                NULL,
                (TransportAddress) m_pszAddress,
				m_fSecure,
                0,
                NULL,
                &m_hGCCConnHandle);
    TRACE_OUT(("GCC call: g_pIT120ControlSap->ConfQueryRequest, rc=%d", GCCrc));

    if (NO_ERROR != (m_hr = ::GetGCCRCDetails(GCCrc)))
    {
        AsyncQueryRemoteResult();
    }

    DebugExitHRESULT(CQueryRemoteWork::DoWork, m_hr);
}


void CQueryRemoteWork::
HandleQueryConfirmation ( QueryConfirmMessage * pQueryMessage )
{
    UINT                                   NumberOfConferences;
    GCCConferenceDescriptor             ** ppConferenceDescriptor;
    PWSTR *                                 ppTempTargetName;
    PWSTR                                   ConferenceTextName;
    GCCConferenceName *     pGCCConferenceName;
    PWSTR *                ppTempTargetDescriptor;
    PWSTR                  pwszConfDescriptor=NULL;
	HRESULT					hrTmp;

    DebugEntry(CQueryRemoteWork::HandleQueryConfirmation);

    // If no error, then package up information.
    m_hr = ::GetGCCResultDetails(pQueryMessage->result);
    if (NO_ERROR == m_hr)
    {
        m_fRemoteIsMCU = (pQueryMessage->node_type == GCC_MCU);
        NumberOfConferences = pQueryMessage->number_of_descriptors;
        DBG_SAVE_FILE_LINE
        m_apConfNames = new PWSTR[NumberOfConferences + 1];
        m_apConfDescriptors = new PWSTR[NumberOfConferences + 1];
        if (!m_apConfNames || !m_apConfDescriptors)
        {
            m_hr = UI_RC_OUT_OF_MEMORY;
        }
        else
        {
            ppConferenceDescriptor = pQueryMessage->conference_descriptor_list;
            ppTempTargetName = m_apConfNames;
            ppTempTargetDescriptor = m_apConfDescriptors;
            while (NumberOfConferences--)
            {
                pwszConfDescriptor = (*(ppConferenceDescriptor))->conference_descriptor;
                pGCCConferenceName = &(*(ppConferenceDescriptor++))->conference_name;

                if (pwszConfDescriptor != NULL)
                {
                    pwszConfDescriptor = ::My_strdupW(pwszConfDescriptor);
                }
                ConferenceTextName = pGCCConferenceName->text_string;
                if (ConferenceTextName != NULL)
                {
                    ConferenceTextName = ::My_strdupW(ConferenceTextName);
                    if (!ConferenceTextName)
                    {
                        // Out of memory, give back what we have.
                        m_hr = UI_RC_OUT_OF_MEMORY;
                        break;
                    }
                }
                else
                if (pGCCConferenceName->numeric_string != NULL)
                {
                    ConferenceTextName = ::AnsiToUnicode((PCSTR)pGCCConferenceName->numeric_string);
                    if (!ConferenceTextName)
                    {
                        // Out of memory, give back what we have.
                        m_hr = UI_RC_OUT_OF_MEMORY;
                        break;
                    }
                }
                if (ConferenceTextName)
                {
                    *(ppTempTargetName++) = ConferenceTextName;
                    *(ppTempTargetDescriptor++) = pwszConfDescriptor;
                }
            }
            *ppTempTargetName = NULL;
            *ppTempTargetDescriptor = NULL;
        }
    }

    m_fInUnknownQueryRequest = FALSE;

	hrTmp = m_hr;

    // Propagate the result directly without posting a message.
    SyncQueryRemoteResult();

    DebugExitHRESULT(CQueryRemoteWork::HandleQueryConfirmation, hrTmp);
}


void CQueryRemoteWork::
SyncQueryRemoteResult(void)
{
    DebugEntry(CQueryRemoteWork::SyncQueryRemoteResult);

    // Let the user know the result of his request.
    // The user is expected to call Release() after getting the result,
    // if he wants to drop the line - and should for errors.
    // Also, if the user is being called back before the inline code
    // has filled in the handle, then fill it in here - see comments in
    // DCRNCConferenceManager::QueryRemote for additional background.
    g_pCallbackInterface->OnQueryRemoteResult(
                                m_pOwner,
                                m_hr,
                                m_fRemoteIsMCU,
                                m_apConfNames,
                                m_apConfDescriptors);

    // If we are not inline, and this request made it into 
    // the sequential work item list,
    // then remove from list (which will cause item to be deleted),
    // otherwise, just delete item.
    g_pQueryRemoteList->RemoveWorkItem(this);

    DebugExitVOID(CQueryRemoteWork::SyncQueryRemoteResult);
}


void CQueryRemoteWork::
AsyncQueryRemoteResult(void)
{
    g_pNCConfMgr->PostWndMsg(NCMSG_QUERY_REMOTE_FAILURE, (LPARAM) this);
}

    
int CQueryRemoteWork::
GenerateRand(void)
{ // code from CRT
    return (((m_nRandSeed = m_nRandSeed * 214013L + 2531011L) >> 16) & 0x7fff);
}


HRESULT CQueryRemoteWorkList::
Cancel ( LPVOID pCallerContext )
{
    HRESULT hr = S_FALSE; // if not found
    CQueryRemoteWork *p;
    Reset();
    while (NULL != (p = Iterate()))
    {
        if (p->IsOwnedBy(pCallerContext))
        {
            // clean up the underlying plumbing.
            g_pIT120ControlSap->CancelConfQueryRequest(p->GetConnectionHandle());

            // clean up node controller data.
            RemoveWorkItem(p);
            hr = S_OK;
            break;
        }
    }

    return hr;
}