/* **********************************************************
   sub.c - Controls a sub in FISH to shoot fish using DDE

   Placed in public domain by Horizon Technologies Inc. 1990

  ***revision history***
1 SUB.C 24-Jan-90,15:11:52,`JMH' Initial version
2 SUB.C 29-Jan-90,9:13:06,`JMH' Semi-stable version
3 SUB.C 19-Feb-90,14:43:14,`JMH' Version as of 3/5/90
4 SUB.C 19-Jun-90,10:51:24,`JMH' Version as of 6/25/90
5 SUB.C 25-Jun-90,14:48:12,`JMH' Version as of 6/25/90
6 SUB.C 2-Jul-90,14:15:54,`PRS' Version as of 6/25/90
7 SUB.C 19-Jul-90,9:22:42,`PRS' Version with win.ini regerstered settings
8 SUB.C 12-Sep-90,15:59:04,`JMH' Version 1.3
  ***revision history***
********************************************************** */
#define MAIN
#define NOCOMM

#include "windows.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "dde.h"
#include "ddelib.h"
#include "sub.h"

/* Undocumented windows functions */
int FAR PASCAL lstrlen (LPSTR);
LPSTR FAR PASCAL lstrcpy (LPSTR, LPSTR);
LPSTR FAR PASCAL lstrcat (LPSTR, LPSTR);
int FAR PASCAL lstrcmp (LPSTR, LPSTR);

/* Globals */

static HANDLE ghInstance;

static char *gszAppName = "Sub";

static int gxFish, gyFish;

static int gxSub, gySub;

static int gxPrevFish;

static DWORD gdwSampleRate;

static DWORD gdwSampleCounter;

static HANDLE ghFish;

static HANDLE ghSub;

static DDECALLBACK glpfnFish;

static DDECALLBACK glpfnSub;

static FARPROC glpfnAbout;

/* Functions */
int PASCAL WinMain (HANDLE, HANDLE, LPSTR, int);
long FAR PASCAL SubProc (HWND, unsigned, WORD, LONG);
WORD FAR PASCAL Fish (HWND, unsigned, LPSTR, HANDLE);
WORD FAR PASCAL Sub (HWND, unsigned, LPSTR, HANDLE);
void MoveSub (void);
BOOL FAR PASCAL About (HWND, unsigned, WORD, LONG);


int PASCAL WinMain (hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
   WNDCLASS wndclass;
   MSG msg;
   HWND hWnd;

   ghInstance = hInstance;

   if ( !hPrevInstance )
      {
      wndclass.style = CS_HREDRAW | CS_VREDRAW;
      wndclass.lpfnWndProc = SubProc;
      wndclass.cbClsExtra = 0;
      wndclass.cbWndExtra = 0;
      wndclass.hInstance = hInstance;
      wndclass.hIcon = LoadIcon ( hInstance, gszAppName );
      wndclass.hCursor = LoadCursor ( NULL, IDC_ARROW );
      wndclass.hbrBackground = (HBRUSH) GetStockObject ( WHITE_BRUSH );
      wndclass.lpszMenuName = gszAppName;
      wndclass.lpszClassName = gszAppName;

      if ( !RegisterClass ( &wndclass ) )
	 return FALSE;
      }  /* if ! hPrevInstance */

   hWnd = CreateWindow ( gszAppName, gszAppName, WS_OVERLAPPEDWINDOW,
			 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL,
			 hInstance, NULL );

   if ( !hWnd )
      return ( NULL );

   ShowWindow ( hWnd, SW_SHOWMINIMIZED );
   UpdateWindow ( hWnd );

   while ( GetMessage ( &msg, NULL, NULL, NULL ) )
      {
      TranslateMessage ( &msg );
      DispatchMessage ( &msg );
      }  /* while GetMessage */

   return ( msg.wParam );
}  /* function WinMain */


long FAR PASCAL SubProc (hWnd, iMessage, wParam, lParam)
HWND hWnd;
unsigned iMessage;
WORD wParam;
LONG lParam;
{
   DDEADVISE ddeAdvise;
   PAINTSTRUCT ps;
   char szText[40];

   switch ( iMessage )
      {
      case WM_CREATE:
	 /* Provide callback addresses for DDE callback functions */
	 glpfnFish = (DDECALLBACK) MakeProcInstance ( (FARPROC) Fish,
						      ghInstance );
	 glpfnSub = (DDECALLBACK) MakeProcInstance ( (FARPROC) Sub,
						     ghInstance );

	 /* Inititate DDE Sessions with FISH */
	 if ( !( ghFish = DDEInitiate ( hWnd, "Fish", "Position" ) ) )
	    break;
	 if ( !( ghSub = DDEInitiate ( hWnd, "Fish", "Sub" ) ) )
	    break;

	 /* Ask to be advised everytime fish number zero makes a move */
	 ddeAdvise.fDeferUpd = FALSE;
	 ddeAdvise.fAckReq = TRUE;
	 ddeAdvise.cfFormat = CF_TEXT;
	 DDEAdvise ( ghFish, "0", &ddeAdvise, glpfnFish );

	 /* Set up a timer to determine the sampling rate */
	 SetTimer ( hWnd, NULL, 5000, NULL );
	 break;

      case WM_COMMAND:
	 switch ( wParam )
	    {
	    case IDM_EXIT:
	       SendMessage ( hWnd, WM_CLOSE, 0, 0L );
	       break;

	    case IDM_ABOUT:
	       glpfnAbout = MakeProcInstance ( About, ghInstance );
	       DialogBox ( ghInstance, "About", hWnd, glpfnAbout );
	       FreeProcInstance ( glpfnAbout );
	       break;
	    }  /* switch wParam */
	 break;

      case WM_PAINT:
	 BeginPaint ( hWnd, &ps );

	 /* Print the sampling rate */
	 sprintf ( szText, "Sample rate:  %ld/minute, %ld/second",
		   gdwSampleRate, gdwSampleRate / 60 );
	 TextOut ( ps.hdc, 0, 0, szText, strlen ( szText ) );

	 sprintf ( szText, "Message rate: %ld/minute, %ld/second",
		   gdwSampleRate * 7, gdwSampleRate * 7 / 60 );
	 TextOut ( ps.hdc, 0, 20, szText, strlen ( szText ) );

	 EndPaint ( hWnd, &ps );
	 break;

      case WM_TIMER:
	 /* Reset the sample rate counter */
	 gdwSampleRate = gdwSampleCounter * 12;
	 gdwSampleCounter = 0;

	 /* Cause a repainting of the client area */
	 InvalidateRect ( hWnd, NULL, TRUE );
	 UpdateWindow ( hWnd );
	 break;

      case WM_DESTROY:
	 /* Cancel the advise circuit for fish zero */
	 // DDEUnadvise (ghFish, "0"); - BUG: FISH does not ack the unadvise

	 /* End the sessions with FISH */
	 DDETerminate ( ghFish );
	 DDETerminate ( ghSub );
	 PostQuitMessage ( 0 );
	 break;

      default:
	 return DefWindowProc ( hWnd, iMessage, wParam, lParam );
      }  /* switch iMessage */

   return 0L;
}  /* function SubProc */


