ACDTAPI.C

/////////////////////////////////////////////////////////////////////////////////// 
//
// ACDTAPI.C
//
// This file handles all tapi functionality in the ACD sample
//
//
//
////////////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <tapi.h>
#include "acdsmpl.h"

VOID CALLBACK LineCallback (DWORD hDevice,
DWORD dwMsg,
DWORD dwCallbackInstance,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3);


#define LogTapiError(__lResult__, __szString__)
#define LogError(__szString__)

extern ACDGLOBALS g;


////////////////////////////////////////////////////////////////////////////////////
//
// BOOL InitializeTapi()
//
// Whatever is needed to init TAPI for the application. This is called
// before the main window is created.
//
////////////////////////////////////////////////////////////////////////////////////
BOOL InitializeTapi()
{
DWORD dwAPIVersion;
LINEINITIALIZEEXPARAMS exparams;
LONG lResult;
DWORD i;
LPLINEDEVCAPS pLDC;


// fill in lineinitex parameters
exparams.dwTotalSize = sizeof(LINEINITIALIZEEXPARAMS);
exparams.dwOptions = LINEINITIALIZEEXOPTION_USEHIDDENWINDOW;

dwAPIVersion = TAPI_CURRENT_VERSION;

// line init
if ((lResult = lineInitializeEx(&g.hLineApp,
g.hInstance,
LineCallback,
SZAPPNAME,
&g.dwNumDevs,
&dwAPIVersion,
&exparams)) < 0)
{
LogTapiError(lResult, "lineInitializeEx");
return FALSE;
}

// if there are no tapi devices, should probably
// not continue
if (g.dwNumDevs == 0)
{
LogError("No TAPI devices installed");
lineShutdown(g.hLineApp);
return FALSE;
}

// need to get the permanent device IDs to map from
// an .ini file being read in
g.pdwPermIDs = (LPDWORD)ACDAlloc(g.dwNumDevs * sizeof(DWORD));

if (!g.pdwPermIDs)
{
return FALSE;
}

for (i = 0; i < g.dwNumDevs; i++)
{
pLDC = LineGetDevCaps(g.hLineApp,
i);

if (pLDC)
{
g.pdwPermIDs[i] = pLDC->dwPermanentLineID;
ACDFree(pLDC);
}
}

return TRUE;
}


//////////////////////////////////////////////////////////////////////
//
// BOOL CleanUp()
//
// Called while shutting down. free memory, close down tapi
//
//////////////////////////////////////////////////////////////////////
BOOL CleanUp()
{
PAGENT pAgent, pAgentNext;
PGROUP pGroup, pGroupNext;

// remove agents
pAgent = g.pAgents;
while(pAgent)
{
pAgentNext = pAgent->pNext;
DeleteAgent(pAgent);
pAgent = pAgentNext;
}

// remove groups
pGroup = g.pGroups;
while (pGroup)
{
pGroupNext = pGroup->pNext;
DeleteGroup(pGroup);
pGroup = pGroupNext;
}

// free id array
ACDFree(g.pdwPermIDs);

// shutdown
lineShutdown(g.hLineApp);

return TRUE;

}
////////////////////////////////////////////////////////////////////////////////
//
// LRESULT MakeGroupList(PAGENT pAgent,
// LPLINEAGENTGROUPLIST pGroupList)
//
// Creates a LINEAGENTGROUPLIST for pAgent - group that the agent
// is allowed to log into
// Assumption: don't care about address for group list
//
////////////////////////////////////////////////////////////////////////////////
LRESULT MakeGroupList(PAGENT pAgent,
LPLINEAGENTGROUPLIST pGroupList)
{
PGROUP pGroup;
DWORD dwTotalSizeNeeded, dwNameOffset, dwNumEntries;
LPLINEAGENTGROUPENTRY pEntry;
LPTSTR pName;


pGroup = g.pGroups;
dwTotalSizeNeeded = sizeof(LINEAGENTGROUPLIST);
pGroupList->dwNumEntries = 0;
dwNumEntries = 0;

// walk list of groups
while (pGroup)
{
if (IsAgentInList(pGroup->pAgentList,
pAgent))
// if found the agent, add the group to the group list
{
// incrememt number of entries
dwNumEntries++;

// add to total size needed
dwTotalSizeNeeded += sizeof(LINEAGENTGROUPENTRY);
dwTotalSizeNeeded += (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR);
}

pGroup = pGroup->pNext;
}

pGroupList->dwNeededSize = dwTotalSizeNeeded;

if (pGroupList->dwTotalSize < dwTotalSizeNeeded)
{
pGroupList->dwUsedSize = sizeof(LINEAGENTGROUPLIST);

return 0;
// return LINEERR_STRUCTURETOOSMALL;
}

pGroupList->dwNumEntries = dwNumEntries;

// set the list info
pGroupList->dwListSize = sizeof(LINEAGENTGROUPENTRY) * pGroupList->dwNumEntries;
pGroupList->dwListOffset = sizeof(LINEAGENTGROUPLIST);

// get the first agentgroup entry struct
pEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pGroupList) + pGroupList->dwListOffset);

