/******************************************************************************\
	COPYRIGHT (c) Digital Equipment Corporation 1993
\******************************************************************************/


#include <windows.h>
#include "dxwin32.h"
#include <stdio.h>
#include <stdarg.h>
#include <malloc.h>
#include <stdlib.h>

#define WSTYLE (WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX) // Window style.
#define NLINES 30

/* non-global data */
static HINSTANCE hInst;
static HWND hMainWnd;
static HDC hMainDC;
static TEXTMETRIC TextMetrics;
static char *MainText[NLINES] = { 0 };
static int CurrentLine = 0;
static char szAppName[] = "dxwin32";
static char szTitle[]   = "eXcursion (SDK) Example Program";
static RECT WinRect;
static RECT ClientRect;

/* external functions called */
extern main();

/* global functions */
int dxprintf();

/* internal functions */
static void PrintText();
static void CallMain();

/******************************************************************************\
	This is a WinMain function supplied with the eXcursion SDK to be
	used with X/Motif applications that wish to operate under the Windows
	subsystem on Windows NT. It provides a scrolling text window to display
	messages sent to the function dxprintf, which should be used instead of
        the usual printf and fprintf. This progarm also simulates the standard
	C environment calling the applications "main" routine, passing along
        the usual argc and argv command line parameters. The main program is
        run in a separate thread so that it can continue to operate while the
        menus, etc., in the Windows part also continue to function.
\******************************************************************************/

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

    /* If first instance of this app, init application */
    if (!hPrevInstance)
        if (!InitApplication(hInstance))
            return (FALSE);

    /* Initialize this instance */
    if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

    hAccelTable = LoadAccelerators (hInstance, szAppName);

    /* Start  the X/Motif application in a separate thread */
    hMainThread = CreateThread(NULL, 0,
        (LPTHREAD_START_ROUTINE)CallMain,
        lpCmdLine,
        0,
        (LPDWORD)&MainThreadID);

    /* Process messages until QuitMessage */
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

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

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

    FUNCTION: dxprintf

    PURPOSE: Simulates printf in Windows subsystem

    COMMENTS: Prints the output to window instead of stdout (which doesn't exist under Windows).
    Tries to emulate printf as far as possible.

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

int dxprintf (char *s)
{
    char *p1, *p2;

    /* Look through the string, print anything printable, and handle newlines */
    p1 = s;
    while (p2 = strchr (p1, '\n')) /* Scroll and print as long as there are newlines */
    {
        if (p2 != p1) /* We have some text */
            PrintText (p1, (p2-p1));
        PrintText (NULL, 0); /* newline */
        p1 = p2 + 1;
    }

    p2 = s + strlen(s);

    if (p2 != p1) /* We have some printable text at the tail end of the string */
        PrintText (p1, (p2-p1));

    UpdateWindow (hMainWnd);
    return (1);
}

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

    FUNCTION: PrintText

    PURPOSE: Used with dxprintf to print and save the text. NULL string means go to
    the next line.

****************************************************************************/
static void PrintText (char *s, int len)
{
    int x, y, w, h, i;
    SIZE tsize;

    if (s == NULL) /* NULL string means newline */
    {
        /* If at the bottom of the window, scroll */
        if (CurrentLine == (NLINES-1))
        {
            /* Scroll up the contents of the window */
            w = ClientRect.right - ClientRect.left;
            h = ClientRect.bottom - ClientRect.top - TextMetrics.tmHeight;
            x = 0; /* Source x */
            y = TextMetrics.tmHeight; /* Source y */
            BitBlt (hMainDC, 0, 0, w, h, hMainDC, x, y, SRCCOPY);

            /* "Scroll" the text buffer, too */
            free (MainText[0]); /* deallocate the top string */
            for (i = 0 ; i < (NLINES-1) ; i++) /* copy all the pointers up one */
                MainText[i] = MainText[i+1];
            MainText[CurrentLine] = NULL;

            /* Clear out the bottom for the next text */
            Rectangle (hMainDC, ClientRect.left, 
                            ClientRect.bottom - TextMetrics.tmHeight, 
                            ClientRect.right, 
                            ClientRect.bottom);
        }
        else
        {
            CurrentLine++;
        }

        MainText[CurrentLine] = malloc (1); /* ready the new line for text */
        *MainText[CurrentLine] = '\0';
    }

    else
    {
        if (MainText[CurrentLine] == NULL)
        {
            MainText[CurrentLine] = malloc (1); /* ready the new line for text */
            *MainText[CurrentLine] = '\0';
        }

        /* Get the size of the text (if any) currently on the line */
        GetTextExtentPoint32 (hMainDC, MainText[CurrentLine], strlen(MainText[CurrentLine]), &tsize);

        /* Print the text after any existing text */
        x = tsize.cx;
        y = (CurrentLine) * TextMetrics.tmHeight;
        TextOut (hMainDC, x, y, s, len);

        /* Save it in the text buffer in case we need to repaint the window */
        MainText[CurrentLine] = realloc (MainText[CurrentLine], strlen (MainText[CurrentLine]) + len);
        strncat (MainText[CurrentLine], s, len);
    }
}

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

    FUNCTION: CallMain

    PURPOSE: Invoked when thread for main program is started.

    COMMENTS:

    This function calls the application's "main" routine so that it looks
    just the same as a standard C program. In reality it is a thread within
    the Win32 program.

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

static void
CallMain (LPDWORD lpCmdLine)
{
    dxprintf ("Main program called...\n\n");
    main(__argc, __argv);
    dxprintf("\nMain program returned...\n");
    ExitThread (0);
}

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

        FUNCTION: InitApplication(HINSTANCE)

        PURPOSE: Initializes window data and registers window class

        COMMENTS:

                This function is called at initialization time only if no other
                instances of the application are running.  This function performs
                initialization tasks that can be done once for any number of running
                instances.

                In this case, we initialize a window class by filling out a data
                structure of type WNDCLASS and calling the Windows RegisterClass()
                function.  Since all instances of this application use the same window
                class, we only need to do this when the first instance is initialized.


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

static 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 (hInstance, szAppName); // 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)

        PURPOSE:  Saves instance handle and creates main window

        COMMENTS:

                This function is called at initialization time for every instance of
                this application.  This function performs initialization tasks that
                cannot be shared by multiple instances.

                In this case, we save the instance handle in a static variable and
                create and display the main program window.

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

static BOOL InitInstance(
    HINSTANCE hInstance,
    int nCmdShow)
{
    HANDLE hFont;
    HGDIOBJ hgdiobj;
    BOOL havemenu;

    hInst = hInstance;

    // Create a main window for this application instance.

    hMainWnd = CreateWindow(
        szAppName,           // See RegisterClass() call.
        szTitle,             // Text for window title bar.
        WSTYLE,              // Window style.
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, // use defaults for now, we'll resize it later
        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 (!hMainWnd)
        return (FALSE);

    /* Get a DC for the window */
    hMainDC = GetDC (hMainWnd);

    /* Select the font into the window's DC */
    hFont = GetStockObject (ANSI_FIXED_FONT);
    SelectObject (hMainDC, hFont);

    /* Get the font metrics for future reference */
    GetTextMetrics (hMainDC, &TextMetrics);

    /* Find out if window has a menu, so we can tell AdjustWindowRect */
    havemenu = (GetMenu (hMainWnd) != NULL) ? TRUE : FALSE;

    /* Figure out how big to make the window based on our buffer size */
    WinRect.top = 0;
    WinRect.left = 0;
    WinRect.right = 80 * TextMetrics.tmAveCharWidth;
    WinRect.bottom = NLINES * TextMetrics.tmHeight;
    AdjustWindowRect (&WinRect, WSTYLE, havemenu);

    /* Size the window to hold our buffer of lines */
    SetWindowPos (hMainWnd, 
        NULL, 0, 0, // these will be ignored 
        (WinRect.right - WinRect.left), // width
        (WinRect.bottom - WinRect.top), //height
        SWP_NOZORDER & SWP_NOMOVE);

    /* Get the bounding rectange of the client part of the window */
    GetClientRect (hMainWnd, &ClientRect);

    /* Select a white brush for clearing out the background */
    hgdiobj = GetStockObject (WHITE_BRUSH);
    SelectObject (hMainDC, hgdiobj);

    /* Select a white brush for clearing out the background */
    hgdiobj= GetStockObject (WHITE_PEN);
    SelectObject (hMainDC, hgdiobj);

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

//    PostMessage(hMainWnd, WM_COMMAND, IDM_ABOUT, 0);

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

}

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

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

        PURPOSE:  Processes messages

        MESSAGES:

        WM_COMMAND    - application menu (About dialog box)
        WM_DESTROY    - destroy window

        COMMENTS:

        To process the IDM_ABOUT message, call MakeProcInstance() to get the
        current instance address of the About() function.  Then call Dialog
        box which will create the box according to the information in your
        dxwin32.rc file and turn control over to the About() function.  When
        it returns, free the intance address.

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

LRESULT CALLBACK WndProc(
HWND hWnd,         // window handle
UINT message,      // type of message
WPARAM uParam,     // additional information
LPARAM lParam)     // additional information
{
    FARPROC lpProcAbout;  // pointer to the "About" function
    int wmId, wmEvent;

    switch (message)
    {
        case WM_COMMAND:

            wmId    = LOWORD(uParam);
            wmEvent = HIWORD(uParam);

            switch (wmId)
            {
                case IDM_ABOUT:
                    lpProcAbout = MakeProcInstance((FARPROC)About, hInst);

                    DialogBox(hInst,           // current instance
                        "AboutBox",            // dlg resource to use
                        hWnd,                  // parent handle
                        (DLGPROC)lpProcAbout); // About() instance address

                    FreeProcInstance(lpProcAbout);
                    break;

                case IDM_EXIT:
                    DestroyWindow (hWnd);
                    break;

                case IDM_HELPHELP:
                    if(!WinHelp(hWnd, (LPSTR)NULL, HELP_HELPONHELP, 0))
                    {
                        MessageBox (GetFocus(),
                        "Unable to activate help",
                        szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);
                    }
                    break;

                // Here are all the other possible menu options,
                                // all of these are currently disabled:
                case IDM_NEW:
                case IDM_OPEN:
                case IDM_SAVE:
                case IDM_SAVEAS:
                case IDM_UNDO:
                case IDM_CUT:
                case IDM_COPY:
                case IDM_PASTE:
                case IDM_LINK:
                case IDM_LINKS:

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

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            int i;

            BeginPaint (hWnd, &ps);

            for (i = 0 ; i < CurrentLine ; i++)
            {
                ExtTextOut (hMainDC, 0, i * TextMetrics.tmHeight, 
                    (ETO_CLIPPED & ETO_OPAQUE), &ps.rcPaint,
                    MainText[i], strlen (MainText[i]), NULL);
            }

            EndPaint (hWnd, &ps);
            break;
        }

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

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

        FUNCTION: CenterWindow (HWND, HWND)

        PURPOSE:  Center one window over another

        COMMENTS:

        Dialog boxes take on the screen position that they were designed at,
        which is not always appropriate. Centering the dialog over a particular
        window usually results in a better position.

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

static 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)

        PURPOSE:  Processes messages for "About" dialog box

        MESSAGES:

        WM_INITDIALOG - initialize dialog box
        WM_COMMAND    - Input received

        COMMENTS:

        Display version information from the version section of the
        application resource.

        Wait for user to click on "Ok" button, then close the dialog box.

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

LRESULT CALLBACK About(
    HWND hDlg,           // window handle of the dialog box
    UINT message,        // type of message
    WPARAM uParam,       // message-specific information
    LPARAM lParam)
{
    static  HFONT hfontDlg;

    switch (message)
    {
        case WM_INITDIALOG:  // message: initialize dialog box
            // Create a font to use
            hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                VARIABLE_PITCH | FF_SWISS, "");

            // Center the dialog over the application window
            CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));

            return (TRUE);

        case WM_COMMAND:                      // message: received a command
            if (LOWORD(uParam) == IDOK        // "OK" box selected?
                || LOWORD(uParam) == IDCANCEL)
            {  // System menu close command?
                EndDialog(hDlg, TRUE);        // Exit the dialog
                DeleteObject (hfontDlg);
                return (TRUE);
            }
        break;
    }
    return (FALSE); // Didn't process the message

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