
/****************************************************************************
                    Microsoft RPC Version 1.0
                  Copyright Microsoft Corp. 1992
                        mandel Example

    FILE:       mandel.c
    
    PURPOSE:    Client side of the RPC distributed application

    COMMENTS:   Main code for the Windows Mandelbrot Set distributed 
                drawing program.

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

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <windows.h>      // Required for all Windows applications 
#include <windowsx.h>     // Allow portability from Win16, Win32   

#ifdef RPC
#include "mdlrpc.h"       // header file generated by the MIDL compiler
#endif
#include "mandel.h"      


/* data structures */

#ifdef RPC
char szTitle[] = "Mandelbrot RPC";
#else
char szTitle[] = "Mandelbrot Standalone";
#endif

CPOINT      cptUL = { (double) -2.05, (double) 1.4 };
double      dPrec = (double) .01;

HANDLE      hInst;         // current instance 

svr_table   SvrTable;
int         iLines = LINES;

int         fContinueZoom = TRUE;
int         fZoomIn       = TRUE;

// split current picture into 16 regions 
// zoom on most complex region;  region with most colors represented 
int         Histogram[4][4][NCOLORS+1] = {0}; 
int         ColorCount[4][4] = {0};
int         Max[4][4] = {0};

int         iHistMaxI = 2;
int         iHistMaxJ = 3;

RECT        rcZoom;
BOOL        fRectDefined = FALSE;

#ifdef RPC
int             fBound = FALSE;     // flag indicates whether bound to svr 
unsigned char * pszUuid             = NULL;
unsigned char * pszProtocolSequence = "ncacn_nb_nb";
unsigned char * pszEndpoint         = "15";
unsigned char * pszOptions          = NULL;
unsigned char * pszStringBinding;
unsigned char   pszNetworkAddress[UNCLEN+1] = {'\0'};
#endif

/* function prototypes */

void     DoSomeWork(HWND, BOOL);
void     InitHistogram(void);
void     CalcHistogram(int, int, DWORD, DWORD); 
void     PaintLine(HWND, svr_table *, HDC, int);
void     DrawRect(HWND, PRECT, BOOL, HDC);
COLORREF MapColor(DWORD, DWORD);


/*
 *  FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)
 *
 *  PURPOSE: Calls initialization function, processes message loop
 *
 *  COMMENTS:
 *
 *      Windows recognizes this function by name as the initial entry point
 *      for the program.  This function calls the application initialization
 *      routine, if no other instance of the program is running, and always
 *      calls the instance initialization routine.  It then executes a message
 *      retrieval and dispatch loop that is the top-level control structure
 *      for the remainder of execution.  The loop is terminated when a WM_QUIT
 *      message is received, at which time this function exits the application
 *      instance by returning the value passed by PostQuitMessage().
 *
 *      If this function must abort before entering the message loop, it
 *      returns the conventional value NULL.
 */

int WINAPI WinMain(
    HINSTANCE hInstance,            /* current instance         */
    HINSTANCE hPrevInstance,        /* previous instance        */
    LPSTR lpCmdLine,                /* command line             */
    int nCmdShow)                   /* show-window type (open/icon) */
{

    MSG msg;         

    UNREFERENCED_PARAMETER(lpCmdLine);

    if (!hPrevInstance)  /* Other instances of app running? */
        if (!InitApplication(hInstance))  /* Initialize shared things */
            return(FALSE);  /* Exits if unable to initialize */

    /* Perform initializations that apply to a specific instance */
    if (!InitInstance(hInstance, nCmdShow))
        return(FALSE);

    /* Acquire and dispatch messages until a WM_QUIT message is received. */
    while (GetMessage(&msg,        /* message structure              */
                      (HWND)NULL,  /* handle of window receiving the message */
                      0,           /* lowest message to examine      */
                      0))          /* highest message to examine     */
    {
        TranslateMessage(&msg);    /* Translates virtual key codes   */
        DispatchMessage(&msg);     /* Dispatches message to window   */
    }

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

} 


/*
 *  FUNCTION: InitApplication(HANDLE)
 *
 *  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.
 */

