#include <windows.h>
#include <windowsx.h>
#include <winuser.h>
#include <wownt32.h>

#include <tapi.h>
#include <tspi.h>

#include "tsp3216.h"
#include "debug.h"


LONG PASCAL TapiThk_ThunkConnect32 (
                                 LPSTR pszDll16,
                                 LPSTR pszDll32,
                                 DWORD hInstance,
                                 DWORD lReason
                               );

LONG PASCAL TapiFThk_ThunkConnect32 (
                                 LPSTR pszDll16,
                                 LPSTR pszDll32,
                                 DWORD hInstance,
                                 DWORD lReason
                               );

LONG PASCAL Tapi32_ThunkConnect32 (
                                 LPSTR pszDll16,
                                 LPSTR pszDll32,
                                 DWORD hInstance,
                                 DWORD lReason
                               );

const char pszDll16[] = "TSP3216S.DLL";
const char pszDll32[] = "TSP3216L.TSP";



//***************************************************************************
//***************************************************************************
enum {
      MYMSG_STARTER = TSP3216L_MESSAGE,
      MYMSG_PROVIDERINIT,
      MYMSG_PROVIDERSHUTDOWN,
      MYMSG_PROVIDERCONFIG,
      MYMSG_LINECONFIGDIALOG,
      MYMSG_LINECONFIGDIALOGEDIT,
      MYMSG_PHONECONFIGDIALOG,
      MYMSG_LINEMAKECALL
     };


//***************************************************************************
DWORD cProcessAttach = 0;


//***************************************************************************
HWND ghWnd = NULL;
CRITICAL_SECTION gcs;


//***************************************************************************
TCHAR gszProviderKey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\"
                         "Telephony\\Providers\\Provider";

DWORD gdwPermanentProviderID;


DWORD gdwThreadParms[2];
enum {
       THE_HINST,
       THE_LREASON
     };



//***************************************************************************
LRESULT CALLBACK MyWndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam );



//***************************************************************************
//***************************************************************************
//***************************************************************************
LPVOID TspAlloc( UINT nSize )
{
   return LocalAlloc( LPTR, nSize );
}