dwNameOffset = pGroupList->dwListOffset + pGroupList->dwListSize;
pGroup = g.pGroups;

// loop through the groups again, and fill in the structure
while (pGroup)
{
if (IsAgentInList(pGroup->pAgentList,
pAgent))
{
// ID is just PGROUP
pEntry->GroupID.dwGroupID1 = (DWORD)pGroup;
pEntry->GroupID.dwGroupID2 = 0;
pEntry->GroupID.dwGroupID3 = 0;
pEntry->GroupID.dwGroupID4 = 0;

// set name of group
pName = (LPTSTR)(((LPBYTE)pGroupList) + dwNameOffset);

pEntry->dwNameSize = (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR);
pEntry->dwNameOffset = dwNameOffset;
lstrcpy(pName,
pGroup->lpszName);

dwNameOffset += pEntry->dwNameSize;

// get next entry
pEntry++;
}

pGroup = pGroup->pNext;
}

pGroupList->dwUsedSize = dwTotalSizeNeeded;

return 0;
}



////////////////////////////////////////////////////////////////////////////
//
// LRESULT SetGroupList()
//
// Sets the groups that the agent is logged into.
// This does not change the groups that the agent _can_ log into
//
////////////////////////////////////////////////////////////////////////////
LRESULT SetGroupList(PAGENT pAgent,
DWORD dwAddress,
LPLINEAGENTGROUPLIST pGroupList)
{
LPLINEAGENTGROUPENTRY pGroupEntry;
PLISTITEM pListEntry;
DWORD i;
PGROUP * ppGroups = NULL;
PGROUP pGroup;

ppGroups = (PGROUP*)ACDAlloc(sizeof(PGROUP) * pGroupList->dwNumEntries);

// get to the group entry struct
pGroupEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pGroupList) + pGroupList->dwListOffset);

// loop through all entries
for (i = 0; i < pGroupList->dwNumEntries; i++)
{
// get the group in entry
// NOTE! NOTE! NOTE!
// should protect here against bad pointers !!!
pGroup = (PGROUP)pGroupEntry->GroupID.dwGroupID1;

if (pGroup->dwKey != GROUPKEY)
{
return LINEERR_INVALAGENTGROUP;
}

pListEntry = pGroup->pAgentList;

// walk list of agents in that group
if (!IsAgentInList(pGroup->pAgentList,
pAgent))
{
ACDFree(ppGroups);
return LINEERR_INVALAGENTGROUP;
}

// save group for easy access
ppGroups[i] = pGroup;

// get the next entry (after the variable portion of
// the previous entry struct)
pGroupEntry++;
}

// now we know that the groups to be set are valid
// walk through the list of groups again, and
// set the status to logged in/ not logged in
// for every group that the agent is a member of

pGroup = g.pGroups;

