/*



*/

#include <windows.h>   // required for all Windows applications
#include <windowsx.h>  // Macro APIs, window message crackers, and control APIs
#include "rtr.h"

#if !defined(_WIN32) && !defined(WIN32)
#include <ver.h>
#endif
#include "rtrw32rq.h"   // specific to this program
#include <stdio.h>


#if !defined (APIENTRY) // Windows NT defines APIENTRY, but 3.x doesn't
#define APIENTRY far pascal
#endif

#if !defined(_WIN32) && !defined(WIN32) // Windows 3.x uses a FARPROC for dialogs
#define DLGPROC FARPROC
#endif

HINSTANCE hInst;          // current instance

char      g_szFacnam[80];               /* facility name             */
char      g_szAccess[80];               /* access string             */
UINT      g_wMsgSize;                   /* message size              */
char      g_szBuffer[256];              /* message buffer            */
HWND      g_hwndEventLB = NULL;         /* handle to event listbox   */
HWND      g_hwndMain = NULL;


rtr_channel_t   g_reqchn;

rtr_evtnum_t  g_pevtnum[] = { RTR_EVTNUM_USERDEF,
                              RTR_EVTNUM_USERBASE,
                              RTR_EVTNUM_UP_TO,
                              RTR_EVTNUM_USERMAX,
                              RTR_EVTNUM_RTRDEF,
                              RTR_EVTNUM_RTRBASE,
                              RTR_EVTNUM_UP_TO,
                              RTR_EVTNUM_RTRMAX,
                              RTR_EVTNUM_ENDLIST };

char szAppName[] = "RTRW32RQ";   // The name of this application
char szTitle[]   = "RTR Sample Application"; // The title bar text

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

        FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, int)

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

int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
                      LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
    HANDLE hAccelTable;

    if (!hPrevInstance)
    {
        if (!InitApplication(hInstance))
        {
            return (FALSE);     // Exits if unable to initialize
        }
    }

    /* Perform initializations that apply to a specific instance */

    if (!InitInstance(hInstance, nCmdShow))
    {
        return (FALSE);
    }

    strcpy ( g_szFacnam, "RTR$DEFAULT_FACILITY" );
    strcpy ( g_szAccess, "ACCSTR" );
    strcpy ( g_szBuffer, "message from client to server" );

    g_wMsgSize = 128;

    hAccelTable = LoadAccelerators (hInstance, szAppName);

    /* Acquire and dispatch messages until a WM_QUIT message is received. */

    while (GetMessage( &msg,    // message structure
                       NULL,    // handle of window receiving the message
                       0,       // lowest message to examine
                       0))      // highest message to examine
    {
        if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);// Translates virtual key codes
            DispatchMessage(&msg); // Dispatches message to window
        }
    }

    return (msg.wParam); // Returns the value from PostQuitMessage

    lpCmdLine; // This will prevent 'unused formal parameter' warnings
}


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

        FUNCTION: InitApplication(HINSTANCE)

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

BOOL InitApplication(HINSTANCE hInstance)
{
        WNDCLASS  wc;

        // Fill in window class structure with parameters that describe the
        // main window.

        wc.style         = CS_HREDRAW | CS_VREDRAW;// Class style(s).
        wc.lpfnWndProc   = (WNDPROC)WndProc;       // Window Procedure
        wc.cbClsExtra    = 0;                      // No per-class extra data.
        wc.cbWndExtra    = 0;                      // No per-window extra data.
        wc.hInstance     = hInstance;              // Owner of this class
        wc.hIcon         = LoadIcon (NULL, IDI_APPLICATION); // Icon name from .RC
        wc.hCursor       = LoadCursor(NULL, IDC_ARROW);// Cursor
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);// Default color
        wc.lpszMenuName  = szAppName;              // Menu name from .RC
        wc.lpszClassName = szAppName;              // Name to register as

        // Register the window class and return success/failure code.
        return (RegisterClass(&wc));
}


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

        FUNCTION:  InitInstance(HINSTANCE, int)

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