BOOL InitApplication(HANDLE hInstance)    /* current instance */
{

    WNDCLASS  wc;

    /* Fill in window class structure with parameters that describe the       */
    /* main window.                                                           */
    wc.style = 0;                       /* Class style(s).                    */
    wc.lpfnWndProc = (WNDPROC)MainWndProc;     
                                        /* Function to retrieve messages for  */
                                        /* windows of this class.             */
    wc.cbClsExtra = 0;                  /* No per-class extra data.           */
    wc.cbWndExtra = 0;                  /* No per-window extra data.          */
    wc.hInstance = hInstance;           /* Application that owns the class.   */
    wc.hIcon = LoadIcon(hInstance, "RPC_ICON");
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH); 
    wc.lpszMenuName =  "MandelMenu";    /* Name of menu resource in .RC file. */
    wc.lpszClassName = "MandelClass";   /* Name used in call to CreateWindow. */

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

}


/*
 *  FUNCTION:  InitInstance(HANDLE, 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.
 */

BOOL InitInstance(HANDLE   hInstance,   /* Current instance identifier.       */
                  int      nCmdShow)    /* Param for first ShowWindow() call. */
{
    HWND            hWnd;               /* Main window handle.                */
    HMENU           hMenu;
    RECT            rc;

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

    /* Create a main window for this application instance.  */
    hWnd = CreateWindow(
               "MandelClass",        /* See RegisterClass() call.          */
               szTitle,              /* Text for window title bar.         */
               WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_BORDER | WS_MINIMIZEBOX,
               CW_USEDEFAULT,        /* Default horizontal position.       */
               CW_USEDEFAULT,        /* Default vertical position.         */
               WIDTH,                /* Default width.                     */
               HEIGHT,               /* Default height.                    */
               (HWND) NULL,          /* Overlapped windows have no parent. */
               (HMENU) NULL,         /* Use the window class menu.         */
               hInstance,            /* This instance owns this window.    */
               (LPVOID) NULL         /* Pointer not needed.                */
           );

    /* If window could not be created, return "failure" */
    if (!hWnd)
        return(FALSE);

    /* Make the window visible; update its client area; and return "success" */
    ShowWindow(hWnd, nCmdShow);  /* Show the window                */
    UpdateWindow(hWnd);          /* Sends WM_PAINT message         */
    rc.top = rc.left = 0;
    rc.bottom = HEIGHT-1;
    rc.right = WIDTH-1;

    SetNewCalc(cptUL, dPrec, rc);
    hMenu = GetMenu(hWnd);

#ifndef RPC
    EnableMenuItem(hMenu, IDM_SERVER, MF_GRAYED);  /* disable option */
#endif

    return(TRUE);               /* Returns the value from PostQuitMessage */
}


/*
 *  FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)
 *
 *  PURPOSE:  Processes messages
 *
 *  MESSAGES: 
 *      WM_COMMAND    - application menu
 *      WM_DESTROY    - destroy window
 *
 *  COMMENTS:
 */