// walk list of all groups
while (pGroup)
{
if (pListEntry = IsAgentInList(pGroup->pAgentList,
pAgent))
{
// default to not logged in
pListEntry->bLoggedIn = FALSE;

// loop through groups being set
for (i = 0; i < pGroupList->dwNumEntries; i++)
{
// if this group is in list, set agent to logged in
if (pGroup == ppGroups[i])
{
pListEntry->bLoggedIn = TRUE;
// assumption: agent can only log into a group on one address.
pListEntry->dwAddress = dwAddress;
break;
}

} // for

} // agent in list

// next group
pGroup = pGroup->pNext;

} // while


ACDFree(ppGroups);

return 0;
}


/////////////////////////////////////////////////////////////////////////
//
// BOOL MakeAgentActivityList()
//
// Creates a LINEAGENTACTIVITYLIST for pAgent
//
// for the sample, just generic names are used
// "Activity 1", "Activity 2"....
//
/////////////////////////////////////////////////////////////////////////
LRESULT MakeAgentActivityList(PAGENT pAgent,
LPLINEAGENTACTIVITYLIST pActivityList)
{
TCHAR szBuffer[64];
DWORD dwTotalSize, dwNameOffset, i, dwNumEntries;
LPTSTR pName;
LPLINEAGENTACTIVITYENTRY pEntry;

// init
dwTotalSize = sizeof(LINEAGENTACTIVITYLIST);
pActivityList->dwNumEntries = 0;
dwNumEntries = 0;

// just a static list of activities
for (i = 0; i < TOTALACTIVITIES; i++)
{
dwNumEntries++;

// create a name
wsprintf(szBuffer, TEXT("Activity %lu"), i);

// determine size of this entry
dwTotalSize += sizeof(LINEAGENTACTIVITYENTRY);
dwTotalSize += (lstrlen(szBuffer) + 1) * sizeof(TCHAR);
}

pActivityList->dwNeededSize = dwTotalSize;

// verify size
if (pActivityList->dwTotalSize < dwTotalSize)
{
pActivityList->dwUsedSize = sizeof(LINEAGENTACTIVITYLIST);

return 0;
// return LINEERR_STRUCTURETOOSMALL;
}

pActivityList->dwNumEntries = dwNumEntries;

// set list stuff
pActivityList->dwListSize = sizeof(LINEAGENTACTIVITYENTRY) * pActivityList->dwNumEntries;
pActivityList->dwListOffset = sizeof(LINEAGENTACTIVITYLIST);

// get first activityentry
pEntry = (LPLINEAGENTACTIVITYENTRY)(((LPBYTE)pActivityList) + pActivityList->dwListOffset);
dwNameOffset = pActivityList->dwListOffset + pActivityList->dwListSize;

// loop through activities again
for (i = 0; i < TOTALACTIVITIES; i++)
{
// fill in members
pEntry->dwID = i;

// create a name
wsprintf(szBuffer, TEXT("Activity %lu"), i);

pName = (LPTSTR)(((LPBYTE)pActivityList) + dwNameOffset);

pEntry->dwNameSize = (lstrlen(szBuffer) + 1) * sizeof(TCHAR);
pEntry->dwNameOffset = dwNameOffset;
lstrcpy(pName,
szBuffer);

dwNameOffset += pEntry->dwNameSize;

pEntry++;

} // for

// fill in used size
pActivityList->dwUsedSize = dwTotalSize;

return 0;
}


#define DWAGENTFEATURES LINEAGENTFEATURE_SETAGENTGROUP | \
LINEAGENTFEATURE_SETAGENTSTATE | \
LINEAGENTFEATURE_SETAGENTACTIVITY | \
LINEAGENTFEATURE_GETAGENTACTIVITYLIST | \
LINEAGENTFEATURE_GETAGENTGROUP

#define DWSTATES LINEAGENTSTATE_LOGGEDOFF | \
LINEAGENTSTATE_NOTREADY | \
LINEAGENTSTATE_READY | \
LINEAGENTSTATE_BUSYACD | \
LINEAGENTSTATE_BUSYINCOMING | \
LINEAGENTSTATE_BUSYOUTBOUND | \
LINEAGENTSTATE_BUSYOTHER | \
LINEAGENTSTATE_WORKINGAFTERCALL | \
LINEAGENTSTATE_UNKNOWN | \
LINEAGENTSTATE_UNAVAIL

