/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    tapiutil.c

Abstract:

    Functions for working with TAPI

Environment:

	Fax configuration applet

Revision History:

	03/16/96 -davidx-
		Created it.

	mm/dd/yy -author-
		description

NOTE:

    We are calling W-version of TAPI APIs explicitly here because
    tapi.h doesn't properly expand them to A- or W-version.
    
--*/

#include "faxcpl.h"
#include <tapi.h>


//
// Global variables used for accessing TAPI services
//

static HLINEAPP          tapiLineApp = NULL;
static DWORD             tapiVersion = TAPI_CURRENT_VERSION;
static LPLINECOUNTRYLIST pLineCountryList = NULL;



BOOL
GetCountries(
    VOID
    )

/*++

Routine Description:

    Return a list of countries from TAPI

Arguments:

    NONE

Return Value:

    TRUE if successful, FALSE if there is an error

NOTE:

    We cache the result of lineGetCountry here since it's incredibly slow.
    This function must be invoked inside a critical section since it updates
    globally shared information.

--*/

#define INITIAL_SIZE_ALL_COUNTRY    22000   // Initial buffer size

{
    DWORD   cbNeeded = INITIAL_SIZE_ALL_COUNTRY;
    INT     repeatCnt = 0;
    LONG    status;

    if (pLineCountryList == NULL) {

        while (TRUE) {

            //
            // Free existing buffer and allocate a new buffer of required size
            //

            MemFree(pLineCountryList);
    
            if (! (pLineCountryList = MemAlloc(cbNeeded))) {

                Error(("Memory allocation failed\n"));
                break;
            }

            //
            // Call TAPI to get the list of countries
            //

            pLineCountryList->dwTotalSize = cbNeeded;
            status = lineGetCountry(0, tapiVersion, pLineCountryList);

            //
            // Retries with a larger buffer size if our initial estimate was too small
            //

            if ((pLineCountryList->dwNeededSize > pLineCountryList->dwTotalSize) &&
                (status == NO_ERROR ||
                 status == LINEERR_STRUCTURETOOSMALL ||
                 status == LINEERR_NOMEM) &&
                (repeatCnt++ == 0))
            {
                cbNeeded = pLineCountryList->dwNeededSize + 1;
                Warning(("LINECOUNTRYLIST size: %d\n", cbNeeded));
                continue;
            }

            if (status != NO_ERROR) {

                Error(("lineGetCountry failed: %x\n", status));
                MemFree(pLineCountryList);
                pLineCountryList = NULL;

            } else
                Verbose(("Number of countries: %d\n", pLineCountryList->dwNumCountries));

            break;
        }
    }

    return pLineCountryList != NULL;
}



VOID CALLBACK
TapiLineCallback(
    DWORD   hDevice,
    DWORD   dwMessage,
    DWORD   dwInstance,
    DWORD   dwParam1,
    DWORD   dwParam2,
    DWORD   dwParam3
    )

/*++

Routine Description:

    TAPI line callback function: Even though we don't actually have anything
    to do here, we must provide a callback function to keep TAPI happy.

Arguments:

    hDevice     - Line or call handle
    dwMessage   - Reason for the callback
    dwInstance  - LINE_INFO index
    dwParam1    - Callback parameter #1
    dwParam2    - Callback parameter #2
    dwParam3    - Callback parameter #3

Return Value:

    NONE

--*/

{
}



BOOL
InitTapiService(
    VOID
    )

/*++

Routine Description:

    Perform TAPI initialization if necessary

Arguments:

    NONE

Return Value:

    TRUE if successful, FALSE if there is an error

--*/

{
    DWORD   nLineDevs;
    LONG    status;

    if (tapiLineApp == NULL) {

        status = lineInitialize(&tapiLineApp,
                               ghInstance,
                               TapiLineCallback,
                               "Fax Configuration",
                               &nLineDevs);

        if (status != NO_ERROR) {

            Error(("lineInitialize failed: %x\n", status));
            tapiLineApp = NULL;

        } else {

            //
            // Don't call lineNegotiateAPIVersion if nLineDevs is 0.
            //

            Verbose(("Number of lines: %d\n", nLineDevs));

            if (nLineDevs > 0) {

                LINEEXTENSIONID lineExtensionID;

                status = lineNegotiateAPIVersion(tapiLineApp,
                                                 0,
                                                 TAPI_CURRENT_VERSION,
                                                 TAPI_CURRENT_VERSION,
                                                 &tapiVersion,
                                                 &lineExtensionID);

                if (status != NO_ERROR) {

                    Error(("lineNegotiateAPIVersion failed: %x\n", status));
                    tapiVersion = TAPI_CURRENT_VERSION;
                }
            }

            //
            // Get a list of countries from TAPI
            //

            GetCountries();
        }
    }

    return tapiLineApp != NULL;
}



VOID
DeinitTapiService(
    VOID
    )

/*++

Routine Description:

    Perform TAPI deinitialization if necessary

Arguments:

    NONE

Return Value:

    NONE

--*/

{
    MemFree(pLineCountryList);
    pLineCountryList = NULL;

    if (tapiLineApp) {

        lineShutdown(tapiLineApp);
        tapiLineApp = NULL;
    }
}