LONG APIENTRY MainWndProc(
    HWND hWnd,               /* window handle               */
    UINT message,            /* type of message             */
    UINT wParam,             /* additional information      */
    LONG lParam)             /* additional information      */
{
    DLGPROC lpProc;          /* pointer to the dialog box function */
    PAINTSTRUCT ps;
    HDC hdc;
    static HDC     hdcMem;
    static HBITMAP hbmMem;
    static int     width;
    static int     height;
    RECT           rc;
    static BOOL    fButtonDown = FALSE;
    static POINT   pSelected;
    POINT          pMove;
    int            iWidthNew;
    int            iHeightNew;
    static int     miOldLines;
    double         scaling;

    switch (message) {

    case WM_CREATE:

#ifdef WIN16
        PostMessage(hWnd, WM_COMMAND, IDM_SERVER, 0L);  // force server spec 
#else
        PostMessage(hWnd, WM_COMMAND, IDM_BIND, 0L);    // bind to server 
#endif

        if (!InitRemote(hWnd))
            return(FALSE);

        InitHistogram();

        hdc = GetDC(hWnd);
        hdcMem = CreateCompatibleDC(hdc);
        GetWindowRect(hWnd, &rc);
        width = rc.right - rc.left;
        height = rc.bottom - rc.top;
        hbmMem = CreateCompatibleBitmap(hdc, width, height);
        SelectObject(hdcMem, hbmMem);

        ReleaseDC(hWnd,hdc);

        rc.left = rc.top = 0;
        rc.right = width+1;
        rc.bottom = height + 1;
        FillRect(hdcMem, &rc, GetStockObject(WHITE_BRUSH));

        SetTimer(hWnd, 1, POLL_TIME, NULL);  // set timer for polls

        CheckMenuItem(GetMenu(hWnd), IDM_4LINES, MF_CHECKED);
        CheckMenuItem(GetMenu(hWnd), IDM_CONTINUOUS, MF_CHECKED);
        miOldLines = IDM_4LINES;  // save to uncheck
        break;

    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps);
        BitBlt(hdc, 
               ps.rcPaint.left,
               ps.rcPaint.top,
               ps.rcPaint.right - ps.rcPaint.left,
               ps.rcPaint.bottom - ps.rcPaint.top,
               hdcMem, 
               ps.rcPaint.left, 
               ps.rcPaint.top, 
               SRCCOPY);
        EndPaint(hWnd, &ps);
        break;

    case WM_COMMAND:  // message: command from application menu 
        switch(wParam) {

        case IDM_BIND:

#ifdef RPC
            if (Bind(hWnd) != RPC_S_OK)
                PostMessage(hWnd, WM_DESTROY, 0, 0L);
#endif
            break;

        case IDM_ABOUT:
            lpProc = MakeProcInstance(About, hInst);

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

            FreeProcInstance(lpProc);
            break;

        case IDM_ZOOMOUT:
            if (dPrec > (double)MAXPREC)  // don't allow the zoom out
                break;

            rcZoom.left = WIDTH/4 + (WIDTH/8);  // center square
            rcZoom.top   = HEIGHT/4 + (HEIGHT/8);
            rcZoom.right = rcZoom.left + (WIDTH/4);
            rcZoom.bottom = rcZoom.top + (HEIGHT/4);

            cptUL.real -= (rcZoom.left * dPrec); // inverse of zoom in
            cptUL.imag += (rcZoom.top * dPrec);
            iWidthNew = (rcZoom.right - rcZoom.left + 1);
            iHeightNew = (rcZoom.bottom - rcZoom.top + 1);
            scaling = ((double) ((iWidthNew > iHeightNew) ? iWidthNew : iHeightNew) / (double) width);
            dPrec /= scaling;

            rc.left = rc.top = 0;
            rc.bottom = height - 1;
            rc.right = width - 1;
            
            SetNewCalc(cptUL, dPrec, rc);
            fRectDefined = FALSE;
            DoSomeWork(hWnd, FALSE);
            break;

        case IDM_ZOOMIN:  // zoom in on selected rectangle
            // if no rectangle, don't zoom in
            if (!fRectDefined)
                break;

            if (dPrec < (double)MINPREC)  // don't allow zoom in
                break;

            DrawRect(hWnd, &rcZoom, TRUE, hdcMem);  // draw new rect

            // calculate new upper-left
            cptUL.real += (rcZoom.left * dPrec);
            cptUL.imag -= (rcZoom.top * dPrec);

            iWidthNew = (rcZoom.right - rcZoom.left + 1);
            iHeightNew = (rcZoom.bottom - rcZoom.top + 1);
            scaling = ((double) ((iWidthNew > iHeightNew) ? iWidthNew : iHeightNew) / (double) width);

            dPrec *= scaling;

            rc.left = rc.top = 0;
            rc.bottom = height - 1;
            rc.right = width - 1;
            
            SetNewCalc(cptUL, dPrec, rc);
            IncPictureID();

            fRectDefined = FALSE;
            DoSomeWork(hWnd, FALSE);
            break;

        case IDM_CONTINUOUS:  // continuous zoom in
            if (fContinueZoom == TRUE) {
                CheckMenuItem(GetMenu(hWnd), IDM_CONTINUOUS, MF_UNCHECKED);
                fContinueZoom = FALSE;
            }
            else {
                CheckMenuItem(GetMenu(hWnd), IDM_CONTINUOUS, MF_CHECKED);
                fContinueZoom = TRUE;
            }
            break;

        case IDM_REDRAW:
            if (fContinueZoom == TRUE)
                InitHistogram();

            rc.left = rc.top = 0;
            rc.right = width+1;
            rc.bottom = height + 1;
            FillRect(hdcMem, &rc, GetStockObject(WHITE_BRUSH));
            InvalidateRect(hWnd, NULL, TRUE);

            rc.left = rc.top = 0;
            rc.bottom = height - 1;
            rc.right = width - 1;
            SetNewCalc(cptUL, dPrec, rc);

            fRectDefined = FALSE;
            DoSomeWork(hWnd, FALSE);
            break;

        case IDM_EXIT:
            DestroyWindow(hWnd);
            FreeDrawBuffer();
            break;

        case IDM_TOP:
            cptUL.real = (double) -2.05;
            cptUL.imag = (double) 1.4;
            dPrec = .01;

            rc.left = rc.top = 0;
            rc.bottom = height - 1;
            rc.right = width - 1;
            
            SetNewCalc(cptUL, dPrec, rc);
            ResetPictureID();  // incremented past original

            fRectDefined = FALSE;
            DoSomeWork(hWnd, FALSE);
            break;

        case IDM_1LINE:
        
        case IDM_2LINES:
        
        case IDM_4LINES:

            CheckMenuItem(GetMenu(hWnd), miOldLines, MF_UNCHECKED);
            miOldLines = wParam;
            switch(wParam) {

            case IDM_1LINE:
                iLines = 1;
                break;
            case IDM_2LINES:
                iLines = 2;
                break;
            case IDM_4LINES:
                iLines = 4;
                break;
            }

            CheckMenuItem(GetMenu(hWnd), miOldLines, MF_CHECKED);
            break;

#ifdef RPC
        case IDM_SERVER:
            lpProc = MakeProcInstance(Server, hInst);
            DialogBox(hInst,         // current instance         
                      "ServerBox",   // resource to use          
                      hWnd,          // parent handle            
                      lpProc);       // Server  instance address 
            FreeProcInstance(lpProc);
            break;

#endif

        default:  // Lets Windows process it   
            return(DefWindowProc(hWnd, message, wParam, lParam));
        
        }

        break;

    case WM_DESTROY:  // message: window being destroyed 
        PostQuitMessage(0);
        DeleteDC(hdcMem);
        DeleteObject(hbmMem);
        break;

    case WM_DOSOMEWORK:  // do another slice of calculation work
        DoSomeWork(hWnd, FALSE);
        break;

    case WM_PAINTLINE:  // The shared buffer contains a line of data; draw it
        PaintLine(hWnd,
                  &SvrTable,
                  hdcMem,
                  height);
        break;

    case WM_TIMER:  // timer means we should do another slice of work
        DoSomeWork(hWnd, TRUE);
        break;

    case WM_LBUTTONDOWN:  // left button down; start to define a zoom rectangle
        if (fRectDefined)
            DrawRect(hWnd, &rcZoom, FALSE, hdcMem);  // undraw old rectangle
            
        // initialize rectangle
        rcZoom.left = rcZoom.right = pSelected.x = LOWORD(lParam);
        rcZoom.top = rcZoom.bottom = pSelected.y = HIWORD(lParam);

        // draw the new rectangle
        DrawRect(hWnd, &rcZoom, TRUE, hdcMem);

        fRectDefined = TRUE;
        fButtonDown = TRUE;
        SetCapture(hWnd);  // capture all mouse events
        break;

    case WM_MOUSEMOVE:  // mouse move 
        // if the button is down, change the rect
        if (!fButtonDown)
            break;

        DrawRect(hWnd, &rcZoom, FALSE, hdcMem);  // undraw old rect

        pMove.x = LOWORD(lParam);
        pMove.y = HIWORD(lParam);

        // update the selection rectangle
        if (pMove.x <= pSelected.x)
            rcZoom.left = pMove.x;
        if (pMove.x >= pSelected.x)
            rcZoom.right = pMove.x;
        if (pMove.y <= pSelected.y)
            rcZoom.top = pMove.y;
        if (pMove.y >= pSelected.y)
            rcZoom.bottom = pMove.y;

        DrawRect(hWnd, &rcZoom, TRUE, hdcMem);  // draw new rect
        break;

    case WM_LBUTTONUP:  // button up; end selection 
        fButtonDown = FALSE;
        ReleaseCapture();
        break;

    case WM_EXCEPTION:
        lpProc = MakeProcInstance(Exception, hInst);
        DialogBox(hInst,         // current instance         
                  "ExceptionBox",// resource to use          
                  hWnd,          // parent handle            
                  lpProc);       // Server  instance address 
        FreeProcInstance(lpProc);
        UnlockDrawBuffer();
        ReturnDrawBuffer();
        break;

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

    }

    return(0L);
}

    
/*
 *  FUNCTION: About(HWND, unsigned, WORD, LONG)
 *
 *  PURPOSE:  Processes messages for "About" dialog box
 *
 *  MESSAGES:
 *
 *      WM_INITDIALOG - initialize dialog box
 *      WM_COMMAND    - Input received
 *
 *  COMMENTS:
 *
 *      No initialization is needed for this particular dialog box, but TRUE
 *      must be returned to Windows.
 *
 *      Wait for user to click on "Ok" button, then close the dialog box.
 */

