/*****************************************************************************

    Stdio.c

    Main Program file.

    This file contains all the code to provide basic text communication
    for another windows application.

    This programs operation is described in winpipe.wri.


*****************************************************************************/

#include <windows.h>
#include <stdio.h>
#include "winpipe.h"
#include "Stdio.h"


/*  circular difference , First, Last, Count	*/

#define CIRDIF(F, L, C) (L >= F) ? (L - F): (C - (F - L))

#define MAX_ROWS     50         /*  upto 50 rows of text on window  */
#define MAX_COLS     100        /*  up to 100 characters per row    */
#define TAB_SIZE      8         /*  Tab expansion size              */
#define BUFFER_SIZE 2048        /*  Buffer size, same as default pipe size  */

FILE *logfile;                  /*  "redirection" file              */

char ScrBuf[MAX_ROWS][MAX_COLS];/*  screen buffer                   */

char szFname[]="\\stdio.log";   /*  default Log file name               */
char *szAppName = "stdio";      /*  Application name                    */
char *szPosition = "Pos";       /*  Position entry in win.ini           */
char *szError = "Error";        /*  String for error                    */

static int  FirstRow=0; 	/*  first entry into circular buffer	*/
static int  LastRow=0;		/*  last entry into circular buffer	*/

static int CurrentCol=0;	/*  Current row being worked on		*/
static int WinRow=0;		/*  reletive Windows row		*/

static int NumRows;		/*  Number of rows avaialable		*/
static int CharHeight;		/*  height of a character		*/
static int CharWidth;           /*  average Width of a character        */


RECT	rect;                   /*  Window rectangle                    */
HANDLE  hBrush;                 /*  Generic brush to paint window with  */

HANDLE  hInstance;              /*  This programs instance              */
HANDLE	hMainWnd;               /*  This programs window                */
static HDC ScrhDC;              /*  internal copy of device context     */

/*  Quick'n easy dialog routine */

BOOL PopupDLG( hParentWnd, lpszTemplate, lpfnDlgProc )
HWND        hParentWnd;
LPSTR       lpszTemplate;
FARPROC     lpfnDlgProc;
{
    BOOL           bResult;
    FARPROC        lpProc;
    HANDLE         hParentInst;

    hParentInst = GetWindowWord(hParentWnd,GWW_HINSTANCE);

    lpProc = MakeProcInstance( lpfnDlgProc, hParentInst);

    bResult = DialogBox( hParentInst, lpszTemplate, hParentWnd, lpProc );

    FreeProcInstance( lpProc );

    return( bResult );

}

/*  Aboutbox proc   */

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)    /*  About box   */
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
        case WM_INITDIALOG:
      return (TRUE);

        case WM_COMMAND:
            switch(wParam){
                case IDOK:
                    EndDialog(hDlg, TRUE);
                    break;

                case IDCANCEL:
                    EndDialog(hDlg, TRUE);
                    break;

                default:
                    break;

            }

    }
    return (FALSE);
}



      /*  Draw single line    */

UpdateLine()
{
    TextOut(ScrhDC, 0, CharHeight * WinRow, (LPSTR) ScrBuf[LastRow], CurrentCol);
}


     /*  Redraw whole window */

void RepaintScr()
{
    int i, y, numrows;
    int len = CurrentCol;
    int row = LastRow;

    numrows = CIRDIF(FirstRow, LastRow, MAX_ROWS);  /* number of displayable lines */

    WinRow = (NumRows <= numrows) ? (NumRows-1) : numrows; /* starting row     */

    y = WinRow * CharHeight;                        /*  Last line in window     */

    FillRect(ScrhDC, (LPRECT) &rect, hBrush);       /*  Clear window            */

    for(i=WinRow; i >= 0; i--){	            /* count down to last displayable	*/
	TextOut(ScrhDC, 0, y, (LPSTR)ScrBuf[row], lstrlen((LPSTR)ScrBuf[row]));
	y -= CharHeight;
	row = (--row < 0) ? (MAX_ROWS -1) : row;

    }

}


      /*  Point to next row   */

void IncRow()
{
    int difference;

    ScrBuf[LastRow][CurrentCol] = (char) 0;	/* make a Z string  */

    if(logfile){
        fputs(ScrBuf[LastRow], logfile);        /*  string          */
        fputc('\n', logfile);                   /*  plus new line   */
    }

    UpdateLine();

    CurrentCol=0;

    LastRow++;
    LastRow = (LastRow >= MAX_ROWS) ? 0 : LastRow;

    if(FirstRow == LastRow){
	FirstRow ++;
	FirstRow = (FirstRow >= MAX_ROWS) ? 0 : FirstRow;
    }

    WinRow++;
    WinRow = (WinRow >= NumRows) ? (NumRows-1) : WinRow;

    ScrBuf[LastRow][CurrentCol] = 0;
    difference = CIRDIF(FirstRow, LastRow, MAX_ROWS);

    if(NumRows <= difference)
	RepaintScr();
}

    /*  Interpret and optionally add character to window buffer */