LPLINECOUNTRYENTRY
FindCountry(
    DWORD   countryId
    )

/*++

Routine Description:

    Find the specified country from a list of all countries and
    return a pointer to the corresponding LINECOUNTRYENTRY structure

Arguments:

    countryId - Specifies the country ID we're interested in

Return Value:

    Pointer to a LINECOUNTRYENTRY structure corresponding to the specified country ID
    NULL if there is an error

--*/

{
    LPLINECOUNTRYENTRY  pEntry;
    DWORD               index;

    if (pLineCountryList == NULL || countryId == 0)
        return NULL;

    //
    // Look at each LINECOUNTRYENTRY structure and compare its country ID with
    // the specified country ID
    //

    pEntry = (LPLINECOUNTRYENTRY)
        ((PBYTE) pLineCountryList + pLineCountryList->dwCountryListOffset);

    for (index=0; index < pLineCountryList->dwNumCountries; index++, pEntry++) {

        if (pEntry->dwCountryID == countryId)
            return pEntry;
    }

    return NULL;
}



INT
AreaCodeRules(
    LPLINECOUNTRYENTRY  pEntry
    )

/*++

Routine Description:

    Given a LINECOUNTRYENTRY structure, determine if area code is needed in that country

Arguments:

    pEntry - Points to a LINECOUNTRYENTRY structure

Return Value:

    AREACODE_DONTNEED - Area code is not used in the specified country
    AREACODE_OPTIONAL - Area code is optional in the specified country
    AREACODE_REQUIRED - Area code is required in the specified country

--*/

#define AREACODE_DONTNEED   0
#define AREACODE_REQUIRED   1
#define AREACODE_OPTIONAL   2

{
    if ((pEntry != NULL) &&
        (pEntry->dwLongDistanceRuleSize != 0) &&
        (pEntry->dwLongDistanceRuleOffset != 0))
    {
        LPTSTR  pLongDistanceRule;

        //
        // Get the long distance rules for the specified country
        //

        Assert(pLineCountryList != NULL);

        pLongDistanceRule = (LPTSTR)
            ((PBYTE) pLineCountryList + pEntry->dwLongDistanceRuleOffset);

        //
        // Area code is required in this country
        //

        if (_tcschr(pLongDistanceRule, TEXT('F')) != NULL)
            return AREACODE_REQUIRED;

        //
        // Area code is not needed in this country
        //

        if (_tcschr(pLongDistanceRule, TEXT('I')) == NULL)
            return AREACODE_DONTNEED;
    }

    //
    // Default case: area code is optional in this country
    //

    return AREACODE_OPTIONAL;
}



VOID
UpdateAreaCodeField(
    HWND    hwndAreaCode,
    DWORD   countryId
    )

/*++

Routine Description:

    Update any area code text field associated with a country list box

Arguments:

    hwndAreaCode - Specifies the text field associated with the country list box
    countryId - Currently selected country ID

Return Value:

    NONE

--*/

{
    if (hwndAreaCode != NULL) {
    
        if (AreaCodeRules(FindCountry(countryId)) == AREACODE_DONTNEED) {
    
            SendMessage(hwndAreaCode, WM_SETTEXT, 0, (LPARAM) TEXT(""));
            EnableWindow(hwndAreaCode, FALSE);
    
        } else
            EnableWindow(hwndAreaCode, TRUE);
    }
}



VOID
InitCountryListBox(
    HWND    hwndList,
    HWND    hwndAreaCode,
    DWORD   countryId
    )

/*++

Routine Description:

    Initialize the country list box

Arguments:

    hwndList - Handle to the country list box window
    hwndAreaCode - Handle to an associated area code text field
    countryId - Initially selected country ID

Return Value:

    NONE

--*/

#define MAX_COUNTRY_NAME    256

{
    DWORD               index;
    TCHAR               buffer[MAX_COUNTRY_NAME];
    LPLINECOUNTRYENTRY  pEntry;

    //
    // Disable redraw on the list box and reset its content
    //

    SendMessage(hwndList, WM_SETREDRAW, FALSE, 0);
    SendMessage(hwndList, CB_RESETCONTENT, FALSE, 0);

    //
    // Loop through LINECOUNTRYENTRY structures and
    // add the available selections to the country list box.
    //

    if (pLineCountryList != NULL) {

        pEntry = (LPLINECOUNTRYENTRY)
            ((PBYTE) pLineCountryList + pLineCountryList->dwCountryListOffset);

        for (index=0; index < pLineCountryList->dwNumCountries; index++, pEntry++) {

            if (pEntry->dwCountryNameSize && pEntry->dwCountryNameOffset) {

                wsprintf(buffer, TEXT("%s (%d)"),
                         (PBYTE) pLineCountryList + pEntry->dwCountryNameOffset,
                         pEntry->dwCountryCode);

                SendMessage(hwndList,
                            CB_SETITEMDATA,
                            SendMessage(hwndList, CB_ADDSTRING, 0, (LPARAM) buffer),
                            pEntry->dwCountryID);
            }
        }
    }

    //
    // Insert None as the very first selection
    //

    LoadString(ghInstance, IDS_NO_COUNTRY, buffer, MAX_COUNTRY_NAME);
    SendMessage(hwndList, CB_INSERTSTRING, 0, (LPARAM) buffer);
    SendMessage(hwndList, CB_SETITEMDATA, 0, 0);

    //
    // Figure out which item in the list should be selected
    //

    if (pLineCountryList != NULL) {

        for (index=0; index <= pLineCountryList->dwNumCountries; index++) {

            if ((DWORD) SendMessage(hwndList, CB_GETITEMDATA, index, 0) == countryId)
                break;
        }

        if (index > pLineCountryList->dwNumCountries)
            index = countryId = 0;

    } else
        index = countryId = 0;

    SendMessage(hwndList, CB_SETCURSEL, index, 0);
    SendMessage(hwndList, WM_SETREDRAW, TRUE, 0);

    //
    // Update the associated area code text field
    //

    UpdateAreaCodeField(hwndAreaCode, countryId);
}