#define DWNEXTSTATES LINEAGENTSTATE_LOGGEDOFF | \
LINEAGENTSTATE_NOTREADY | \
LINEAGENTSTATE_READY | \
LINEAGENTSTATE_BUSYACD | \
LINEAGENTSTATE_BUSYINCOMING | \
LINEAGENTSTATE_BUSYOUTBOUND | \
LINEAGENTSTATE_BUSYOTHER | \
LINEAGENTSTATE_WORKINGAFTERCALL | \
LINEAGENTSTATE_UNKNOWN | \
LINEAGENTSTATE_UNAVAIL

#define DWSTATUSMESSAGES LINEAGENTSTATUS_GROUP | \
LINEAGENTSTATUS_STATE | \
LINEAGENTSTATUS_NEXTSTATE | \
LINEAGENTSTATUS_ACTIVITY | \
LINEAGENTSTATUS_ACTIVITYLIST | \
LINEAGENTSTATUS_GROUPLIST | \
LINEAGENTSTATUS_CAPSCHANGE | \
LINEAGENTSTATUS_VALIDSTATES | \
LINEAGENTSTATUS_VALIDNEXTSTATES


////////////////////////////////////////////////////////////////////
//
// BOOL IsValidState(DWORD dwState)
//
////////////////////////////////////////////////////////////////////
BOOL IsValidState(DWORD dwState)
{
if (!dwState)
{
return TRUE;
}

if ((dwState) & (dwState - 1))
{
// more than one bit set
return FALSE;
}

// make sure it's one of the valid states
return (dwState & DWSTATES);

}


////////////////////////////////////////////////////////////////////
//
// BOOL IsValidNextState(DWORD dwState)
//
////////////////////////////////////////////////////////////////////
BOOL IsValidNextState(DWORD dwState)
{
if (!dwState)
{
return TRUE;
}

if ((dwState) & (dwState - 1))
{
// more than one bit set
return FALSE;
}

// make sure it's one of the valid states
return (dwState & DWNEXTSTATES);

}


///////////////////////////////////////////////////////////////////////
//
// BOOL IsValidActivityID(DWORD dwActivityID)
//
///////////////////////////////////////////////////////////////////////
BOOL IsValidActivityID(DWORD dwActivityID)
{
return (dwActivityID <= TOTALACTIVITIES);
}



////////////////////////////////////////////////////////////////////////
//
// LRESULT MakeAgentCaps(PAGENT pAgent,
// LPLINEAGENTCAPS pAgentCaps)
//
// Creates a LINEAGENTCAPS for pAgent
// Features/states/messages are hardcoded
// for this example
//
////////////////////////////////////////////////////////////////////////
LRESULT MakeAgentCaps(PAGENT pAgent,
LPLINEAGENTCAPS pAgentCaps)
{
DWORD dwStringSize;

dwStringSize = (lstrlen(SZAPPNAME) + 1) * sizeof(TCHAR);

pAgentCaps->dwNeededSize = sizeof(LINEAGENTCAPS) + dwStringSize;

if (pAgentCaps->dwTotalSize < pAgentCaps->dwNeededSize)
{
pAgentCaps->dwUsedSize = sizeof(LINEAGENTCAPS);
return 0;
// return LINEERR_STRUCTURETOOSMALL;
}


pAgentCaps->dwAgentHandlerInfoSize = dwStringSize;
pAgentCaps->dwAgentHandlerInfoOffset = sizeof(LINEAGENTCAPS);

pAgentCaps->dwCapsVersion = TAPI_CURRENT_VERSION;

// these features are hardcoded here.
// a real implementation may set specific features
// per agent or line or address
pAgentCaps->dwFeatures = DWAGENTFEATURES;
pAgentCaps->dwStates = DWSTATES;
pAgentCaps->dwNextStates = DWNEXTSTATES;
pAgentCaps->dwMaxNumGroupEntries = NUMGROUPENTRIES;
pAgentCaps->dwAgentStatusMessages = DWSTATUSMESSAGES;

// no extensions
pAgentCaps->dwNumAgentExtensionIDs = 0;
pAgentCaps->dwAgentExtensionIDListSize = 0;
pAgentCaps->dwAgentExtensionIDListOffset = 0;


pAgentCaps->dwUsedSize = pAgentCaps->dwNeededSize;

return 0;
}