BOOL InitInstance( HINSTANCE hInstance, int nCmdShow)
{
        HWND            hWnd; // Main window handle.

        // Save the instance handle in static variable, which will be used in
        // many subsequence calls from this application to Windows.

        hInst = hInstance; // Store instance handle in our global variable

        // Create a main window for this application instance.

        hWnd = CreateWindow(
                szAppName,           // See RegisterClass() call.
                szTitle,             // Text for window title bar.
                WS_OVERLAPPEDWINDOW,// Window style.
                CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, // Use default positioning
                NULL,                // Overlapped windows have no parent.
                NULL,                // Use the window class menu.
                hInstance,           // This instance owns this window.
                NULL                 // We don't use any data in our WM_CREATE
        );

        // If window could not be created, return "failure"

        if (!hWnd)
        {
            return (FALSE);
        }

        g_hwndMain = hWnd;

        // Make the window visible; update its client area; and return "success"

        ShowWindow(hWnd, nCmdShow); // Show the window
        UpdateWindow(hWnd);         // Sends WM_PAINT message

        return (TRUE);              // We succeeded...
}

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

        FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)

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

LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM uParam,
                          LPARAM lParam)
{
        FARPROC lpProcAbout;
        FARPROC lpProcOptions;
        FARPROC lpProcTransaction;
        int wmId, wmEvent;

        switch (message)
        {
            case WM_COMMAND:  // message: command from application menu
#if defined (_WIN32) || defined(WIN32)
                wmId    = LOWORD(uParam);
                wmEvent = HIWORD(uParam);
#else
                wmId    = uParam;
                wmEvent = HIWORD(lParam);
#endif
                switch (wmId)
                {
                    case IDM_ABOUT:
                        lpProcAbout = MakeProcInstance((FARPROC)About, hInst);
                        DialogBox(hInst,
                                  "AboutBox",
                                  hWnd,
                                  (DLGPROC)lpProcAbout);

                        FreeProcInstance(lpProcAbout);
                        break;

                    case IDM_OPTIONS:
                        lpProcOptions = MakeProcInstance((FARPROC)Options, hInst);
                        DialogBox(hInst,
                                  "Options",
                                  hWnd,
                                  (DLGPROC)lpProcOptions);

                        FreeProcInstance(lpProcOptions);
                        break;

                    case IDM_TRANSACTION:
                        lpProcTransaction = MakeProcInstance((FARPROC)Transaction, hInst );
                        DialogBox( hInst,
                                   "Transaction",
                                   hWnd,
                                   (DLGPROC)lpProcTransaction );

                        FreeProcInstance( lpProcTransaction );
                        break;

                    case IDM_EXIT:
                        DestroyWindow (hWnd);
                        break;

                    default:
                        return (DefWindowProc(hWnd, message, uParam, lParam));
                }
                break;

            case WM_DESTROY:  // message: window being destroyed
                PostQuitMessage(0);
                break;

            default:          // Passes it on if unproccessed
                return (DefWindowProc(hWnd, message, uParam, lParam));
        }
        return (0);
}

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

        FUNCTION: CenterWindow (HWND, HWND)

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

BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
{
    RECT    rChild, rParent;
    int     wChild, hChild, wParent, hParent;
    int     wScreen, hScreen, xNew, yNew;
    HDC     hdc;

    // Get the Height and Width of the child window
    GetWindowRect (hwndChild, &rChild);
    wChild = rChild.right - rChild.left;
    hChild = rChild.bottom - rChild.top;

    // Get the Height and Width of the parent window
    GetWindowRect (hwndParent, &rParent);
    wParent = rParent.right - rParent.left;
    hParent = rParent.bottom - rParent.top;

    // Get the display limits
    hdc = GetDC (hwndChild);
    wScreen = GetDeviceCaps (hdc, HORZRES);
    hScreen = GetDeviceCaps (hdc, VERTRES);
    ReleaseDC (hwndChild, hdc);

    // Calculate new X position, then adjust for screen
    xNew = rParent.left + ((wParent - wChild) /2);
    if (xNew < 0)
    {
        xNew = 0;
    }
    else if ((xNew+wChild) > wScreen)
    {
        xNew = wScreen - wChild;
    }

    // Calculate new Y position, then adjust for screen
    yNew = rParent.top  + ((hParent - hChild) /2);

    if (yNew < 0)
    {
        yNew = 0;
    }
    else if ((yNew+hChild) > hScreen)
    {
        yNew = hScreen - hChild;
    }

    // Set it, and return
    return SetWindowPos (hwndChild, NULL,
                         xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}

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

        FUNCTION: About(HWND, UINT, WPARAM, LPARAM)

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

LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message)
  {
    case WM_INITDIALOG:
      return (TRUE);

    case WM_COMMAND:
      if (wParam == IDOK || wParam == IDCANCEL)
      {
        EndDialog(hDlg, TRUE);
        return (TRUE);
      }
    break;
  }
  return (FALSE);
}

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

    FUNCTION: Options(HWND, UINT, WPARAM, LPARAM)

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

LRESULT CALLBACK Options(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
  switch (message)
  {
    case WM_INITDIALOG:
      SendDlgItemMessage( hDlg, IDE_FACILITY_NAME, EM_LIMITTEXT, (WPARAM)32, 0L );
      SetDlgItemText( hDlg, IDE_FACILITY_NAME, (LPSTR)g_szFacnam);
      SendDlgItemMessage( hDlg, IDE_ACCESS, EM_LIMITTEXT, (WORD)128, 0L );
      SetDlgItemText( hDlg, IDE_ACCESS, (LPSTR)g_szAccess);
      SendDlgItemMessage( hDlg, IDE_MESSAGE_SIZE, EM_LIMITTEXT, (WPARAM)4, 0L );
      SetDlgItemInt( hDlg, IDE_MESSAGE_SIZE, g_wMsgSize, FALSE );
      return (TRUE);

    case WM_COMMAND:
      switch (wParam)
      {
        case IDOK:
          GetDlgItemText( hDlg, IDE_FACILITY_NAME, g_szFacnam , 32);
          GetDlgItemText( hDlg, IDE_ACCESS, g_szAccess, 80);
          g_wMsgSize = GetDlgItemInt( hDlg, IDE_MESSAGE_SIZE, NULL, FALSE);
          EndDialog(hDlg, TRUE);
          return (TRUE);

        case IDCANCEL:
          EndDialog(hDlg, TRUE);
          return (TRUE);
      }
      break;
  }
  return (FALSE);
}

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

    FUNCTION: Transaction(HWND, UINT, WPARAM, LPARAM)

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