/* Callback routine to notify us when the fish moves */
WORD FAR PASCAL Fish (hFish, iMessage, lpszItem, hData)
HWND hFish;
unsigned iMessage;
LPSTR lpszItem;
HANDLE hData;
{
   LPDDEDATA lpData;
   char szData[20];

   /* If this is good data */

   if ( iMessage == DDE_DATA )
      {
      /* Move data to a local string */
      lpData = (LPDDEDATA) GlobalLock ( hData );
      lstrcpy ( szData, lpData->Value );
      GlobalUnlock ( hData );

      /* Save the previous x-position of Fish zero */
      gxPrevFish = gxFish;

      /* Fish out new fish position */
      sscanf ( szData, "%d %d", &gxFish, &gyFish );

      /* Request the current position of the sub */
      DDERequest ( ghSub, "Position", CF_TEXT, glpfnSub );

      /* Go get the fish that moved */
      MoveSub ();

      /* Show a sample hit */
      gdwSampleCounter++;
      }  /* if iMessage */

   return NULL;
}  /* function Fish */


/* Callback routine to notify us of the current sub position */
WORD FAR PASCAL Sub (hSub, iMessage, lpszItem, hData)
HWND hSub;
unsigned iMessage;
LPSTR lpszItem;
HANDLE hData;
{
   LPDDEDATA lpData;
   char szData[20];

   /* If this is good data */

   if ( iMessage == DDE_DATA )
      {
      /* Move data to a local string */
      lpData = (LPDDEDATA) GlobalLock ( hData );
      lstrcpy ( szData, lpData->Value );
      GlobalUnlock ( hData );

      /* Fish out sub position */
      sscanf ( szData, "%d %d", &gxSub, &gySub );
      }  /* if iMessage */

   return NULL;
}  /* function Sub */


/* Function to compute execute command and do it */
void MoveSub ()
{
   HANDLE hCommand;
   LPSTR lpszCommand;
   BOOL bFire = TRUE;

   /* Allocate room for the command */

   hCommand = GlobalAlloc ( GHND | GMEM_DDESHARE, (DWORD) 10 );
   lpszCommand = GlobalLock ( hCommand );

   /* Fish is moving right... */
   if ( gxFish > gxPrevFish )
      {
      /* We are behind the fish, go get it! */
      if ( gxFish > gxSub + 100 )
	 lstrcpy ( lpszCommand, "[r]" );

      /* Can't fire... too close... back away */
      else
	 {
	 lstrcpy ( lpszCommand, "[l]" );
	 bFire = FALSE;
	 }  /* else */
      }  /* if gxFish */

   /* Fish is moving left... */
   else
      {
      /* We are behind the fish, go get it! */
      if ( gxFish < gxSub - 100 )
	 lstrcpy ( lpszCommand, "[l]" );

      /* Can't fire... too close... back away */
      else
	 {
	 lstrcpy ( lpszCommand, "[r]" );
	 bFire = FALSE;
	 }  /* else */
      }  /* else */

   /* Compute simple up/down command */
   lstrcat ( lpszCommand, ( gyFish < gySub ) ? "[u]" : "[d]" );

   /* Check for vertical accuracy, cancel fire if too high or low */
   if ( abs ( gyFish - gySub ) > 10 )
      bFire = FALSE;

   /* Add the command to Fire! */
   if ( bFire )
      lstrcat ( lpszCommand, "[f]" );

   /* Clean up */
   GlobalUnlock ( hCommand );

   /* Execute the command */
   DDEExecute ( ghSub, hCommand );
}  /* function MoveSub */


/* About box */
BOOL FAR PASCAL About (hDlg, iMessage, wParam, lParam)
HWND hDlg;
unsigned iMessage;
WORD wParam;
LONG lParam;
{
   switch ( iMessage )
      {
      case WM_INITDIALOG:
	 break;

      case WM_COMMAND:
	 EndDialog ( hDlg, FALSE );
	 break;

      default:
	 return FALSE;
      }  /* switch iMessage */
   return TRUE;
}  /* function About */