//////////////////////////////////////////////////////////////////////////
//
// LRESULT GetAgentStatus()
//
// Creates a LINEAGENTSTATUS for pAgent
//
//////////////////////////////////////////////////////////////////////////
LRESULT GetAgentStatus(PAGENT pAgent,
DWORD dwAddress,
LPLINEAGENTSTATUS pAgentStatus)
{
PGROUP pGroup;
LPLINEAGENTGROUPENTRY pGroupEntry;
DWORD dwTotalSize, dwNameOffset, dwCount;
TCHAR szActivityName[NAMESIZE];
PGROUP * ppGroups;
PLISTITEM pEntry;

// init total size
dwTotalSize = sizeof(LINEAGENTSTATUS);

if (dwAddress >= pAgent->dwNumAddresses)
{
return LINEERR_INVALADDRESSID;
}

// set know members
// for valid states / next states / agent features, just setting it to
// generic stuff. a real implementation may want to set these
// field depending on current state / agent / hline
pAgentStatus->dwState = pAgent->pAddressInfo[dwAddress].dwState;
pAgentStatus->dwNextState = pAgent->pAddressInfo[dwAddress].dwNextState;
pAgentStatus->dwActivityID = pAgent->pAddressInfo[dwAddress].dwActivity;
pAgentStatus->dwAgentFeatures = DWAGENTFEATURES;
pAgentStatus->dwValidStates = DWSTATES;
pAgentStatus->dwValidNextStates = DWNEXTSTATES;

// create the activity name
wsprintf(szActivityName, TEXT("Activity %lu"), pAgent->pAddressInfo[dwAddress].dwActivity);
dwTotalSize += (lstrlen(szActivityName) + 1) * sizeof(TCHAR);

ppGroups = (PGROUP *)ACDAlloc(sizeof(PGROUP) * g.dwNumGroups);

pGroup = g.pGroups;

pAgentStatus->dwNumEntries = 0;

// walk list of groups
while (pGroup)
{
pEntry = pGroup->pAgentList;

// walk each agent in each group
while (pEntry)
{
if (pEntry->pAgent == pAgent)
{
if ((!pEntry->bLoggedIn) ||
(pEntry->dwAddress != dwAddress))
{
break;
}

// save group
ppGroups[pAgentStatus->dwNumEntries] = pGroup;

// adjust total size / entries
pAgentStatus->dwNumEntries++;
dwTotalSize += sizeof(LINEAGENTGROUPENTRY);
dwTotalSize += (lstrlen(pGroup->lpszName) + 1) * sizeof(TCHAR);

break;
}

pEntry = pEntry->pNext;

} // while (pEntry)

pGroup = pGroup->pNext;

} // while (pGroup)

// set needed size
pAgentStatus->dwNeededSize = dwTotalSize;

// do we have enough room?
if (pAgentStatus->dwTotalSize < dwTotalSize)
{
// if not, return
pAgentStatus->dwUsedSize = sizeof(LINEAGENTSTATUS);
ACDFree(ppGroups);

return 0;
// return LINEERR_STRUCTURETOOSMALL;
}


// set the group entries...

// first get the offset to the first entry
pGroupEntry = (LPLINEAGENTGROUPENTRY)(((LPBYTE)pAgentStatus) + sizeof(LINEAGENTSTATUS));
pAgentStatus->dwGroupListOffset = sizeof(LINEAGENTSTATUS);

// figure out where the names can go (after all the fixed structures)
dwNameOffset = sizeof(LINEAGENTSTATUS) +
sizeof(LINEAGENTGROUPENTRY) * pAgentStatus->dwNumEntries;

// loop through all the group that the agent is logged into
for (dwCount = 0; dwCount < pAgentStatus->dwNumEntries; dwCount++)
{
// set the it (just the pGroup)
pGroupEntry->GroupID.dwGroupID1 = (DWORD)ppGroups[dwCount];
pGroupEntry->GroupID.dwGroupID2 = 0;
pGroupEntry->GroupID.dwGroupID3 = 0;
pGroupEntry->GroupID.dwGroupID4 = 0;

// set name size and offset
pGroupEntry->dwNameSize = (lstrlen(ppGroups[dwCount]->lpszName) + 1) * sizeof(TCHAR);
pGroupEntry->dwNameOffset = dwNameOffset;

// copy name
lstrcpy((LPTSTR)(((LPBYTE)pAgentStatus) + dwNameOffset),
ppGroups[dwCount]->lpszName);

// fix the name offset
dwNameOffset += pGroupEntry->dwNameSize;

// next entry
pGroupEntry++;

}

pAgentStatus->dwGroupListSize = dwNameOffset - pAgentStatus->dwGroupListOffset;

// put the activity name at the end
pAgentStatus->dwActivitySize = (lstrlen(szActivityName) + 1) * sizeof(TCHAR);
pAgentStatus->dwActivityOffset = dwNameOffset;

lstrcpy((LPTSTR)(((LPBYTE)pAgentStatus) + dwNameOffset),
szActivityName);


ACDFree(ppGroups);

pAgentStatus->dwUsedSize = pAgentStatus->dwNeededSize;
// return success
return 0;
}