BOOL APIENTRY About(
    HWND hDlg,              /* window handle of the dialog box */
    UINT message,           /* type of message                 */
    UINT wParam,            /* message-specific information    */
    LONG lParam)
{

    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:     /* message: initialize dialog box  */
        return(TRUE);

    case WM_COMMAND:        /* message: received a command     */
        if (wParam == IDOK || wParam == IDCANCEL) 
        {
            EndDialog(hDlg, TRUE);    /* Exits the dialog box  */
            return(TRUE);
        }
        break;
    }

    return(FALSE);          /* Didn't process a message        */
}


/*
 *  FUNCTION: Server(HWND, unsigned, WORD, LONG)
 *
 *  PURPOSE:  Processes messages for "Server" dialog box
 *
 *  MESSAGES:
 *
 *      WM_INITDIALOG - initialize dialog box
 *      WM_COMMAND    - Input received
 
 *  COMMENTS:
 *
 *      No initialization is needed for this particular dialog box, but TRUE
 *      must be returned to Windows.
 *
 *      Wait for user to click on "Ok" button, then close the dialog box.
 */

BOOL APIENTRY Server(
    HWND hDlg,               /* window handle of the dialog box */
    UINT message,            /* type of message             */
    UINT wParam,             /* message-specific information    */
    LONG lParam)
{

    UNREFERENCED_PARAMETER(lParam);

#ifdef RPC

    switch (message) {

    case WM_INITDIALOG:      /* message: initialize dialog box */
        SetDlgItemText(hDlg, IDD_SERVERNAME, pszNetworkAddress);
        return(TRUE);

    case WM_COMMAND:         /* message: received a command    */
        switch(wParam) {

        case IDCANCEL:       /* System menu close command?     */
            EndDialog(hDlg, FALSE);
            return(TRUE);

        case IDOK:            /* "OK" box selected?            */
            GetDlgItemText(hDlg, IDD_SERVERNAME, pszNetworkAddress, UNCLEN);

            /* pszNetworkAddress must start with two backslashes */
            if (pszNetworkAddress[0] != '\0' && 
                strncmp(pszNetworkAddress, "\\\\", 2)) {

                unsigned char oldNetAddr[UNCLEN+1];

                strcpy(oldNetAddr, pszNetworkAddress);
                sprintf(pszNetworkAddress, "\\\\%s", oldNetAddr);
            }

            if (Bind(hDlg) != RPC_S_OK) {
                EndDialog(hDlg, FALSE);
                return(FALSE);
            }

            EndDialog(hDlg, TRUE);
            return(TRUE);
        }  

    } 

#endif

    return(FALSE);           /* Didn't process a message      */

}

    
/*
 *  FUNCTION: Exception(HWND, unsigned, WORD, LONG)
 *
 *  PURPOSE:  Processes messages for "Exception" dialog box
 *
 *  MESSAGES:
 *
 *      WM_INITDIALOG - initialize dialog box
 *      WM_COMMAND    - Input received
 *
 *  COMMENTS:
 *
 *      No initialization is needed for this particular dialog box, but TRUE
 *      must be returned to Windows.
 *
 *      Wait for user to click on "Ok" button, then close the dialog box.
 */