void AddChar(int c)
{
int     i;
RECT    rCell;
DWORD   dwStrSpec;

    switch(c){

	case	9:              /*  Tab                 */
            if((CurrentCol + TAB_SIZE) >= MAX_COLS)
                DrawChar('\n');

	    for(i=0; i < TAB_SIZE; i++)
		    ScrBuf[LastRow][CurrentCol++] = ' ';
	    break;

	case	10:             /*  Line feed           */
	    IncRow();
	    break;

	case	11:             /*  Vertical tab        */
	    IncRow();
	    break;

	case	12:             /* Form feed (ignore)   */
	    break;

	case	13:             /* enter key            */
	    CurrentCol=0;
	    break;

        case    8:              /*  Back Space          */
            rCell.left = 0;                             /*  left side of window     */
            rCell.top  = CharHeight * WinRow;           /*  Top of text             */

            /*  Width of all characters */
            dwStrSpec=GetTextExtent(ScrhDC, (LPSTR)ScrBuf[LastRow], CurrentCol);
            rCell.right = LOWORD(dwStrSpec);
            rCell.bottom = rCell.top + HIWORD(dwStrSpec);

            /*  Clear Cell  */
            FillRect(ScrhDC, (LPRECT) &rCell, hBrush);

            /*  Clear cell position  */
            CurrentCol = (--CurrentCol <= 0) ? 0 : CurrentCol;

            /*  repaint text minus character    */
	    TextOut(ScrhDC, 0, CharHeight * WinRow, (LPSTR) ScrBuf[LastRow], CurrentCol);

            /*  cap off ScrBuf to be an 'Z' string  */
	    ScrBuf[LastRow][CurrentCol] = (char) 0;
            break;

	default:    /*  just draw characters  */

	    ScrBuf[LastRow][CurrentCol++] = (char) c;
            ScrBuf[LastRow][CurrentCol] = (char) 0;
            break;
	}


}

     /*  Draw character on window    */

DrawChar(int c)
{
    AddChar(c);
    UpdateLine();
}


    /*  Draw string on window   */

void DrawBuffer(LPSTR szString, int num)
{
    int i;
    for(i=0; i < num && szString[i] != 0; i++)
	AddChar(szString[i]);

    UpdateLine();
}

    /*  Clear screen    */

void ClearScr()
{
    if(ScrhDC = GetDC(hMainWnd)){
        FirstRow=LastRow=0;                     /*  zero circular buffer    */
        CurrentCol=0;                           /*  Set pos to zero         */
        ScrBuf[LastRow][CurrentCol]=0;          /*  Kill string             */
        RepaintScr();
        ReleaseDC(hMainWnd, ScrhDC);

    }

}

/*  Winproc for stdio */

long FAR PASCAL WndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
PAINTSTRUCT	   ps;
int		   i;
HMENU		   hMenu;
char		   Buffer[BUFFER_SIZE];


    switch (message) {
        case WM_COMMAND:
            switch(wParam){
		case	IDM_ABOUT:      /*  Show about box  */
		    PopupDLG(hWnd, "AboutBox", About);
		    break;

		case	IDM_SAVE:       /*  Save data       */
		    logfile=fopen(szFname, "w");

		    if(logfile){
		        hMenu=GetMenu(hWnd);
			EnableMenuItem(hMenu, IDM_CLOSE, MF_ENABLED);
			EnableMenuItem(hMenu, IDM_SAVE, MF_GRAYED);
                    }

		    break;

		case	IDM_CLEAR:      /*  Clear "screen"  */
                    ClearScr();
		    break;

		case	IDM_CLOSE:      /*  Close log file  */
		    if(!fclose(logfile)){
			hMenu=GetMenu(hWnd);
			EnableMenuItem(hMenu, IDM_CLOSE, MF_GRAYED);
			EnableMenuItem(hMenu, IDM_SAVE, MF_ENABLED);
		    }
		    break;

                case    IDM_DEFAULT:    /*  Set default position in win.ini */
                    {
                        RECT    rect;
                        char buffer[25];
                        GetWindowRect(hWnd, &rect); /*  get size    */
                        wsprintf((LPSTR)buffer,
                        (LPSTR)"%d,%d,%d,%d", rect.left, rect.top, rect.right, rect.bottom);
                        WriteProfileString((LPSTR)szAppName, (LPSTR)szPosition, (LPSTR)buffer);
                    }
                    break;


                default:
                    return (DefWindowProc(hWnd, message, wParam, lParam));
            }
	    break;

	case	WM_USER:
	    if(wParam == 11){
                ScrhDC = GetDC(hWnd);

#ifdef BY_THE_BOOK
                i = Pgets((LPSTR) Buffer, LOWORD(lParam), Stdout);
                DrawBuffer(Buffer, i);
#else
                while((i=QueryPipe(Stdout))){
                    i = Pgets((LPSTR) Buffer, LOWORD(lParam), Stdout);
                    DrawBuffer(Buffer, i);
                }
#endif

/*
 *  This is done to fix a phasing error. If testapp is started before
 *  stdio, stdio will be a number of read messages behind. So - I have
 *  Changed the code to empty the pipe after a read message. Your code
 *  Should either do this also or assure correct phasing of read and
 *  write messages.
 */

	        ReleaseDC(hWnd, ScrhDC);
	    }
	    break;

	case	WM_CHAR:
	    if(wParam == 13)
		Pputc(10, Stdin);
	    else
		Pputc((char) wParam, Stdin);
	    break;

	case WM_DESTROY:
	    ReleasePipe(Stdin);
	    ReleasePipe(Stdout);
	    PostQuitMessage(0);
	    break;


	case	WM_CREATE:
	    {
	    TEXTMETRIC	tm;

	    if(OpenPipe(hWnd, (LPSTR) "Stdout", PIPE_READ, 11) < 0)
		PostQuitMessage(0);

	    logfile=NULL;
	    hBrush = GetStockObject(WHITE_BRUSH);
	    LastRow=0;
	    CurrentCol=0;
	    ScrhDC = GetDC(hWnd);
	    GetTextMetrics(ScrhDC, (LPTEXTMETRIC) &tm);
	    CharHeight = tm.tmHeight;
            CharWidth = tm.tmWeight;
	    ReleaseDC(hWnd, ScrhDC);
	    GetClientRect(hWnd, &rect);
	    NumRows = (rect.bottom - rect.top) / CharHeight;
	    }
	    break;

	case	WM_PAINT:
	    ScrhDC = BeginPaint(hWnd, &ps);
	    GetClientRect(hWnd, &rect);
	    NumRows = (rect.bottom - rect.top) /CharHeight;
	    RepaintScr();
	    EndPaint(hWnd, &ps);
	    break;


        default:
	    return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);

}


    /*  routine to create window sized by parameters in win.ini */


