//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
#include <windows.h>
#include <winbase.h>                                                /* for "Sleep" */
#include <tchar.h>
#include <tapi.h>
#include <katoex.h>
#include <tux.h>
#include "tapiinfo.h"
#include "tuxmain.h"
extern CKato* g_pKato;
void MonitorLines(void);
long DropCall(HCALL);

LineInfoStr LineInfo[MAX_OPEN_LINES];
DWORD dwMediaMode = LINEMEDIAMODE_DATAMODEM;
DWORD dwTestRunTime = RUN_FOREVER;                     /* test length (milliseconds) */
DWORD dwConnectTimeout = RUN_FOREVER;    /* time to hold call after connection made */

DWORD dwCallsReceived = 0;                               /* number of calls received */
DWORD dwCallsConnected = 0;                              /* number of calls connected */
DWORD dwTotalErrors = 0;                       /* total errors reported by program */
HINSTANCE hInst;                                     /* handle to current instance */
DWORD nOpenLines = 0;
#define  DEFAULT_THREAD_COUNT 0

TESTPROCAPI DefaultTest         (UINT uMsg, TPPARAM tpParam, LPFUNCTION_TABLE_ENTRY lpFTE)
{
    UNREFERENCED_PARAMETER(lpFTE);
    if (uMsg == TPM_QUERY_THREAD_COUNT) 
    {
        ((LPTPS_QUERY_THREAD_COUNT)tpParam)->dwThreadCount = DEFAULT_THREAD_COUNT;
        return TPR_HANDLED;
    } 
    else if (uMsg != TPM_EXECUTE) 
    {
        return TPR_NOT_HANDLED;
    }

    MonitorLines();
    if (dwTotalErrors > 0)
        return TPR_FAIL;
    else if (dwCallsReceived == 0 && dwCallsConnected == 0)
    {
        g_pKato->Log(LOG_COMMENT, TEXT("No calls received, skipping test.\r\n"));
        return TPR_SKIP;
    }
    else return TPR_PASS;
}