BOOL APIENTRY Exception(
    HWND hDlg,               /* window handle of the dialog box */
    UINT message,            /* type of message             */
    UINT wParam,             /* message-specific information    */
    LONG lParam)
{

    UNREFERENCED_PARAMETER(lParam);

#ifdef RPC

    switch (message) {

    case WM_INITDIALOG:      /* message: initialize dialog box */
        SetDlgItemText(hDlg, IDD_SERVERNAME, pszNetworkAddress);
        SetDlgItemText(hDlg, IDD_ENDPOINT, pszEndpoint);
        return(TRUE);

    case WM_COMMAND:         /* message: received a command    */
        switch(wParam) {

        case IDCANCEL:       /* System menu close command?     */
            EndDialog(hDlg, FALSE);
            return(TRUE);

        case IDOK:            /* "OK" box selected?            */
            GetDlgItemText(hDlg, IDD_SERVERNAME, pszNetworkAddress, UNCLEN);
            GetDlgItemText(hDlg, IDD_ENDPOINT, pszEndpoint, UNCLEN);

            /* pszNetworkAddress must start with two backslashes */
            if (pszNetworkAddress[0] != '\0' && 
                strncmp(pszNetworkAddress, "\\\\", 2)) {

                unsigned char oldNetAddr[UNCLEN+1];

                strcpy(oldNetAddr, pszNetworkAddress);
                sprintf(pszNetworkAddress, "\\\\%s", oldNetAddr);
            }

            if (Bind(hDlg) != RPC_S_OK) {
                EndDialog(hDlg, FALSE);
                return(FALSE);
            }

            EndDialog(hDlg, TRUE);
            return(TRUE);

        case IDM_EXIT:
            DestroyWindow(hDlg);
            FreeDrawBuffer();
            exit(1);
        }  

    } 

#endif

    return(FALSE);           /* Didn't process a message      */

}


