/*  mrr1.c
 *
 *  This program is a prototype for a future program, to be named WINC.
 *  WINC, (for Windowed INterface to Conferencing) will (if ever completed)
 *  act as a Windows-based interface to a mainframe conferencing program
 *  named CONTACT.  CONTACT runs on IBM's VM/CMS and is regarded as
 *  having a rather mediocre user interface.
 *  WINC will provide a friendlier interface, without substantially
 *  changing the model upon which CONTACT is based.
 *  CONTACT works by maintaining conversations as VM/CMS files,
 *  which various users view.  A conversation is continued by
 *  individual users APPENDing to the file containing that conversation.
 *
 *  WINC will talk to the IBM mainframe via a serial line
 *  connected indirectly to a 7171 ASCII terminal controller
 *  (emulates 3270 over asynch lines).
 *
 *  The real purpose of this program, though, is to give me a vehicle
 *  to learn Windows programming.  I doubt that I'll ever really finish
 *  the program.  So far the prototype seems to be bug-free, but
 *  whether it represents good Windows programming techniques, I don't know.
 *  At any rate, I offer this source code freely to anyone who can make
 *  any use of it.  Let me know if you have any suggestions for
 *  improving the coding techniques here.  The program has not been
 *  tested under LIM 4.0.
 *
 *  Currently, file viewing is implemented only as viewing of MS-DOS
 *  files, and no real talking to CONTACT is done.
 *  There is a dumb terminal emulator window, mostly to test my
 *  understanding of Windows Comm support.
 *
 *  Mark Riordan    January 1989    riordan@frith.egr.msu.edu
 *  1100 Parker  Lansing, MI  48912
 */


#define WINMAIN
#include "windows.h"
#include "mrr1.h"
#include "newmemos.h"
#include "winundoc.h"
#include "mrr1glob.h"
#include "ctype.h"

#define FILELISTNAME  "CONTACT.LST"  /* Name of file containing list of */
                                     /* CONTACT files to view. */
char *CommStrtoID(char *, int *, int *, char *);

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    HWND hWnd;
    MSG msg;
    HDC hDC;

    if (!hPrevInstance)
        if (!Mrr1Init(hInstance))
            return (NULL);

    hInst = hInstance;

    hWnd = CreateWindow("Mrr1",
        "Mark Riordan Test Application",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        (int) (GetSystemMetrics(SM_CXSCREEN) * 5 / 6),  /* Initial X width */
        GetSystemMetrics(SM_CYSCREEN)/ 3,  /* Initial Y height */
        NULL,
        NULL,
        hInstance,
        NULL);

    if (!hWnd)
        return (NULL);

    hWndConf = hWnd;

    MoreInit();

    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    while (GetMessage(&msg, NULL, NULL, NULL)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (msg.wParam);
}

/****************************************************************************

    FUNCTION: Mrr1Init(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL Mrr1Init(hInstance)
HANDLE hInstance;
{
    HANDLE hMemory;
    PWNDCLASS pWndClass;
    BOOL bSuccess;

    for(il=0; il<MAXLINES; il++) {
      for(ic=0; ic<MAXCHARS; ic++) {
        Lines[il][ic] = ' ';
        Rend [il][ic] = FALSE;
      }
    }
#ifdef UNDEF
    strcpy(&(Lines[0][0]), "This is line 0");
    strcpy(&(Lines[1][0]), "This is line 1.");
#endif
    TermRow = 0;   TermCol = 0;
    NTerminals = 0;
    ViewNew = 1;
    SelectAll = 1;
    NViews = 0;
    szAppName = "Winc";

/*  for(NMemos=0; NewMemos[NMemos]; NMemos++);  */
    for(imemo=0; imemo<NMemos; imemo++) {
      MemoSel[imemo] = FALSE;
    }
    MemoSel[0] = TRUE;
    MemoSel[2] = TRUE;
    MemoSel[11] = TRUE;

    for(ih=0; ih<MAXVIEWS; ih++) {
        hWndViews[ih] = (HANDLE) 0;
    }

    hMemory = LocalAlloc(LPTR, sizeof(WNDCLASS));
    pWndClass = (PWNDCLASS) LocalLock(hMemory);
    pWndClass->hCursor = LoadCursor(NULL, IDC_ARROW);
    pWndClass->hIcon = LoadIcon(hInstance, (LPSTR) "czar");
    pWndClass->lpszMenuName = (LPSTR) "ConfMenu";
    pWndClass->lpszClassName = (LPSTR) "Mrr1";
    pWndClass->hbrBackground = GetStockObject(WHITE_BRUSH);
    pWndClass->hInstance = hInstance;
    pWndClass->style = CS_HREDRAW | CS_VREDRAW;
    pWndClass->lpfnWndProc = Mrr1ConfWndProc;

    bSuccess = RegisterClass(pWndClass);

    if(bSuccess) {
      pWndClass->hCursor = LoadCursor(NULL, IDC_ARROW);
      pWndClass->hIcon = LoadIcon(hInstance, (LPSTR) "winc");
      pWndClass->lpszMenuName = (LPSTR) "ViewMenu";
      pWndClass->lpszClassName = (LPSTR) "Mrr1View";
      pWndClass->hbrBackground = GetStockObject(WHITE_BRUSH);
      pWndClass->hInstance = hInstance;
      pWndClass->style = CS_HREDRAW | CS_VREDRAW;
      pWndClass->lpfnWndProc = Mrr1ViewWndProc;

      bSuccess = RegisterClass(pWndClass);
    }

    if(bSuccess) {
      pWndClass->hCursor = LoadCursor(NULL, IDC_ARROW);
      pWndClass->hIcon = LoadIcon(hInstance, (LPSTR) "term");
      pWndClass->lpszMenuName = NULL;
      pWndClass->lpszClassName = (LPSTR) "Mrr1Term";
      pWndClass->hbrBackground = GetStockObject(WHITE_BRUSH);
      pWndClass->hInstance = hInstance;
      pWndClass->style = CS_HREDRAW | CS_VREDRAW;
      pWndClass->lpfnWndProc = Mrr1TermWndProc;

      bSuccess = RegisterClass(pWndClass);
    }


    LocalUnlock(hMemory);
    LocalFree(hMemory);
    return (bSuccess);
}