HWND SetWindow()
{
    char Buffer[25];
    char *szString;
    int i;
    int startx, starty, sizex, sizey;

    szString=Buffer;

    GetProfileString((LPSTR)szAppName, (LPSTR)szPosition, (LPSTR)szError,
        (LPSTR)szString, 25);

    if(lstrcmpi((LPSTR)szError, (LPSTR)szString)){    /* if default set  */
        while(*szString && !isdigit(*szString))
            szString++;

        startx=atoi(szString);

        while(*szString && isdigit(*szString))
            szString++;     /* skip start x number  */

        while(*szString && !isdigit(*szString))
            szString++;     /*  Skip non numbers    */

        starty=atoi(szString);

        while(*szString && isdigit(*szString))
            szString++;  /* skip start y number  */

        while(*szString && !isdigit(*szString))
            szString++; /*  skip non numbers    */

        sizex=atoi(szString);

        while(*szString && isdigit(*szString))
            szString++;  /* skip start y number  */

        while(*szString && !isdigit(*szString))
            szString++; /*  skip non numbers    */

        sizey=atoi(szString);

        sizex = sizex - startx; /*  Get size, not coords.   */
        sizey = sizey - starty;


    }
    else{

        startx = 0;
        sizex  = GetSystemMetrics(SM_CXSCREEN); /*  All across the bottom   */

        i=GetSystemMetrics(SM_CYSCREEN);        /*  Get Height of screen    */
        starty = i - (i * .3);                  /*  1/4 of the screen       */
        sizey = i - starty;

    }

    return(CreateWindow (szAppName,"Stdio Window", WS_OVERLAPPEDWINDOW,
        startx, starty, sizex, sizey, NULL, NULL, hInstance, NULL));



}



    /*  Winmain routine for stdio   */

int PASCAL WinMain(hInst, hPrevInstance, lpszCmdLine, nCmdShow)
    HANDLE  hInst;
    HANDLE  hPrevInstance;
    LPSTR   lpszCmdLine;
    int     nCmdShow;
    {
        MSG         msg;
        BOOL        bStat;

        hInstance = hInst;

        if(!hPrevInstance){
            WNDCLASS    wc;
            wc.style            =   CS_HREDRAW | CS_VREDRAW;
            wc.lpfnWndProc      =   WndProc;
            wc.cbClsExtra       =   0;
            wc.cbWndExtra       =   0;
            wc.hInstance        =   hInst;
	    wc.hIcon		=   LoadIcon(hInst,"StdioIcon");
            wc.hCursor          =   LoadCursor (NULL, IDC_ARROW);
            wc.hbrBackground    =   GetStockObject(WHITE_BRUSH);
	    wc.lpszMenuName	=   szAppName;
	    wc.lpszClassName	=   szAppName;

            if(!RegisterClass(&wc))
                return(FALSE);

            }


    hMainWnd = SetWindow();

    ShowWindow(hMainWnd, nCmdShow);
    UpdateWindow(hMainWnd);


    while(GetMessage(&msg, NULL, 0, 0)){
	TranslateMessage(&msg);
        DispatchMessage(&msg);
        }

    return(msg.wParam);

}