/////////////////////////////////////////////////////////////////////
//
// LRESULT SetAgentState()
//
// Sets the current and next state for pAgent
// on that specific address
//
//
/////////////////////////////////////////////////////////////////////
LRESULT SetAgentState(PAGENT pAgent,
DWORD dwAddress,
DWORD dwState,
DWORD dwNextState)
{
// make sure valid
if ((!IsValidState(dwState)) ||
(!IsValidNextState(dwNextState)))
{
return LINEERR_INVALAGENTSTATE;
}

// check address
if (dwAddress >= pAgent->dwNumAddresses)
{
return LINEERR_INVALADDRESSID;
}

// set the state if specified
if (dwState)
{
pAgent->pAddressInfo[dwAddress].dwState = dwState;
}

if (dwNextState)
{
pAgent->pAddressInfo[dwAddress].dwNextState = dwNextState;
}

return 0;
}


///////////////////////////////////////////////////////////////////////////////
//
// LRESULT SetAgentActivity()
//
// Sets the activity for pAgent
//
///////////////////////////////////////////////////////////////////////////////
LRESULT SetAgentActivity(PAGENT pAgent,
DWORD dwAddress,
DWORD dwActivityID)
{
if (dwAddress >= pAgent->dwNumAddresses)
{
return LINEERR_INVALADDRESSID;
}

if (!IsValidActivityID(dwActivityID))
{
return LINEERR_INVALAGENTACTIVITY;
}

pAgent->pAddressInfo[dwAddress].dwActivity = dwActivityID;

return 0;
}


////////////////////////////////////////////////////////////////////////////////
//
// BOOL HandleOffering(HCALL hCall)
//
// Handles a LINECALLSTATE_OFFERING message
// Determines the group associated with the address
// that the call came in on, finds an available
// agent in that group, and transfers the call to that
// agent.
//
////////////////////////////////////////////////////////////////////////////////
BOOL HandleOffering(HCALL hCall)
{
LPLINECALLINFO pLCI;
PGROUP pGroup;
PLISTITEM pEntry;
LONG lResult;

pLCI = LineGetCallInfo(hCall);

if (!pLCI)
{
return FALSE;
}

pGroup = g.pGroups;

// find the group that this call came in on
// walk all the groups
while (pGroup)
{
// if the line and address match, it's the
// correct address
if ((pGroup->hLine == pLCI->hLine) &&
(pGroup->dwAddress == pLCI->dwAddressID))
{
break;
}

pGroup = pGroup->pNext;
}

// couldn't find the group
if (!pGroup)
{
// error!
ACDFree(pLCI);
return FALSE;
}

// OK - found the group that this call is for. Now transfer to
// an agent that is available.
pEntry = pGroup->pAgentList;

while (pEntry)
{
if (pEntry->bLoggedIn &&
(pEntry->pAgent->pAddressInfo[pEntry->dwAddress].dwState ==
LINEAGENTSTATE_READY))
{
// found someone
// doing a blind transfer here
// other implementations may need to
// do lineSetupTransfer / lineDial / lineCompleteTransfer
if (lResult = lineBlindTransfer(hCall,
(LPCWSTR)pEntry->pAgent->lpszNumber,
0))
{
//LogTapiError(TEXT("lineBlindTransfer"), lResult);
// don't break - try the next agent
}
else
{
// set the state to reflect that
// a call is being handled
SetAgentState(pEntry->pAgent,
pEntry->dwAddress,
LINEAGENTSTATE_BUSYACD,
LINEAGENTSTATE_READY);

break;
}

}

pEntry = pEntry->pNext;
}

if (!pEntry)
{
// couldn't find an available agent

// NOTE! NOTE! NOTE! NOTE! NOTE!
// something should be done here with this call. put into
// a queue on hold or something. For this sample, we are just
// ignoring it
}

ACDFree(pLCI);

return TRUE;
}