void MonitorLines(void)
{
    long lRet;                                       /* return value from function */
    HLINEAPP hLineApp;                                  /* line application handle */
    DWORD dwAPIVersion = TAPI_CURRENT_VERSION;          /* negotiated TAPI version */
    DWORD dwNumDevs;                           /* number of line devices available */
    DWORD dwCurrentTime, dwFinishTime,dwStartTime;/* times read via GetTickCount() */
    DWORD i, j;
    BOOL bAcceptMoreCalls = TRUE;              /* set to FALSE when timer runs out */
    DWORD nActiveCalls = 0;                    /* number of currently active calls */
    BOOL bOverflowOccured = FALSE; /* checks if overflow occured in GetTickCount() */

    TCHAR szBuff[512];                                /* buffer for error messages */
    LINEINITIALIZEEXPARAMS LineInitializeExParams;      /* for TAPI initialization */
    LINEEXTENSIONID ExtensionID;                  /* for "lineNegotiateAPIVersion" */
    LINEMESSAGE LineMessage;

    /* Timer initialization (Note: timers do not compensate */
    /*   for overflow in GetTickCount() every 49.7 days)    */
    dwStartTime = GetTickCount();
    dwCurrentTime = GetTickCount();
    g_pKato->Log(LOG_COMMENT, TEXT("Server started: tick count = %lu"), dwCurrentTime);
    if (dwTestRunTime != RUN_FOREVER)
    {
        dwFinishTime = dwCurrentTime + dwTestRunTime;

        if (dwFinishTime < dwCurrentTime)
            bOverflowOccured = TRUE;
    }
    else
    {
        g_pKato->Log(LOG_COMMENT, TEXT("Server looping forever"));
        dwFinishTime = 0xFFFFFFFF;
    }

    // Initialize TAPI:
    g_pKato->Log(LOG_COMMENT, TEXT("Calling \"lineInitializeEx\":"));    
    LineInitializeExParams.dwTotalSize = sizeof(LINEINITIALIZEEXPARAMS);
    LineInitializeExParams.dwOptions = LINEINITIALIZEEXOPTION_USEEVENT;

    lRet = lineInitializeEx(&hLineApp, hInst, NULL, NULL, &dwNumDevs, &dwAPIVersion,
        &LineInitializeExParams);
    if (lRet)
    {
        g_pKato->Log(LOG_FAIL, TEXT("\"lineInitializeEx\" failed: %s"),
            FormatLineError(lRet, szBuff));
        dwTotalErrors++;
        return;
    }
    g_pKato->Log(LOG_DETAIL, TEXT("\"lineInitializeEx\" succeeded: %lu devices available"),
        dwNumDevs);

    // Negotiate API version (the version returned from "lineInitializeEx"
    //   is not necessarily valid for NT):
    g_pKato->Log(LOG_DETAIL, TEXT("Negotiating API version for device %lu:"),
        LineInfo[0].dwDeviceID);
    lRet = lineNegotiateAPIVersion(hLineApp, LineInfo[0].dwDeviceID, 0x00010000,
        TAPI_CURRENT_VERSION, &dwAPIVersion, &ExtensionID);
    if (lRet)
    {
        g_pKato->Log(LOG_FAIL, TEXT("\"lineNegotiateAPIVersion\" failed: %s"),
            FormatLineError(lRet, szBuff));
        ++dwTotalErrors;
        lineShutdown(hLineApp);
        return;
    }
    g_pKato->Log(LOG_COMMENT, TEXT("API version used: %.8X"), dwAPIVersion);    

    for (i = 0; i < nOpenLines; ++i)
    {
        if (LineInfo[i].dwDeviceID >= dwNumDevs)
        {                                                // skip out-of-range devices
            g_pKato->Log(LOG_COMMENT, TEXT("Device ID (%lu) is outside permitted range (0 - %lu) - skipping"),
                LineInfo[i].dwDeviceID, dwNumDevs - 1);
            for (j = i + 1; j < nOpenLines; ++j)
                LineInfo[j - 1].dwDeviceID = LineInfo[j].dwDeviceID;
            nOpenLines--;
        }
    }
    // TODO - This should be replaced with a call to "lineOpen" using LINEMAPPER 
    //   to select a line to be monitored.
    if (nOpenLines == 0)
    {
        g_pKato->Log(LOG_COMMENT, TEXT("No TAPI devices selected - using device 0 by default"));
        nOpenLines++;
        LineInfo[0].dwDeviceID = 0;
    }

    for (i = 0; i < nOpenLines; ++i)
    {
        g_pKato->Log(LOG_COMMENT, TEXT("Opening line %lu of %lu: TAPI device %lu"), i + 1,
            nOpenLines, LineInfo[i].dwDeviceID);
        // Open line - pass in array index as callback data
        lRet = lineOpen(hLineApp, LineInfo[i].dwDeviceID, &(LineInfo[i].hLine),
            dwAPIVersion, 0, i, LINECALLPRIVILEGE_OWNER, dwMediaMode, NULL);
        if (lRet)
        {
            g_pKato->Log(LOG_FAIL, TEXT("\"lineOpen\" failed: %s"), FormatLineError(lRet, szBuff));
            ++dwTotalErrors;
            continue;
        } 
        g_pKato->Log(LOG_COMMENT, TEXT("Device %d successfully opened - handle = %lu"),
            LineInfo[i].dwDeviceID, LineInfo[i].hLine);
    }

    while ((bAcceptMoreCalls) || (nActiveCalls > 0))
    {
        Sleep(50);                         // wait to let other applications catch up
        dwCurrentTime = GetTickCount();

        for (i = 0; i < nOpenLines; ++i)
        {                                               // Check timeout for each call
            if ((dwTestRunTime != RUN_FOREVER) &&
                (dwCurrentTime > LineInfo[i].dwTime + dwTestRunTime) &&
                (LineInfo[i].dwStatus == LINECALLSTATE_CONNECTED) &&
                (((bOverflowOccured == TRUE) && (dwCurrentTime < dwStartTime)) || (bOverflowOccured == FALSE)) )
            {
                g_pKato->Log(LOG_COMMENT, TEXT("Call on line %lu expired - dropping"),
                    LineInfo[i].dwDeviceID);
                DropCall(LineInfo[i].hCall);
                LineInfo[i].dwStatus = LINECALLSTATE_DISCONNECTED;
            }
        }

        if ((dwTestRunTime != RUN_FOREVER) && (dwCurrentTime > dwFinishTime) &&
            (((bOverflowOccured == TRUE) && (dwCurrentTime < dwStartTime)) || (bOverflowOccured == FALSE)) )
        {                                                        // Check program timeout
            if (bAcceptMoreCalls)
            {
                g_pKato->Log(LOG_COMMENT, TEXT("Time to quit - dropping any active calls"));
                bAcceptMoreCalls = FALSE;
                for (i = 0; i < nOpenLines; ++i)
                {
                    if ((LineInfo[i].dwStatus != LINECALLSTATE_DISCONNECTED) &&
                        (LineInfo[i].dwStatus != LINECALLSTATE_IDLE) &&
                        (LineInfo[i].dwStatus != 0))
                    {
                        g_pKato->Log(LOG_COMMENT, TEXT("Dropping call on line %lu"),
                            LineInfo[i].dwDeviceID);
                        DropCall(LineInfo[i].hCall);
                        LineInfo[i].dwStatus = LINECALLSTATE_DISCONNECTED;
                    }
                }
            }

            if ((dwCurrentTime - dwFinishTime > 15000) && 
                (((bOverflowOccured == TRUE) && (dwCurrentTime < dwStartTime)) || (bOverflowOccured == FALSE)))
            {
                g_pKato->Log(LOG_COMMENT, TEXT("Time to quit application"));
                nActiveCalls = 0;         // Force to shutdown if hung for 15 seconds
            }
        }

        lRet = lineGetMessage(hLineApp, &LineMessage, 0);
        if (lRet)
        {
            //  "lineGetMessage" will return LINEERR_OPERATIONFAILED
            //    if there is no message waiting in the queue
            if (lRet != LINEERR_OPERATIONFAILED)
            {
                g_pKato->Log(LOG_FAIL, TEXT("\"lineGetMessage\" failed: %s"),
                    FormatLineError(lRet, szBuff));
                ++dwTotalErrors;
            }
            continue;
        }
        g_pKato->Log(LOG_COMMENT, TEXT("Message received: %s"),
            FormatLineCallback(szBuff, LineMessage.hDevice, LineMessage.dwMessageID,
            LineMessage.dwCallbackInstance, LineMessage.dwParam1, LineMessage.dwParam2,
            LineMessage.dwParam3));

        // Sanity check: dwCallbackInstance should be index into "LineInfo"
        if (LineMessage.dwCallbackInstance >= nOpenLines)
        {
            g_pKato->Log(LOG_FAIL, TEXT("Callback ID %lu does not match an open line"),
                LineMessage.dwCallbackInstance);
            if (LineMessage.dwMessageID == LINE_APPNEWCALL)
                DropCall((HCALL)LineMessage.dwParam2);
            dwTotalErrors++;
            continue;
        }

        i = LineMessage.dwCallbackInstance;
        switch (LineMessage.dwMessageID)
        {
        case LINE_APPNEWCALL:
            if (!bAcceptMoreCalls)
            {
                g_pKato->Log(LOG_COMMENT, TEXT("Quitting time - can't accept call"));
                DropCall((HCALL)LineMessage.dwParam2);
                break;
            }
            LineInfo[i].hCall = (HCALL)LineMessage.dwParam2;
            LineInfo[i].dwTime = dwCurrentTime;
            g_pKato->Log(LOG_COMMENT, TEXT("Call #%lu received at %lu - %lu active call(s)"),
                ++dwCallsReceived, LineInfo[i].dwTime, ++nActiveCalls);
            break;

        case LINE_CALLSTATE:
            LineInfo[i].dwStatus = LineMessage.dwParam1;
            switch (LineMessage.dwParam1)
            {
            case LINECALLSTATE_OFFERING:
                g_pKato->Log(LOG_COMMENT, TEXT("Answering call:"));
                if ((lRet = lineAnswer(LineInfo[i].hCall, NULL, 0)) < 0)
                {
                    g_pKato->Log(LOG_FAIL, TEXT("\"lineAnswer\" failed: %s"),
                        FormatLineError(lRet, szBuff));
                    ++dwTotalErrors;
                }
                else
                    g_pKato->Log(LOG_COMMENT, TEXT("\"lineAnswer\" succeeded; request ID = %lu"),
                    lRet);
                break;

            case LINECALLSTATE_CONNECTED:
                g_pKato->Log(LOG_COMMENT, TEXT("Call connected - connection #%lu"),
                    ++dwCallsConnected);
                g_pKato->Log(LOG_COMMENT, TEXT("Connected after %lu milliseconds"),
                    dwCurrentTime - LineInfo[i].dwTime);
                break;

            case LINECALLSTATE_DISCONNECTED:
                g_pKato->Log(LOG_COMMENT, TEXT("Call disconnected on line %lu"),
                    LineInfo[i].dwDeviceID);

                // NT doesn't always send CALLSTATE_IDLE so we deallocate the call on DISCONNECTED
                // CE "lineDeallocateCall" fails unless we are in CALLSTATE_IDLE
#ifdef UNDER_CE
                break;

            case LINECALLSTATE_IDLE:
#endif
                g_pKato->Log(LOG_COMMENT, TEXT("Call on line %lu dropped - %lu active call(s)"),
                    LineInfo[i].dwDeviceID, --nActiveCalls);
                lRet = lineDeallocateCall(LineInfo[i].hCall);
                if (lRet)
                {
                    g_pKato->Log(LOG_FAIL, TEXT("\"lineDeallocateCall\" failed: %s"),
                        FormatLineError(lRet, szBuff));
                    ++dwTotalErrors;
                }
                else
                {
                    g_pKato->Log(LOG_DETAIL, TEXT("Call deallocated"));
                    LineInfo[i].hCall = NULL;
                }
                break;
            }
            break;

        case LINE_REPLY:
            if (LineMessage.dwParam2)
            {
                g_pKato->Log(LOG_FAIL, TEXT("LINE_REPLY error: %s"),
                    FormatLineError(LineMessage.dwParam2, szBuff));
                ++dwTotalErrors;
            }
            break;

        }   // end switch (LineMessage.dwMessageID)
    }   // end while ((bAcceptMoreCalls) ||(bCallsActive))

    for (i = 0; i < nOpenLines; ++i)
    {
        if (LineInfo[i].hLine)
        {
            lRet = lineClose(LineInfo[i].hLine);
            if (lRet)
            {
                g_pKato->Log(LOG_FAIL, TEXT("\"lineClose\" failed for line %lu: %s"),
                    LineInfo[i].dwDeviceID, FormatLineError(lRet, szBuff));
                ++dwTotalErrors;
            }
            else
            {
                g_pKato->Log(LOG_COMMENT, TEXT("Line %lu successfully closed"),
                    LineInfo[i].dwDeviceID);
                LineInfo[i].hLine = NULL;
            }
        }
    }
    lRet = lineShutdown(hLineApp);
    if (lRet)
    {
        g_pKato->Log(LOG_FAIL, TEXT("\"lineShutdown\" failed: %s"),
            FormatLineError(lRet, szBuff));
        ++dwTotalErrors;
    }
    else
        g_pKato->Log(LOG_DETAIL, TEXT("\"lineShutdown\" completed"));
}

long DropCall(HCALL hCall)
{
    long lRet;                                       /* return value from function */
    TCHAR szBuff[64];                                 /* buffer for error messages */

    g_pKato->Log(LOG_COMMENT, TEXT("Dropping call:"));
    if ((lRet = lineDrop(hCall, NULL, 0)) < 0)
    {
        g_pKato->Log(LOG_FAIL, TEXT("\"lineDrop\" failed: %s"),
            FormatLineError(lRet, szBuff));
        ++dwTotalErrors;
    }
    else
        g_pKato->Log(LOG_DETAIL, TEXT("\"lineDrop\" succeeded: request ID = %lu"), lRet);

    return lRet;
}