/*
 *  DoSomeWork --
 *
 *  This function does our work for us. It does it in little pieces, and
 *  will schedule itself as it sees fit.
 */

void
DoSomeWork(HWND    hwnd,
           BOOL    fTimer)
{
    static WORD   wIteration = 0;

    if (fTimer) {
        wIteration++;

        // on every nth tick, we send out a poll
        if (wIteration == 120) {  // tune this?
            wIteration = 0;
            return;
        }

        // on the half-poll, we check for responses
        if ((wIteration == 2) || (wIteration == 10)) {
            return;
        }
    }

    if (CheckDrawStatus(hwnd)) 
        SendMessage(hwnd, WM_DOSOMEWORK, 0, 0L);

    return;
}

    
/*
 *  DrawRect --
 *
 *  This function draws (or undraws) the zoom rectangle.
 */

void
DrawRect(HWND      hwnd,
         PRECT     prc,
         BOOL      fDrawIt,
         HDC       hdcBM)
{
    HDC     hdc;
    DWORD   dwRop;

    hdc = GetDC(hwnd);

    if (fDrawIt)
        dwRop = NOTSRCCOPY;
    else
        dwRop = SRCCOPY;

    // top side
    BitBlt(hdc, prc->left, prc->top, (prc->right - prc->left) + 1,
           1, hdcBM, prc->left, prc->top, dwRop);

    // bottom side
    BitBlt(hdc, prc->left, prc->bottom, (prc->right - prc->left) + 1,
           1, hdcBM, prc->left, prc->bottom, dwRop);

    // left side
    BitBlt(hdc,prc->left, prc->top, 1, (prc->bottom - prc->top) + 1,
           hdcBM, prc->left, prc->top, dwRop);

    // right side
    BitBlt(hdc,prc->right, prc->top, 1, (prc->bottom - prc->top) + 1,
           hdcBM, prc->right, prc->top, dwRop);

    ReleaseDC(hwnd, hdc);
}

    
/*
 *  PaintLine --
 *
 *  This function paints a buffer of data into the bitmap.
 */