//////////////////////////////////////////////////////////////////////////
//
// BOOL HandleIdle(HCALL hCall)
//
// Handles LINECALLSTATE_IDLE
// Should always always always deallocate when
// getting an IDLE message. Also, determine if this is a call
// that we know about and set the agent state appropriatly
//
//////////////////////////////////////////////////////////////////////////
BOOL HandleIdle(HCALL hCall)
{
LPLINECALLINFO pLCI;
PAGENT pAgent;

pLCI = LineGetCallInfo(hCall);

// always deallocate the call
lineDeallocateCall(hCall);

if (!pLCI)
{
return FALSE;
}

// get the agent associated with the line
pAgent = GetAgentFromhLine(pLCI->hLine);

if (!pAgent)
{
ACDFree(pLCI);
return FALSE;
}


// set that agent to their next state
// Assumption: only calls that the ACD app know about
// occur. For example, if an agent made an outgoing call
// and it transitioned to idle, this code would still be executed.
// May make more sense to not handle NextState in the ACD app, and let
// the client app handle it.
SetAgentState(pAgent,
pLCI->dwAddressID,
pAgent->pAddressInfo[pLCI->dwAddressID].dwNextState,
0);

ACDFree(pLCI);

return TRUE;

}


////////////////////////////////////////////////////////////////////////////
//
// void HandleLineProxyRequest(HLINE hLine,
// LPLINEPROXYREQUEST pProxyRequest)
//
// Handles LINE_PROXYREQUEST message
// Just dispatches to appropriate functions
//
////////////////////////////////////////////////////////////////////////////
void HandleLineProxyRequest(HLINE hLine,
LPLINEPROXYREQUEST pProxyRequest)
{
PAGENT pAgent;
LRESULT lResult;

pAgent = GetAgentFromName((LPTSTR)(((LPBYTE)pProxyRequest) +
pProxyRequest->dwClientUserNameOffset));

if (!pAgent)
{
lineProxyResponse(hLine,
pProxyRequest,

LINEERR_INVALAGENTID); 

return;
}

switch (pProxyRequest->dwRequestType)
{
case LINEPROXYREQUEST_SETAGENTGROUP:

lResult = SetGroupList(pAgent,
pProxyRequest->SetAgentGroup.dwAddressID,
&pProxyRequest->SetAgentGroup.GroupList);

lineProxyResponse(hLine,
pProxyRequest,
lResult);

return;

case LINEPROXYREQUEST_SETAGENTSTATE:

lResult = SetAgentState(pAgent,
pProxyRequest->SetAgentState.dwAddressID,
pProxyRequest->SetAgentState.dwAgentState,
pProxyRequest->SetAgentState.dwNextAgentState);

lineProxyResponse(hLine,
pProxyRequest,
lResult);

break;

case LINEPROXYREQUEST_SETAGENTACTIVITY:

lResult = SetAgentActivity(pAgent,
pProxyRequest->SetAgentActivity.dwAddressID,
pProxyRequest->SetAgentActivity.dwActivityID);

lineProxyResponse(hLine,
pProxyRequest,
lResult);

break;

case LINEPROXYREQUEST_GETAGENTSTATUS:

lResult = GetAgentStatus(pAgent,
pProxyRequest->GetAgentStatus.dwAddressID,
&pProxyRequest->GetAgentStatus.AgentStatus);

lineProxyResponse(hLine,
pProxyRequest,
lResult);

break;

case LINEPROXYREQUEST_GETAGENTCAPS:

if ((hLine == pAgent->hLine) &&
(pProxyRequest->GetAgentCaps.dwAddressID < pAgent->dwNumAddresses))
{
lResult = MakeAgentCaps(pAgent,
&pProxyRequest->GetAgentCaps.AgentCaps);
}
else
{
lResult = LINEERR_BADDEVICEID;
}

lineProxyResponse(hLine,
pProxyRequest,
lResult);
break;

case LINEPROXYREQUEST_GETAGENTACTIVITYLIST:

lResult = MakeAgentActivityList(pAgent,
&pProxyRequest->GetAgentActivityList.ActivityList);

lineProxyResponse(hLine,
pProxyRequest,
lResult);

break;

case LINEPROXYREQUEST_GETAGENTGROUPLIST:

lResult = MakeGroupList(pAgent,
&pProxyRequest->GetAgentGroupList.GroupList);

lineProxyResponse(hLine,
pProxyRequest,
lResult);
return;


}
return;
}