/* --- Function MoreInit --------------------------------
 *
 */

BOOL
MoreInit()
{
    char mesbuf[60];
    char szComName[10];
    char *errptr;
    int retcode;
    TEXTMETRIC  tmFontInfo;

    HDC hDC;

    /*  Get information about the system font.  LineHeight and CharWidth
     *  are pretty self-explanatory and the methods of determining these
     *  are standard.  TopSpace and SideSpace are the top and left margins
     *  in pixels; I compute them by a method I determined empirically.
     */

    hDC = GetDC(hWndConf);
    GetTextMetrics(hDC,(LPTEXTMETRIC) &tmFontInfo);
    LineHeight = tmFontInfo.tmExternalLeading + tmFontInfo.tmHeight;
    CharWidth  = tmFontInfo.tmAveCharWidth;
    TopSpace = tmFontInfo.tmExternalLeading;
    TopSpace = LineHeight / 4;
    SideSpace = tmFontInfo.tmAveCharWidth/2;
    ReleaseDC(hWndConf,hDC);

    ReverseVideo = FALSE;

    strcpy(szCommString,"COM1:2400,e,7");
    errptr = CommStrtoID(szCommString,&CommPortID,&CommParityID,pszCommSpeed);
    if(errptr) {
      strcpy(mesbuf,"Error parsing ");
      strcat(mesbuf,szCommString);
      MessageBox(hWndConf,errptr,mesbuf,MB_OK|MB_ICONEXCLAMATION);
    }


    /*
     *  Open the Comm port.  I use a hard-coded communications rate
     *  and other parameters; obviously, this should be improved
     *  with a dialog box.
     */

    strncpy(szComName,szCommString,4);
    CommDevice = OpenComm(szComName,1024,1024);
    if(CommDevice < 0) {
        sprintf(mesbuf,"OpenComm returned %d",CommDevice);
        MessageBox(hWndConf,mesbuf,"Error opening COM1",MB_OK|MB_ICONEXCLAMATION);
    } else {
        Mrr1DoComm(szCommString);
    }


    retcode = GetFileList(hWndConf);

    return retcode;
}

/*--- Function GetFileList ------------------------------------------
 *
 *  Get the list of files that we can view.
 *  Entry    The file containing the list of files resides in file
 *           FILELISTNAME.
 *           hWnd  is a handle to the window to which we should
 *                 display any error messages.
 *  Exit     The file names reside in NewMemos[]
 *
 */