void
PaintLine(HWND        hwnd,
          svr_table * pst,
          HDC         hdcBM,
          int         cHeight)
{
    LPWORD  pwDrawData;
    int     y;
    int     x;
    DWORD   dwThreshold;
    RECT    rc;
    WORD    lines;

    lines  = (WORD) pst->cLines;

    // picture ID had better match, or else we skip it
    if (CheckDrawingID(pst->cPicture))
    {
        // figure out our threshold
        dwThreshold = QueryThreshold();

        // get a pointer to the draw buffer
        pwDrawData = (LPWORD) LockDrawBuffer();
        if (pwDrawData == NULL) {
            ReturnDrawBuffer();
            return;
        }

        // starting x coordinate
        x = (int) pst->dwLine;

        // now loop through the rectangle
        while (lines-- > 0)
        {
            // bottom to top, since that's the order of the data in the buffer
            y = (int) cHeight-1;

            while (y >= 0) 
            {
                // draw a pixel
                SetPixel(hdcBM, x,y, MapColor(*pwDrawData, dwThreshold));

                if (fContinueZoom == TRUE)
                    CalcHistogram(x, y, *pwDrawData, dwThreshold);

                // now increment buffer pointer and y coord
                y--;
                pwDrawData++;
            }

            // increment X coordinate
            x++;   
        }

        // figure out the rectangle to invalidate
        rc.top = 0;
        rc.bottom = cHeight;
        rc.left = (int)(pst->dwLine);
        rc.right = (int)(pst->dwLine) + pst->cLines;

        UnlockDrawBuffer();

        // and invalidate it on the screen so we redraw it
        InvalidateRect(hwnd, &rc, FALSE);
    }

    // free this for someone else to use
    ReturnDrawBuffer();

    // and change the pipe state, if necessary
    if (pst->iStatus == SS_PAINTING)
        pst->iStatus = SS_IDLE;

}


#define CLR_BLACK       RGB(0,0,0)
#define CLR_DARKBLUE    RGB(0,0,127)
#define CLR_BLUE        RGB(0,0,255)
#define CLR_CYAN        RGB(0,255,255)
#define CLR_DARKGREEN   RGB(0,127,0)
#define CLR_GREEN       RGB(0,255,0)
#define CLR_YELLOW      RGB(255,255,0)
#define CLR_RED         RGB(255,0,0)
#define CLR_DARKRED     RGB(127,0,0)
#define CLR_WHITE       RGB(255,255,255)
#define CLR_PALEGRAY    RGB(194,194,194)
#define CLR_DARKGRAY    RGB(127,127,127)


static COLORREF ColorMapTable[] = {     // size = NCOLORS
    CLR_DARKBLUE,
    CLR_BLUE,
    CLR_CYAN,
    CLR_DARKGREEN,
    CLR_GREEN,
    CLR_YELLOW,
    CLR_RED,
    CLR_DARKRED,
    CLR_WHITE,
    CLR_PALEGRAY,
    CLR_DARKGRAY};


/*
 *  MapColor --
 *
 *  This function maps an iteration count into a corresponding RGB color.
 */

COLORREF
MapColor(DWORD  dwIter,
         DWORD  dwThreshold)
{

    /* if it's beyond the threshold, call it black */
    if (dwIter >= dwThreshold) {
        return(CLR_BLACK);
    }

    /* get a modulus based on the number of colors */
    dwIter = (dwIter / 3) % NCOLORS; // 11;

    /* and return the appropriate color */
    return(ColorMapTable[dwIter]);

}


/* 
 * CalcHistogram --
 *
 * This function is used to select the region that is the
 * most complex and will be used to zoom in for the next picture;
 * it contains the most colors.  The number of colors are counted.
 */

void
CalcHistogram(int    x,
              int    y,
              DWORD  dwIter,
              DWORD  dwThreshold)
{

    /* if it's beyond the threshold, call it black */
    if (dwIter >= dwThreshold) {
        Histogram[x/(WIDTH/4)][y/(HEIGHT/4)][NCOLORS]++;
        return;
    }
    
    /* get a modulus based on the number of colors */
    dwIter = (dwIter / 3) % NCOLORS; // 11;

    /* and bump the count for the appropriate color */
    Histogram[x/(WIDTH/4)][y/(HEIGHT/4)][dwIter]++;  // region of map

    return;

}