/////////////////////////////////////////////////////////////
//
// void HandleLineCallState(DWORD dwDevice,
//
// Handles callstate messages we are interested in
//
/////////////////////////////////////////////////////////////
void HandleLineCallState(DWORD dwDevice,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3)
{
switch (dwParam1)
{
case LINECALLSTATE_OFFERING:
{
LPLINECALLSTATUS pLCS;

// get the call privilege.
// NOTE: the new LINE_APPNEWCALL message notifies applications
// of their priv for new calls not created by app
pLCS = LineGetCallStatus((HCALL)dwDevice);
if (!pLCS)
{
break;
}

if (pLCS->dwCallPrivilege & LINECALLPRIVILEGE_OWNER)
{
HandleOffering((HCALL)dwDevice);
}

ACDFree(pLCS);

break;
}
case LINECALLSTATE_CONNECTED:
break;

case LINECALLSTATE_DISCONNECTED:
break;

case LINECALLSTATE_IDLE:

HandleIdle((HCALL)dwDevice);

break;

case LINECALLSTATE_BUSY:
break;

default:
break;
}
}


///////////////////////////////////////////////////////////////////////
// TAPI message handlers. For this sample, they don't
// do anything
///////////////////////////////////////////////////////////////////////
void HandleLineDevState(DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3)
{
}


void HandleLineReply(DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3)
{
}
void HandleLineCallInfo(DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3)
{
}

void HandleLineClose(DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3)
{
}


//////////////////////////////////////////////////////////////////////////////////
//
// LineCallback() - TAPI callback function
//
//////////////////////////////////////////////////////////////////////////////////
VOID CALLBACK LineCallback (DWORD hDevice,
DWORD dwMsg,
DWORD dwCallbackInstance,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3)
{
switch(dwMsg)
{
case LINE_PROXYREQUEST:
HandleLineProxyRequest((HLINE) hDevice,
(LPLINEPROXYREQUEST)dwParam1);
return;

case LINE_LINEDEVSTATE:
HandleLineDevState(dwParam1,
dwParam2,
dwParam3);
return;
case LINE_REPLY:
HandleLineReply(dwParam1,
dwParam2,
dwParam3);
return;
case LINE_CALLSTATE:
HandleLineCallState(hDevice,
dwParam1,
dwParam2,
dwParam3);
return;
case LINE_CALLINFO:
HandleLineCallInfo(dwParam1,
dwParam2,
dwParam3);
return;
case LINE_CLOSE:
HandleLineClose(dwParam1,
dwParam2,
dwParam3);
return;

default:
return;
}
}