LPVOID TspFree( LPVOID p )
{
   return LocalFree( p );
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
void InitThunks( void )
{
            TapiThk_ThunkConnect32( 
                                    (LPSTR)pszDll16,
                                    (LPSTR)pszDll32,
                                    gdwThreadParms[THE_HINST],
                                    gdwThreadParms[THE_LREASON]
                                  );

            TapiFThk_ThunkConnect32(
                                    (LPSTR)pszDll16,
                                    (LPSTR)pszDll32,
                                    gdwThreadParms[THE_HINST],
                                    gdwThreadParms[THE_LREASON]
                                  );

            Tapi32_ThunkConnect32( 
                                    (LPSTR)pszDll16,
                                    (LPSTR)pszDll32,
                                    gdwThreadParms[THE_HINST],
                                    gdwThreadParms[THE_LREASON]
                                  );

            InitializeCriticalSection( &gcs );
}


//***************************************************************************
//***************************************************************************
//***************************************************************************

UINT PASCAL NewData( void );


void FreeThunks( void )
{
   HINSTANCE hInst;
   hInst = (HINSTANCE)NewData();
   FreeLibrary16( hInst );
   FreeLibrary16( hInst );
   FreeLibrary16( hInst );


   DeleteCriticalSection( &gcs );

}


//***************************************************************************
//***************************************************************************
//***************************************************************************
DWORD WINAPI TheUIThread( LPVOID lpThreadParameter )
{
   MSG msg;

   WNDCLASS wndclass = {
                  0,
                  MyWndProc,
                  0,
                  0,
                  (HANDLE)gdwThreadParms[THE_HINST],
                  0,
                  0,
                  0,
                  0,
                  "Tsp3216LWindowClass"
               };


DBGOUT((2, "The UI thread"));


   //
   // We need _another_ goddamn window.
   //

   RegisterClass( &wndclass );

   ghWnd = CreateWindow(
         "Tsp3216LWindowClass",
         "Tsp3216LWindow",
         0,
         0,
         0,
         0,
         0,
         0,
         NULL,
         (HANDLE)gdwThreadParms[THE_HINST],
         NULL
      );


DBGOUT((2, "Starting message loop in ui thread"));

    while (GetMessage(&msg, 0, 0, 0) != 0)
        {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
        }

DBGOUT((2, "Done message loop in ui thread"));

    ghWnd = NULL;

    return (msg.wParam);
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
LONG WINAPI DllEntryPoint(
                      DWORD hModule,
                      DWORD lReason,
                      DWORD lContext
                    )
{


DBGOUT((2, "Entering DllEntryPoint  reason=0x%08lx", lReason));


	switch ( lReason )
   {
      case 0:
      {
#if DBG
{
TCHAR cName[MAX_PATH];
TCHAR buf[256];
GetModuleFileName( NULL, cName, MAX_PATH);
wsprintf(buf, "DllEntryPoint - 0 process detach [%s]\r\n", cName);
OutputDebugString(buf);
}
#endif


         FreeThunks();
      }
      break;



      case 1:
      {
#if DBG
{
TCHAR cName[MAX_PATH];
TCHAR buf[256];
GetModuleFileName( NULL, cName, MAX_PATH);
wsprintf(buf, "DllEntryPoint - 1 process attach [%s]\r\n", cName);
OutputDebugString(buf);
}
#endif


         //
         // Yeah, I know it's not threadsafe.  Ask me if I care.
         //
         gdwThreadParms[THE_HINST] = (DWORD)hModule;
         gdwThreadParms[THE_LREASON] = (DWORD)lReason;


         InitThunks();
      }
      break;

   }

DBGOUT((2, "Leaving DllEntryPoint"));

   return TRUE;
}


//****************************************************************************
//****************************************************************************
//****************************************************************************
LONG PASCAL TSPI_lineConfigDialog (
                                    DWORD dwDeviceID,
                                    HWND hwndOwner,
                                    LPCSTR lpszDeviceClass
                                  );

LONG PASCAL TSPI_lineConfigDialogEdit(
                                DWORD dwDeviceID,
                                HWND hwndOwner,
                                LPCSTR lpszDeviceClass,
                                LPVOID  lpDeviceConfigIn,
                                DWORD dwSize,
                                LPVARSTRING lpDeviceConfigOut
                              );


LONG
TSPIAPI
TSPI_providerGenericDialogData(
                                DWORD dwObjectID,
                                DWORD dwObjectType,
                                LPVOID lpParams,
                                DWORD dwSize
                              )
{
//   LONG lResult = LINEERR_OPERATIONUNAVAIL;


   DBGOUT((2, "In TSPI_providerGenericDialogData"));

   DBGOUT((11, "   Msg=0x%08lx", ((LPDWORD)lpParams)[0]));

//   return SendMessage( ghWnd, ((LPDWORD)lpParams)[0], 0, (LPARAM)&(((LPDWORD)lpParams)[1]) );
//   return 
            PostMessage( ghWnd, ((LPDWORD)lpParams)[0], 0, (LPARAM)&(((LPDWORD)lpParams)[1]) );
     return 0;

//   return lResult;
}


//****************************************************************************
//****************************************************************************
//****************************************************************************
LONG
TSPIAPI
TSPI_providerUIIdentify(
                         LPSTR pszUIDllName
                       )
{

   DBGOUT((2, "In TSPI_providerUIIdentify"));

   lstrcpy( pszUIDllName, "TSP3216L.TSP" );

   return 0;

}


//****************************************************************************
//****************************************************************************
//****************************************************************************
LONG
TSPIAPI
TUISPI_lineConfigDialog(
                         TUISPIDLLCALLBACK lpfnUIDLLCallback,
                         DWORD dwDeviceID,
                         HWND  hwndOwner,
                         LPCSTR lpszDeviceClass
                       )
{
   DWORD dwParms[] = {
                        MYMSG_LINECONFIGDIALOG,
                        dwDeviceID,
                        (DWORD)hwndOwner,
                        (DWORD)lpszDeviceClass
                      };


//BUGBUG: Can't pass strings across a callback

   DBGOUT((2, "In TUISPI_lineConfigDialog"));
   DBGOUT((11, "  dwDeviceID=0x%08lx", dwDeviceID));
   DBGOUT((11, "  hwndOwner =0x%08lx", hwndOwner));
   DBGOUT((11, "  lpszDeviceClass=0x%08lx", lpszDeviceClass));

   (*lpfnUIDLLCallback)(
                      dwDeviceID,
                      TUISPIDLL_OBJECT_LINEID,
                      dwParms,
                      sizeof(dwParms)
                    );

//BUGBUG how do we wait and/or return error code?
   return 0;

}


//****************************************************************************
//****************************************************************************
//****************************************************************************
LONG
TSPIAPI
TUISPI_lineConfigDialogEdit(
                             TUISPIDLLCALLBACK lpfnUIDLLCallback,
                             DWORD dwDeviceID,
                             HWND  hwndOwner,
                             LPCSTR lpszDeviceClass,
                             LPVOID const lpDeviceConfigIn,
                             DWORD dwSize,
                             LPVARSTRING lpDeviceConfigOut
                           )
{
   DWORD dwParms[] = {
                        MYMSG_LINECONFIGDIALOGEDIT,
                        dwDeviceID,
                        (DWORD)hwndOwner,
                        (DWORD)lpszDeviceClass,
                        (DWORD)lpDeviceConfigIn,
                        dwSize,
                        (DWORD)lpDeviceConfigOut
                      };


   DBGOUT((2, "In TUISPI_lineConfigDialogEdit"));

   (*lpfnUIDLLCallback)(
                      dwDeviceID,
                      TUISPIDLL_OBJECT_LINEID,
                      dwParms,
                      sizeof(dwParms)
                    );

//BUGBUG how do we wait and/or return error code?
   return 0;


}


//****************************************************************************
//****************************************************************************
//****************************************************************************
LONG
TSPIAPI
TSPI_phoneConfigDialog(
                         DWORD dwDeviceID,
                         HWND  hwndOwner,
                         LPCSTR lpszDeviceClass
                       );

LONG
TSPIAPI
TUISPI_phoneConfigDialog(
                         TUISPIDLLCALLBACK lpfnUIDLLCallback,
                         DWORD dwDeviceID,
                         HWND  hwndOwner,
                         LPCSTR lpszDeviceClass
                       )
{
   DWORD dwParms[] = {
                        MYMSG_PHONECONFIGDIALOG,
                        dwDeviceID,
                        (DWORD)hwndOwner,
                        (DWORD)lpszDeviceClass
                      };


//BUGBUG: Can't pass strings across a callback

   DBGOUT((2, "In TUISPI_phoneConfigDialog"));
   DBGOUT((11, "  dwDeviceID=0x%08lx", dwDeviceID));
   DBGOUT((11, "  hwndOwner =0x%08lx", hwndOwner));
   DBGOUT((11, "  lpszDeviceClass=0x%08lx", lpszDeviceClass));

   (*lpfnUIDLLCallback)(
                      dwDeviceID,
                      TUISPIDLL_OBJECT_PHONEID,
                      dwParms,
                      sizeof(dwParms)
                    );

//BUGBUG how do we wait and/or return error code?

   return 0;

}


//****************************************************************************
//****************************************************************************
//****************************************************************************
LONG
TSPIAPI
TSPI_providerConfig(
                       HWND  hwndOwner,
                       DWORD dwPermanentProviderID
                   );

LONG
TSPIAPI
TUISPI_providerConfig(
                         TUISPIDLLCALLBACK lpfnUIDLLCallback,
                         HWND  hwndOwner,
                         DWORD dwPermanentProviderID
                       )
{
   DWORD dwParms[] = {
                        MYMSG_PROVIDERCONFIG,
                        (DWORD)hwndOwner,
                        dwPermanentProviderID
                      };


//BUGBUG: Can't pass strings across a callback

   DBGOUT((2, "In TUISPI_providerConfig"));
   DBGOUT((11, "  hwndOwner =0x%08lx", hwndOwner));
   DBGOUT((11, "  dwPermanentProviderID=0x%08lx", dwPermanentProviderID));

   (*lpfnUIDLLCallback)(
                      0,  //BUGBUG Is this correct?
                      TUISPIDLL_OBJECT_LINEID,
                      dwParms,
                      sizeof(dwParms)
                    );

//BUGBUG how do we wait and/or return error code?

   return 0;
}


//****************************************************************************
//****************************************************************************
//****************************************************************************
LONG
TSPIAPI
TUISPI_providerInstall(
                        TUISPIDLLCALLBACK lpfnUIDLLCallback,
                        HWND  hwndOwner,
                        DWORD dwPermanentProviderID
                      )
{

   DBGOUT((2, "In TUISPI_providerInstall"));

   return 0;
}


//****************************************************************************
//****************************************************************************
//****************************************************************************
LONG
TSPIAPI
TUISPI_providerRemove(
                        TUISPIDLLCALLBACK lpfnUIDLLCallback,
                        HWND  hwndOwner,
                        DWORD dwPermanentProviderID
                      )
{

   DBGOUT((2, "In TUISPI_providerRemove"));

   return 0;
}


//****************************************************************************
//****************************************************************************
//****************************************************************************



LONG
PASCAL
TSPI_providerEnumDevices16(                                       // TSPI v1.4
    DWORD               dwPermanentProviderID,
    LPDWORD             lpdwNumLines,
    LPDWORD             lpdwNumPhones,
    HPROVIDER           hProvider,
    LINEEVENT           lpfnLineCreateProc,
    PHONEEVENT          lpfnPhoneCreateProc,
    HWND                hSecretWnd
    );


LONG PASCAL TSPI_providerEnumDevices(
                                      DWORD      dwPermanentProviderID,
                                      LPDWORD    lpdwNumLines,
                                      LPDWORD    lpdwNumPhones,
                                      HPROVIDER  hProvider,
                                      LINEEVENT  lpfnLineCreateProc,
                                      PHONEEVENT lpfnPhoneCreateProc
                                    )
{
   DWORD dwThreadID;


   DBGOUT((2, "In TSPI_providerEnumDevices"));

   //
   // BUGBUG There's gotta be a better way to earn a buck...
   //

   if ( NULL == CreateThread( NULL,
                              0,
                              TheUIThread,
                              (LPVOID)&gdwThreadParms,
                              0,
                              &dwThreadID
                            )
      )
   {
      //
      // The CreateThread failed!!
      //
      DBGOUT((1, "CreateThread() failed!!!!"));
      return LINEERR_OPERATIONFAILED;
   }


   while ( !ghWnd)
   {
      Sleep(0);
   }


   return TSPI_providerEnumDevices16(
                                      dwPermanentProviderID,
                                      lpdwNumLines,
                                      lpdwNumPhones,
                                      hProvider,
                                      lpfnLineCreateProc,
                                      lpfnPhoneCreateProc,
                                      ghWnd
                                    );

}


//****************************************************************************
//****************************************************************************
//****************************************************************************



LONG
PASCAL
TSPI_lineGetID16(
                  HDRVLINE hdLine,
                  DWORD dwAddressID,
                  HDRVCALL hdCall,
                  DWORD dwSelect,
                  LPVARSTRING lpDeviceID,
                  LPCSTR lpszDeviceClass
                );


LONG
PASCAL
TSPI_lineGetID(
                  HDRVLINE hdLine,
                  DWORD dwAddressID,
                  HDRVCALL hdCall,
                  DWORD dwSelect,
                  LPVARSTRING lpDeviceID,
                  LPCSTR lpszDeviceClass,
                  HANDLE hTargetProcess

                )
{
   LONG lResult;

   DBGOUT((2, "Entering TSPI_lineGetID"));


   DBGOUT((20, "lpszDeviceClass=[%s]", lpszDeviceClass));

   lResult =  TSPI_lineGetID16(
                                hdLine,
                                dwAddressID,
                                hdCall,
                                dwSelect,
                                lpDeviceID,
                                lpszDeviceClass
                              );

   //
   // Only continue if the operation was successful
   //
   if ( 0 == lResult )
   {

      //
      // Is this a handle that we should translate?
      //

      DWORD dwDataSize;
      DWORD dwDataType;
      HKEY  hKey;
      TCHAR  buf[64];
      TCHAR  KeyName[128];


      //
      // We determine if we should translate by simply trying to retrive a
      // value with the name of lpszDeviceClass.  If we succeed, we'll
      // translate.
      //
      // We'll use the value as an offset into the string part (AN OFFSET
      // FROM THE START OF VAR DATA, NOT FROM THE START OF THE STRUCT!!!)
      // of where we can find the handle.
      //
      wsprintf(KeyName, "%s%d", gszProviderKey, gdwPermanentProviderID);

      RegOpenKeyEx(
                     HKEY_LOCAL_MACHINE,
                     KeyName,
                     0,
                     KEY_ALL_ACCESS,
                     &hKey
                     );

      dwDataSize = sizeof(buf);


      DBGOUT((11, "Looking in key [%s] for [%s]", KeyName, lpszDeviceClass));


      lResult = RegQueryValueEx(
                                 hKey,
                                 lpszDeviceClass,
                                 0,
                                 &dwDataType,
                                 buf,
                                 &dwDataSize
                              );

      RegCloseKey( hKey );


      if ( 0 == lResult )
      {
         HANDLE hTemp;
#if DBG
         LONG lRet;

         lRet =
#endif
         DuplicateHandle( GetCurrentProcess(),
                          *(LPHANDLE)((LPBYTE)lpDeviceID +
                                              lpDeviceID->dwStringOffset +
                                              *(LPDWORD)buf
                                     ),
                          hTargetProcess,
                          &hTemp,
                          0,
                          TRUE,
                          DUPLICATE_SAME_ACCESS |
                             DUPLICATE_CLOSE_SOURCE
                        );

//         CloseHandle( *(LPHANDLE)( (LPBYTE)lpDeviceID +
//                      lpDeviceID->dwStringOffset +
//                      *(LPDWORD)buf ) );


         *(LPHANDLE)( (LPBYTE)lpDeviceID +
                      lpDeviceID->dwStringOffset +
                      *(LPDWORD)buf ) =
                           hTemp;

         DBGOUT((2, "  Duplicate handle return code=0x%08lx", lRet));
      }
#if DBG
      else
      {
         DBGOUT((2, "  We won't dupe this handle. [%s]", lpszDeviceClass));
      }
#endif

      lResult = 0;  //What else can we do? Do we fail this if the dupe failed?

   }


   DBGOUT((2, "Leaving TSPI_lineGetID - lResult=0x%08lx", lResult));

   return lResult;
}


//****************************************************************************
//****************************************************************************
//****************************************************************************
LONG
PASCAL
TSPI_phoneGetID16(
                  HDRVPHONE hdPhone,
                  LPVARSTRING lpDeviceID,
                  LPCSTR lpszDeviceClass
                );


LONG
PASCAL
TSPI_phoneGetID(
                  HDRVPHONE hdPhone,
                  LPVARSTRING lpDeviceID,
                  LPCSTR lpszDeviceClass,
                  HANDLE hTargetProcess

                )
{
   LONG lResult;

   DBGOUT((2, "Entering TSPI_phoneGetID"));


   DBGOUT((20, "lpszDeviceClass=[%s]", lpszDeviceClass));

   lResult =  TSPI_phoneGetID16(
                                hdPhone,
                                lpDeviceID,
                                lpszDeviceClass
                              );

   //
   // Only continue if the operation was successful
   //
   if ( 0 == lResult )
   {

      //
      // Is this a handle that we should translate?
      //

      DWORD dwDataSize;
      DWORD dwDataType;
      HKEY  hKey;
      TCHAR  buf[64];
      TCHAR  KeyName[128];


      //
      // We determine if we should translate by simply trying to retrive a
      // value with the name of lpszDeviceClass.  If we succeed, we'll
      // translate.
      //
      // We'll use the value as an offset into the string part (AN OFFSET
      // FROM THE START OF VAR DATA, NOT FROM THE START OF THE STRUCT!!!)
      // of where we can find the handle.
      //
      wsprintf(KeyName, "%s%d", gszProviderKey, gdwPermanentProviderID);

      RegOpenKeyEx(
                     HKEY_LOCAL_MACHINE,
                     KeyName,
                     0,
                     KEY_ALL_ACCESS,
                     &hKey
                     );

      dwDataSize = sizeof(buf);


      DBGOUT((20, "Looking in key [%s] for [%s]", KeyName, lpszDeviceClass));


      lResult = RegQueryValueEx(
                                 hKey,
                                 lpszDeviceClass,
                                 0,
                                 &dwDataType,
                                 buf,
                                 &dwDataSize
                              );

      RegCloseKey( hKey );


      if ( 0 == lResult )
      {
         HANDLE hTemp;
#if DBG
         LONG lRet;

         lRet =
#endif
         DuplicateHandle( GetCurrentProcess(),
                          *(LPHANDLE)((LPBYTE)lpDeviceID +
                                              lpDeviceID->dwStringOffset +
                                              *(LPDWORD)buf
                                     ),
                          hTargetProcess,
                          &hTemp,
                          0,
                          TRUE,
                          DUPLICATE_SAME_ACCESS
//                          DUPLICATE_CLOSE_SOURCE
                        );

         CloseHandle( *(LPHANDLE)( (LPBYTE)lpDeviceID +
                      lpDeviceID->dwStringOffset +
                      *(LPDWORD)buf ) );

         *(LPHANDLE)( (LPBYTE)lpDeviceID +
                      lpDeviceID->dwStringOffset +
                      *(LPDWORD)buf ) =
                           hTemp;

         DBGOUT((20, "  Duplicate handle return code=0x%08lx", lRet));
      }
#if DBG
      else
      {
         DBGOUT((20, "  We won't dupe this handle. [%s]", lpszDeviceClass));
      }
#endif

      lResult = 0;  //What else can we do? Do we fail this if the dupe failed?

   }


   DBGOUT((2, "Leaving TSPI_phoneGetID - lResult=0x%08lx", lResult));

   return lResult;
}



//****************************************************************************
//****************************************************************************
//****************************************************************************

LONG
PASCAL
TSPI_providerInit16(
    DWORD               dwTSPIVersion,
    DWORD               dwPermanentProviderID,
    DWORD               dwLineDeviceBaseID,
    DWORD               dwPhoneDeviceBaseID,
    DWORD               dwNumLines,
    DWORD               dwNumPhones,
    ASYNC_COMPLETION    lpfnCompletionProc
                 );

LONG
TSPIAPI
TSPI_providerInit(
    DWORD               dwTSPIVersion,
    DWORD               dwPermanentProviderID,
    DWORD               dwLineDeviceIDBase,
    DWORD               dwPhoneDeviceIDBase,
    DWORD               dwNumLines,
    DWORD               dwNumPhones,
    ASYNC_COMPLETION    lpfnCompletionProc,
    LPDWORD             lpdwTSPIOptions                         // TAPI v2.0
    )
{
   DWORD pParams[7];

   DBGOUT((2,  "In 32-providerinit"));
   DBGOUT((11, "  dwTSPIVersion        =0x%08lx", dwTSPIVersion));
   DBGOUT((11, "  dwPermanentProviderID=0x%08lx", dwPermanentProviderID));


   gdwPermanentProviderID = dwPermanentProviderID;

   *lpdwTSPIOptions = 0;

   pParams[0] = dwTSPIVersion;
   pParams[1] = dwPermanentProviderID;
   pParams[2] = dwLineDeviceIDBase;
   pParams[3] = dwPhoneDeviceIDBase;
   pParams[4] = dwNumLines;
   pParams[5] = dwNumPhones;
   pParams[6] = (DWORD)lpfnCompletionProc;

   return SendMessage( ghWnd, MYMSG_PROVIDERINIT, 0, (LPARAM)pParams);

}


//****************************************************************************
//****************************************************************************
//****************************************************************************

LONG
PASCAL
TSPI_providerShutdown16(
    DWORD  dwTSPIVersion
                 );

LONG
TSPIAPI
TSPI_providerShutdown(
                       DWORD  dwTSPIVersion
                     )
{
   LONG lResult;

   DBGOUT((2, "In 32-providerShutdown"));


   lResult = SendMessage( ghWnd, MYMSG_PROVIDERSHUTDOWN, 0, dwTSPIVersion);

   //
   // Is the UI thread still around?
   //
   if ( ghWnd )
   {
      SendMessage(ghWnd, WM_CLOSE, 0, 0);

      //
      // Verify that the other thread has done its work and killed
      // the window
      //
      while ( ghWnd )
      {
         Sleep(0);
      }
   }

   return lResult;
}


//****************************************************************************
//****************************************************************************
//****************************************************************************

LONG
PASCAL
TSPI_lineMakeCall16(
    DRV_REQUESTID       dwRequestID,
    HDRVLINE            hdLine,
    HTAPICALL           htCall,
    LPHDRVCALL          lphdCall,
    LPCSTR              lpszDestAddress,
    DWORD               dwCountryCode,
    LPLINECALLPARAMS    const lpCallParams
    );

LONG
TSPIAPI
TSPI_lineMakeCall(
                    DRV_REQUESTID       dwRequestID,
                    HDRVLINE            hdLine,
                    HTAPICALL           htCall,
                    LPHDRVCALL          lphdCall,
                    LPCSTR              lpszDestAddress,
                    DWORD               dwCountryCode,
                    LPLINECALLPARAMS    const lpCallParams
                 )
{
   LONG lResult;
   DWORD pParams[7];


   DBGOUT((2, "In 32-linemakecall"));

   pParams[0] = (DWORD)dwRequestID;
   pParams[1] = (DWORD)hdLine;
   pParams[2] = (DWORD)htCall;
   pParams[3] = (DWORD)lphdCall;
   pParams[4] = (DWORD)lpszDestAddress;
   pParams[5] = (DWORD)dwCountryCode;
   pParams[6] = (DWORD)lpCallParams;


   lResult = SendMessage( ghWnd, MYMSG_LINEMAKECALL, 0, (LPARAM)pParams);


   return lResult;
}


//****************************************************************************
//****************************************************************************
//****************************************************************************
LONG PASCAL TapiCallbackThunk(    DWORD dwDevice,
                              DWORD dwMessage,
                              DWORD dwInstance,
                              DWORD dwParam1,
                              DWORD dwParam2,
                              DWORD dwParam3,
                              DWORD dwjunk
                         )
{


   DBGOUT((2, "TapiCallbackThunk  <<<----------------"));

   return 0;
}



//***************************************************************************
//***************************************************************************
//***************************************************************************
void HandleCallback( PCOPYDATASTRUCT pCds )
{
  DWORD dwCallbackType = ((LPDWORD)pCds->lpData)[0];
  LPDWORD lpdwCallbackData = (LPDWORD)pCds->lpData;



  switch ( dwCallbackType )
  {

     case CALLBACK_ASYNCCOMPLETION:
        DBGOUT((2, "AsyncCompletion"));
        (((ASYNC_COMPLETION)(pCds->dwData)))( lpdwCallbackData[1],
                          lpdwCallbackData[2]
                        );
        break;



     case CALLBACK_LINEEVENT:

        DBGOUT((2, "LineEvent"));

        if ( lpdwCallbackData[3] == LINE_NEWCALL )
        {
           lpdwCallbackData[5] = (DWORD)WOWGetVDMPointer(
                                                   lpdwCallbackData[5],
                                                   sizeof(DWORD),
                                                   1
                                                 );
        }

        //FALTHROUGH!!!

     case CALLBACK_LINECREATE:

        if ( dwCallbackType == CALLBACK_LINECREATE )
        {
           DBGOUT((2, "LineCreate"));
        }


        (((FARPROC)(pCds->dwData)))( lpdwCallbackData[1],
                          lpdwCallbackData[2],
                          lpdwCallbackData[3],
                          lpdwCallbackData[4],
                          lpdwCallbackData[5],
                          lpdwCallbackData[6]
                        );

#if DBG
        if ( dwCallbackType == CALLBACK_LINEEVENT &&
             lpdwCallbackData[3] == LINE_NEWCALL )
           DBGOUT((11, "Returned htCall=0x%08lx", *(LPDWORD)(lpdwCallbackData[5])));
#endif
        break;


     case CALLBACK_PHONEEVENT:
        DBGOUT((2, "PhoneEvent"));
     case CALLBACK_PHONECREATE:
        DBGOUT((2, "PhoneCreate?"));
        (((FARPROC)(pCds->dwData)))( lpdwCallbackData[1],
                          lpdwCallbackData[2],
                          lpdwCallbackData[3],
                          lpdwCallbackData[4],
                          lpdwCallbackData[5]
                        );
        break;


#if DBG
     default:
        DBGOUT((1, "Invalid callback type!!"));
        // Should rip or assert?
        break;
#endif
  }
}


//***************************************************************************
//***************************************************************************
//***************************************************************************
LRESULT CALLBACK _loadds MyWndProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
{

   DBGOUT((91, "msggg"));

   switch( nMsg )
   {

      case TSP3216L_MESSAGE:
         if ((wParam == 1) && (lParam == 2) )
            DBGOUT((0,"  Got a message thingy"));
         break;


      case MYMSG_PROVIDERINIT:
         DBGOUT((2, "Got a providerInit message"));
         return TSPI_providerInit16(
                                     ((LPDWORD)lParam)[0],
                                     ((LPDWORD)lParam)[1],
                                     ((LPDWORD)lParam)[2],
                                     ((LPDWORD)lParam)[3],
                                     ((LPDWORD)lParam)[4],
                                     ((LPDWORD)lParam)[5],
                                     (ASYNC_COMPLETION)((LPDWORD)lParam)[6]
                                   );
         break;


      case MYMSG_PROVIDERSHUTDOWN:
         DBGOUT((2, "Got a providerShutdown message"));
         return TSPI_providerShutdown16(
                                         lParam
                                       );
         break;


      case MYMSG_PROVIDERCONFIG:
         DBGOUT((2, "Got a providerConfig message"));
         return TSPI_providerConfig(
                                     (HWND)((LPDWORD)lParam)[0],
                                     ((LPDWORD)lParam)[1]
                                  );
         break;


      case MYMSG_LINEMAKECALL:
         DBGOUT((2, "Got a lineMakeCall message"));
         return TSPI_lineMakeCall16(
                                      (DRV_REQUESTID)((LPDWORD)lParam)[0],
                                      (HDRVLINE)((LPDWORD)lParam)[1],
                                      (HTAPICALL)((LPDWORD)lParam)[2],
                                      (LPHDRVCALL)((LPDWORD)lParam)[3],
                                      (LPCSTR)((LPDWORD)lParam)[4],
                                      (DWORD)((LPDWORD)lParam)[5],
                                      (LPLINECALLPARAMS)((LPDWORD)lParam)[6]
                                   );
         break;


      case MYMSG_LINECONFIGDIALOG:
         DBGOUT((2, "Got a lineConfigDialog message"));
         return TSPI_lineConfigDialog(
                                      ((LPDWORD)lParam)[0],
                                      (HWND)((LPDWORD)lParam)[1],
                                      (LPCSTR)((LPDWORD)lParam)[2]
                                     );
         break;


      case MYMSG_PHONECONFIGDIALOG:
         DBGOUT((2, "Got a phoneConfigDialog message"));
         return TSPI_phoneConfigDialog(
                                      ((LPDWORD)lParam)[0],
                                      (HWND)((LPDWORD)lParam)[1],
                                      (LPCSTR)((LPDWORD)lParam)[2]
                                     );
         break;


      case WM_COPYDATA:
         DBGOUT((11, "VaHoo!"));

         HandleCallback( (PCOPYDATASTRUCT)lParam );

         break;


      case WM_CLOSE:
         DestroyWindow( hWnd );
         break;


      case WM_DESTROY:
         PostQuitMessage(0);
         break;


      default:
         return DefWindowProc( hWnd, nMsg, wParam, lParam);
   }
   DBGOUT((2, "msggg done"));

   return(FALSE);
}