BOOL
GetFileList(hWnd)
HWND hWnd;
{
    char szFileName[20];
    int copied,pos;
    HANDLE hFile;
    OFSTRUCT of;
    DWORD dwLength;
    LPSTR lpTextBuffer, lpBuf, lpStart;
    char *pFile;
    int size;
    HANDLE hTextBuffer;
    LOCALHANDLE hLocalMemory;
    char mesbuf[60];

    hFile = OpenFile(FILELISTNAME,&of,OF_READ);

    if(hFile == -1) {
        OkMessageBox(hWnd,"Cannot open file %s",FILELISTNAME);
        return FALSE;
    }

    if((dwLength = FileLength(hFile)) >= 24000) {
        _lclose(hFile);
        OkMessageBox(hWnd, "File %s is too large.", szFileName);
        return FALSE;
    }

    if(NULL == (hTextBuffer = GlobalAlloc(GHND, (DWORD)dwLength+1))) {
        _lclose(hFile);
        OkMessageBox(hWnd, "Cannot allocate memory for %s",FILELISTNAME);
        return FALSE;
    }

    lpTextBuffer = GlobalLock(hTextBuffer);
    _lread(hFile,lpTextBuffer, (WORD) dwLength);
    _lclose(hFile);
    lpTextBuffer[(WORD)dwLength] = '\0';

    /*  OK, we have read in the file.  Now go through the buffer
     *  containing the contents of the file,
     *  isolating the individual lines, allocating local memory
     *  and copying the lines into them.
     *  Copy pointers to the lines into NewMemos[];
     */


    lpBuf = lpStart = lpTextBuffer;
    NMemos = 0;
    while(NMemos < MAXMEMOS-1) {
      while(*lpBuf != '\r' && *lpBuf && lpBuf<lpTextBuffer+dwLength) lpBuf++;
      *lpBuf = '\0';
      lpBuf += 2;
      size = lpBuf - lpStart;

      /*  Allocate space for the line.
       *  We'll leave this memory allocated throughout execution
       *  of the program--this is no problem w/ local memory.
       */
      hLocalMemory = LocalAlloc(LMEM_FIXED,size);
      pFile = LocalLock(hLocalMemory);
      NewMemos[NMemos++] = pFile;
  /*  sprintf(mesbuf,"Memo %2d",NMemos);
      MessageBox(hWnd,lpStart,mesbuf,MB_OK);  */

      /* Copy the line into the newly-allocated memory. */
      do { *(pFile++) = *(lpStart++);} while(*lpStart);
      *pFile = '\0';
      lpStart = lpBuf;

      if(lpBuf >= lpTextBuffer+dwLength-1) break;
    }

    GlobalUnlock(hTextBuffer);
    GlobalFree(hTextBuffer);

    return TRUE;

}

/*--- Function Mrr1DoComm  -----------------------------------
 *
 *  Set/reset communications parameters.
 *
 *  Entry    szComm         is a string equivalent to that on a MODE statement.
 *                          E.g.,  "COM1:2400,n,8"
 */
int
Mrr1DoComm(szComm)
char *szComm ;
{
    int retcode;
    char mesbuf[60];

    if(retcode = BuildCommDCB(szComm,(DCB FAR *) &DCBComm)) {
        sprintf(mesbuf,"BuildComm returned %d",retcode);
        MessageBox(hWndConf,mesbuf,"Error building COM1 DCB",MB_OK|MB_ICONEXCLAMATION);
    } else {
        SetCommState((DCB FAR *) &DCBComm);
    }
    return (retcode);
}

/*--- Function CommStrtoID --------------------------------------
 *
 *   Takes a communications string of the form given to the MODE command
 *   and breaks it down to its constituent parts.
 *
 *   Entry    CommStr    is the string; e.g., "COM1:2400,n,8"
 *
 *   Exit     *Port      is the port (an IDD_* variable)
 *            *Parity    is the parity/data bits infor (an IDD_* variable)
 *            Speed     is the speed, in character form
 *            CommStr    has been converted to upper case.
 *            Function value is non-zero if error.
 */
char *
CommStrtoID(CommStr,Port,Parity,szSpeed)
char *CommStr;
int *Port, *Parity;
char *szSpeed;
{
  char *ptr, *Speedptr;
  int myPort;

  strupr(CommStr);
  ptr = CommStr;

  while(*(ptr) == ' ') ptr++;

  if(strncmp(ptr,"COM",3) != 0) return("Must be COM port");
  ptr += 3;

  if(*ptr == '1') {
    *Port = IDD_COM1;
  } else if (*ptr == '2') {
    *Port = IDD_COM2;
  } else {
    return("COM port must be 1 or 2");
  }

  ptr += 2;
  while(*ptr == ' ') ptr++;
  for(Speedptr = szSpeed; isdigit(*ptr); *(Speedptr++) = *(ptr++));
  *Speedptr = '\0';

  if(szSpeed == Speedptr) {
    return("Non-numeric COM speed");
  }

  while (isdigit(*ptr) || *ptr==' ' || *ptr==',') ptr++;

  if(*ptr == 'N') {
    *Parity = IDD_8NONE;
  } else if(*ptr == 'E') {
    *Parity = IDD_7EVEN;
  } else {
    return("Bad parity");
  }

  return (NULL);
}