LRESULT CALLBACK Transaction(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
  rtr_status_t sts;

  switch (message)
  {
    case WM_INITDIALOG:
      SendDlgItemMessage( hDlg, IDE_TRANS_DATA, EM_LIMITTEXT, (WPARAM)256, 0L );
      SetDlgItemText( hDlg, IDE_TRANS_DATA, (LPSTR)g_szBuffer );

      g_hwndEventLB = GetDlgItem ( hDlg, IDL_RTREVENTS );

      Button_Enable( GetDlgItem ( hDlg, IDB_ACCEPT ), FALSE );
      Button_Enable( GetDlgItem ( hDlg, IDB_REJECT ), FALSE );
      Button_Enable( GetDlgItem ( hDlg, IDB_CLOSE ), FALSE );

      return (TRUE);

    case WM_COMMAND:
      switch (wParam)
      {
        case IDB_OPEN:
          sts = rtr_open_channel (
                    /* pchannel */ &g_reqchn,
                    /* flags    */ RTR_F_OPE_CLIENT,
                    /* facnam   */ g_szFacnam,
                    /* chanam   */ "TEST_CHUNNEL",
                    /* pevtnum  */ g_pevtnum,
                    /* access   */ g_szAccess,
                    /* numseg   */ 0,
                    /* pkeyseg  */ RTR_NO_PKEYSEG );

          exit_if_error ( "rtr_open_channel", sts );

          if ( wait_for_channel_open ( ) == TRUE )
          {
              Button_Enable( GetDlgItem ( hDlg, IDB_ACCEPT ), TRUE );
              Button_Enable( GetDlgItem ( hDlg, IDB_REJECT ), TRUE );
              Button_Enable( GetDlgItem ( hDlg, IDB_CLOSE ), TRUE );
              Button_Enable( GetDlgItem ( hDlg, IDB_OPEN ), FALSE );
              Button_Enable( GetDlgItem ( hDlg, IDCANCEL ), FALSE );
          }

          return (TRUE);

        case IDB_CLOSE:
          sts = rtr_close_channel (
                    /* channel  */ g_reqchn,
                    /* flags    */ RTR_NO_FLAGS );

          exit_if_error ( "rtr_close_channel", sts );

          g_reqchn = 0;

          Button_Enable( GetDlgItem ( hDlg, IDB_ACCEPT ), FALSE );
          Button_Enable( GetDlgItem ( hDlg, IDB_REJECT ), FALSE );
          Button_Enable( GetDlgItem ( hDlg, IDB_CLOSE ), FALSE );
          Button_Enable( GetDlgItem ( hDlg, IDB_OPEN ), TRUE );
          Button_Enable( GetDlgItem ( hDlg, IDCANCEL ), TRUE );
          return (TRUE);

        case IDB_REJECT:
        case IDB_ACCEPT:
          GetDlgItemText( hDlg, IDE_TRANS_DATA, (LPSTR)g_szBuffer, 256 );

//          SetDlgItemText( hDlg, IDE_TRANS_RESPONSE, (LPSTR)g_szBuffer );

          sts = rtr_send_to_server (
                     /* channel  */ g_reqchn,
                     /* flags    */ RTR_NO_FLAGS,
                     /* pmsg     */ g_szBuffer,
                     /* msglen   */ g_wMsgSize,
                     /* msgfmt   */ RTR_NO_MSGFMT );

          exit_if_error ( "rtr_send_to_server", sts );

          sts = rtr_send_to_server (
                     /* channel  */ g_reqchn,
                     /* flags    */ RTR_NO_FLAGS,
                     /* pmsg     */ g_szBuffer,
                     /* msglen   */ g_wMsgSize,
                     /* msgfmt   */ RTR_NO_MSGFMT );

          exit_if_error ( "rtr_send_to_server", sts );

          if ( wParam == IDB_ACCEPT )
          {
              sts = rtr_accept_tx (
	 	/* channel  */ g_reqchn,
		/* flags    */ RTR_NO_FLAGS,
		/* reason   */ RTR_NO_REASON );

              exit_if_error ( "rtr_accept_tx", sts );

              wait_for_tx_status ( );
          }
          else
          {
              sts = rtr_reject_tx (
	 	/* channel  */ g_reqchn,
		/* flags    */ RTR_NO_FLAGS,
		/* reason   */ RTR_NO_REASON );

              exit_if_error ( "rtr_reject_tx", sts );

              AddStrToListBox ( "Tx rejected (client)" );     		
          }

          return (TRUE);

        case IDCANCEL:
          EndDialog(hDlg, TRUE);
          return (TRUE);
      }
      break;
  }
  return (FALSE);
}

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

    FUNCTION: AddStrToListBox (LPSTR szFmt, ...)

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

void AddStrToListBox (LPSTR szFmt, ...)
{
   char szBuf[150];
   int nIndex;
   va_list va_params;

   // Make va_params point to the first argument after szFmt.

   va_start(va_params, szFmt);

   // Build the string to be displayed.
   vsprintf(szBuf, szFmt, va_params);

   do {
      // Add the string to the end of the list box.

      nIndex = ListBox_AddString( g_hwndEventLB, szBuf );

      // If the list box is full, delete the first item in it.

      if ( nIndex == LB_ERR )
         ListBox_DeleteString( g_hwndEventLB, 0 );

   } while ( nIndex == LB_ERR );

   // Select the newly added item.

   ListBox_SetCurSel( g_hwndEventLB, nIndex );

   // Indicate that we are done referencing
   // the variable arguments.

   va_end( va_params );
}

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

    FUNCTION: exit_if_error ( char *msgtxt, rtr_status_t sts)

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