/*
 * InitHistogram --
 *
 * This function initializes the histogram data structures.
 */

void InitHistogram(void)
{
    int i, j, k;

    for (i = 0; i < 4; i++)
        for (j = 0; j < 4; j++)
            for (k = 0; k <= NCOLORS; k++)
                Histogram[i][j][k] = 0;  // count of colors 
}


/* 
 * CountHistogram --
 *
 * This function determines the number of colors represented
 * within a region.  The region with the most colors is
 * selected using the maxi and maxj values.  X and Y coordinates
 * corresponding to these regions are stored in the HistRegion
 * table and are used for the next picture.
 */

void CountHistogram(void)
{

    int i, j, k;

    /* count the number of colors in each region */
    /* find the color that dominates each region */
    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            ColorCount[i][j] = 0;
            Max[i][j] = 0;
            for (k = 0; k <= NCOLORS; k++) {
                if (Histogram[i][j][k] > Max[i][j])
                    Max[i][j] = Histogram[i][j][k];
                if (Histogram[i][j][k] != 0)  // count of colors 
                    ColorCount[i][j]++;
            }
        }
    }

    iHistMaxI = 0;
    iHistMaxJ = 0;

    /* if several regions have the same number of colors,        */
    /* select the region with the most variety: the smallest max */
    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            if (   (ColorCount[i][j] >= ColorCount[iHistMaxI][iHistMaxJ])
                && (Max[i][j] < Max[iHistMaxI][iHistMaxJ]) ) {
                iHistMaxI = i;
                iHistMaxJ = j;
            }
        }
    }
    
    InitHistogram();  // initialize for next time 

}


#ifdef RPC

void __RPC_FAR * __RPC_API midl_user_allocate(size_t len)
{
    UNREFERENCED_PARAMETER(len);
    return(NULL);
}


void __RPC_API midl_user_free(void __RPC_FAR * ptr)
{
    UNREFERENCED_PARAMETER(ptr);
    return;
}


/*
 *  FUNCTION: Bind(HWND)
 *
 *  PURPOSE:  Make RPC API calls to bind to the server application
 *
 *  COMMENTS:
 *
 *      The binding calls are made from InitInstance() and whenever
 *      the user changes the server name or endpoint. If the bind
 *      operation is successful, the global flag fBound is set to TRUE.
 *
 *      The global flag fBound is used to determine whether to call
 *      the RPC API function RpcBindingFree.
 */

RPC_STATUS Bind(HWND hWnd)
{
    RPC_STATUS status;
    char pszFail[MSGLEN];

    if (fBound == TRUE) {  // unbind only if bound   
        status = RpcStringFree(&pszStringBinding);  
        if (status) {
            sprintf(pszFail, "RpcStringFree failed 0x%x", status);
            MessageBox(hWnd, 
                       pszFail, 
                       "RPC Sample Application",
                       MB_ICONSTOP);
            return(status);
        }

        status = RpcBindingFree(&hMandel);  
        if (status) {
            sprintf(pszFail, "RpcBindingFree failed 0x%x", status);
            MessageBox(hWnd, 
                       pszFail, 
                       "RPC Sample Application",
                       MB_ICONSTOP);
            return(status);
        }

        fBound = FALSE;  // unbind successful; reset flag
    }

    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    if (status) {
        sprintf(pszFail, "RpcStringBindingCompose returned: (0x%x)\nNetwork Address = %s\n",
              status, pszNetworkAddress);
        MessageBox(hWnd, pszFail, "RPC Sample Application", MB_ICONINFORMATION);
        return(status);
    }

    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &hMandel);
    if (status) {
        sprintf(pszFail, "RpcBindingFromStringBinding returned: (0x%x)\nString = %s\n",
             status, pszStringBinding);
        MessageBox(hWnd, pszFail, "RPC Sample Application", MB_ICONINFORMATION);
        return(status);
    }

    fBound = TRUE;  // bind successful; reset flag 

    return(status);
}

#endif


/* end mandel.c */