VOID
SelChangeCountryListBox(
    HWND    hwndList,
    HWND    hwndAreaCode
    )

/*++

Routine Description:

    Handle dialog selection changes in the country list box

Arguments:

    hwndList - Handle to the country list box window
    hwndAreaCode - Handle to an associated area code text field

Return Value:

    NONE

--*/

{
    UpdateAreaCodeField(hwndAreaCode, GetCountryListBoxSel(hwndList));
}



DWORD
GetCountryListBoxSel(
    HWND    hwndList
    )

/*++

Routine Description:

    Return the country ID of the currently selected country in the list box

Arguments:

    hwndList - Handle to the country list box window

Return Value:

    Currently selected country ID

--*/

{
    LONG msgResult;

    if ((msgResult = SendMessage(hwndList, CB_GETCURSEL, 0, 0)) == CB_ERR ||
        (msgResult = SendMessage(hwndList, CB_GETITEMDATA, msgResult, 0)) == CB_ERR)
    {
        return 0;
    }

    return msgResult;
}



DWORD
GetCountryCodeFromCountryID(
    DWORD   countryId
    )

/*++

Routine Description:

    Return a country code corresponding to the specified country ID

Arguments:

    countryId - Specified the interested country ID

Return Value:

    Country code corresponding to the specified country ID

--*/

{
    LPLINECOUNTRYENTRY  pLineCountryEntry;

    pLineCountryEntry = FindCountry(countryId);
    return pLineCountryEntry ? pLineCountryEntry->dwCountryCode : 0;
}



DWORD
GetDefaultCountryID(
    VOID
    )

/*++

Routine Description:

    Return the default country ID for the current location

Arguments:

    NONE

Return Value:

    Default country ID

--*/

#define INITIAL_LINETRANSLATECAPS_SIZE  5000    // Initial buffer size

{
    DWORD               cbNeeded = INITIAL_LINETRANSLATECAPS_SIZE;
    DWORD               countryId = 0;
    LONG                status;
    INT                 repeatCnt = 0;
    LPLINETRANSLATECAPS pTranslateCaps = NULL;

    if (tapiLineApp == NULL)
        return 0;

    while (TRUE) {

        //
        // Free any existing buffer and allocate a new one with larger size
        //

        MemFree(pTranslateCaps);

        if (! (pTranslateCaps = MemAlloc(cbNeeded))) {

            Error(("Memory allocation failed\n"));
            return 0;
        }

        //
        // Get the LINETRANSLATECAPS structure from TAPI
        //
    
        pTranslateCaps->dwTotalSize = cbNeeded;
        status = lineGetTranslateCaps(tapiLineApp, tapiVersion, pTranslateCaps);

        //
        // Retry if our initial estimated buffer size was too small
        //

        if ((pTranslateCaps->dwNeededSize > pTranslateCaps->dwTotalSize) &&
            (status == NO_ERROR ||
             status == LINEERR_STRUCTURETOOSMALL ||
             status == LINEERR_NOMEM) &&
            (repeatCnt++ == 0))
        {
            cbNeeded = pTranslateCaps->dwNeededSize;
            Warning(("LINETRANSLATECAPS size: %d\n", cbNeeded));
            continue;
        }

        break;
    }

    //
    // Find the current location entry
    //

    if (status != NO_ERROR) {
    
        Error(("lineGetTranslateCaps failed: %x\n", status));

    } else if (pTranslateCaps->dwLocationListSize && pTranslateCaps->dwLocationListOffset) {

        LPLINELOCATIONENTRY pLineLocationEntry;
        DWORD               index;

        pLineLocationEntry = (LPLINELOCATIONENTRY)
            ((PBYTE) pTranslateCaps + pTranslateCaps->dwLocationListOffset);

        for (index=0; index < pTranslateCaps->dwNumLocations; index++, pLineLocationEntry++) {

            if (pLineLocationEntry->dwPermanentLocationID == pTranslateCaps->dwCurrentLocationID) {

                countryId = pLineLocationEntry->dwCountryID;
                break;
            }
        }
    }

    MemFree(pTranslateCaps);
    return countryId;
}