void exit_if_error ( char *msgtxt, rtr_status_t sts)
{
    char szBuffer[254];

    if ( sts > 0 )
    {
        return;
    }

    wsprintf ( szBuffer, "%s: unexpected error(%d) -> %s\n", msgtxt, sts, rtr_error_text (sts));

    MessageBox( g_hwndMain, szBuffer, msgtxt , MB_OK  );

    exit (1);
}

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

    FUNCTION: wait_for_channel_open ()

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

BOOL wait_for_channel_open ( void )
{
    char            rcvbuf[RTR_MAX_MSGLEN];
    rtr_channel_t   channel;
    rtr_status_t    sts;
    rtr_msgsb_t     msgsb;
    rtr_status_data_t *p_status_data;

rerun:

    sts = rtr_receive_message (
                   /* pchannel */ &channel,
                   /* flags    */ RTR_NO_FLAGS,
		   /* prcvchan */ RTR_ANYCHAN,
	           /* pmsg     */ rcvbuf,
                   /* maxlen   */ RTR_MAX_MSGLEN,
                   /* timoutms */ RTR_NO_TIMOUTMS,
		   /* pmsgsb    */ &msgsb );

    exit_if_error ( "rtr_receive_message", sts );

    switch ( msgsb.msgtype )
    {
        case rtr_mt_opened:
            AddStrToListBox ( "Channel opened %d", channel );
            return ( TRUE );

        case rtr_mt_closed:
            p_status_data = (rtr_status_data_t *)rcvbuf;
            AddStrToListBox ( "Channel closed because %s", rtr_error_text (p_status_data->status) );
            return ( FALSE );

        default:
            break;
	}

    goto rerun;
}

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

    FUNCTION: wait_for_tx_status ()

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

void  wait_for_tx_status ( void )
{
    char            rcvbuf[RTR_MAX_MSGLEN];
    rtr_channel_t   channel;
    rtr_status_t    sts;
    rtr_msgsb_t     msgsb;
    rtr_status_data_t *p_status_data;

rerun:


    sts = rtr_receive_message (
                   /* pchannel */ &channel,
                   /* flags    */ RTR_NO_FLAGS,
		   /* prcvchan */ RTR_ANYCHAN,
	           /* pmsg     */ rcvbuf,
                   /* maxlen   */ RTR_MAX_MSGLEN,
                   /* timoutms */ RTR_NO_TIMOUTMS,
		   /* pmsgsb    */ &msgsb );

    exit_if_error ( "rtr_receive_message", sts );

    switch ( msgsb.msgtype )
    {
        case rtr_mt_accepted:
            AddStrToListBox ( "Tx accepted (%x,%x,%x,%x,%x,%x,%x)",     		
                               msgsb.tid.tid32[0],
    			       msgsb.tid.tid32[1],
    			       msgsb.tid.tid32[2],
    			       msgsb.tid.tid32[3],
    			       msgsb.tid.tid32[4],
    			       msgsb.tid.tid32[5],
    			       msgsb.tid.tid32[6] );
            return;

        case rtr_mt_rejected:
            p_status_data = (rtr_status_data_t *)rcvbuf;

            AddStrToListBox ( "UXEXPECTED Tx rejected (%x,%x,%x,%x,%x,%x,%x)",     		
                               msgsb.tid.tid32[0],
    			       msgsb.tid.tid32[1],
    			       msgsb.tid.tid32[2],
    			       msgsb.tid.tid32[3],
    			       msgsb.tid.tid32[4],
    			       msgsb.tid.tid32[5],
    			       msgsb.tid.tid32[6] );

            AddStrToListBox ( "UXEXPECTED Tx rejected . staus = %s", rtr_error_text (p_status_data->status) );
            AddStrToListBox ( "UXEXPECTED Tx rejected . reason = %s", rtr_error_text (p_status_data->reason) );
            return;

        default:
	    break;
    }

    goto rerun;
}

