/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994
                        mandel Example

    FILE:      remote.c
    
    PURPOSE:   Client side of the RPC distributed application Mandel
    
    COMMENTS:  Code to do the remote calculations for the Windows 
               Mandelbrot Set distributed drawing program.
 
               Information coming into this module (via API calls) is 
               based on upper-left being (0,0) (the Windows standard). 
               We translate that to lower-left is (0,0) before we ship 
               it out onto the net, and we do reverse translations 
               accordingly. 

               The iteration data is passed back to the main window
               procedure (by means of a WM_PAINTLINE message) which 
               draws the picture. 

               A word about the shared buffer: multiple buffers could
               be used, but a single one is used. The buffer is requested 
               in this code, and then released after the data has been
               drawn (in PaintLine() in mandel.c). So long as the painting
               is done quickly, this is efficient.
 
****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>

#ifdef RPC
#include "mdlrpc.h"
#endif
#include "mandel.h"


/*
 * External variables
 */
extern int          fBound;
extern svr_table    SvrTable;    // the server table
extern int          iLines;
extern double       dPrec;
extern int          fContinueZoom;
extern int          fZoomIn;
extern int          iHistMaxI;
extern int          iHistMaxJ;
extern RECT         rcZoom;
extern BOOL         fRectDefined;


/*
 * Picture information
 */
int             cPictureID = 0;    // picture id, in case we reset in the middle
static CPOINT   cptLL;             // upper-left
static double   dPrecision;        // precision of draw
static LONGRECT rclPicture;        // rectangle defining client window
static DWORD    dwCurrentLine;     // next line to be drawn
static DWORD    dwThreshold;       // threshold for iterations

/*
 * Function prototypes for local procs
 */
DWORD CalcThreshold(double);


/*
 *  InitRemote --
 *
 *  This function initializes everything for our remote connections.
 *  It gets the local wksta name (making sure the wksta is started)
 *  and it creates the mailslot with which to collect replies to our poll.
 *
 *  RETURNS
 *      TRUE    - initialization succeeded
 *      FALSE   - initialization failed, can't go on
 */

BOOL InitRemote(HWND hWnd)
{

#ifndef RPC
    UNREFERENCED_PARAMETER(hWnd);
#endif

    // set up our local entry
    strcpy(SvrTable.name, "Local machine");
    SvrTable.iStatus = SS_LOCAL;

    // good, we succeeded
    return(TRUE);
}


/*
 *  CheckDrawStatus --
 *
 *  This function does a check of all buffers being drawn.
 *
 *  If it finds an idle pipe, and there is work to be done, it assigns
 *      a line, and writes out the request.
 *  If it finds a read-pending pipe, it checks if the read has completed.
 *      If it has, it is read and a message is sent so the read data can
 *      be processed.
 *
 *  RETURNS
 *      TRUE    - we did a piece of work
 *      FALSE   - we could not find any work to do.
 */

BOOL CheckDrawStatus(HWND hwnd)
{
    CALCBUF     cb;
    LPVOID      pbBuf;
	
    while(TRUE) {

        // Check the status
        switch(SvrTable.iStatus) {

        case SS_PAINTING:
            break;

        case SS_IDLE:
            break;

        case SS_LOCAL:
            // Do a chunk of work locally

#ifdef RPC
            if (fBound == FALSE)
                break;
#endif

            if ((long) dwCurrentLine > rclPicture.xRight) {
                if (fContinueZoom == TRUE) {
                    if ((fZoomIn == TRUE) && (dPrec < (double)MINPREC))
                        fZoomIn = FALSE;  // start zooming out
                    if ((fZoomIn == FALSE) && (dPrec > (double)MAXPREC))
                        fZoomIn = TRUE;
                    if (fZoomIn) {
                        CountHistogram();
                        rcZoom.top    = iHistMaxJ * (WIDTH/4);
                        rcZoom.bottom = rcZoom.top + (WIDTH/4) - 1;
                        rcZoom.left   = iHistMaxI * (HEIGHT/4);
                        rcZoom.right  = rcZoom.left + (HEIGHT/4) - 1;
                        fRectDefined = TRUE;
                        PostMessage(hwnd, WM_COMMAND, IDM_ZOOMIN, 0L);
                    }
                    else
                        PostMessage(hwnd, WM_COMMAND, IDM_ZOOMOUT, 0L);
                }
                break;
            }

            if (TakeDrawBuffer() == FALSE)
                break;

            pbBuf = LockDrawBuffer();
            
            cb.rclDraw.xLeft = dwCurrentLine;
            cb.rclDraw.xRight = dwCurrentLine + iLines - 1;
            cb.rclDraw.yTop = rclPicture.yTop;
            cb.rclDraw.yBottom = rclPicture.yBottom;

            RpcTryExcept {
                MandelCalc(&cptLL,
                           &(cb.rclDraw),
                           dPrecision,
                           dwThreshold,
                           (LINEBUF *) pbBuf);
            }
            RpcExcept(1) {
                char szFail[MSGLEN];
                
            	sprintf (szFail, "%s (0x%x)\n", EXCEPT_MSG, RpcExceptionCode());
                MessageBox(hwnd,
                           szFail,
                           "Remote Procedure Call",
                           MB_ICONINFORMATION);
		        KillTimer(hwnd, 1);  // stop timer for polls
	            EnableMenuItem(GetMenu(hwnd), IDM_GO, MF_ENABLED);	// enable GO
	            UnlockDrawBuffer();

			    ReturnDrawBuffer();
		        return(FALSE);
            }
            RpcEndExcept

            UnlockDrawBuffer();

            SvrTable.cPicture = cPictureID;
            SvrTable.dwLine = dwCurrentLine;
            SvrTable.cLines = iLines;

            PostMessage(hwnd, WM_PAINTLINE, 0, 0L);
            dwCurrentLine += iLines;

            return(TRUE);
        }

        return(FALSE);
    }
}


/*
 *  SetNewCalc --
 *
 *  This sets up new information for a drawing and
 *  updates the drawing ID so any calculations in progress will not
 *  be mixed in.
 */

void SetNewCalc(CPOINT cptUL, double dPrec, RECT rc)
{
    // First, translate from upper left to lower left
    cptLL.real = cptUL.real;
    cptLL.imag = cptUL.imag - (dPrec * (rc.bottom - rc.top));

    // Now the precision
    dPrecision = dPrec;

    // The rectangle. Once again, translate.
    rclPicture.xLeft = (long) rc.left;
    rclPicture.xRight = (long) rc.right;
    rclPicture.yBottom = (long) rc.top;
    rclPicture.yTop = (long) rc.bottom;

    // Current line, start of drawing
    dwCurrentLine = rclPicture.xLeft;

    dwThreshold = CalcThreshold(dPrecision);
}


void IncPictureID(void)
{
    cPictureID++;
}


void ResetPictureID(void)
{
    cPictureID = 0;
}


/*
 *  CheckDrawing --
 *
 *  Just a sanity check here -- a function to check to make sure that we're
 *  on the right drawing
 */

BOOL CheckDrawingID(int id)
{
    return((id == cPictureID) ? TRUE : FALSE);
}


/*
 *  TakeDrawBuffer ensures only one pipe read at a time.
 *  LockDrawBuffer locks the handle and returns a pointer.
 *  UnlockDrawBuffer unlocks the handle.
 *  ReturnDrawBuffer lets another pipe read go.
 *  FreeDrawBuffer ensures the allocated buffer is freed upon exit.
 */

static BOOL fBufferTaken = FALSE;
static HANDLE hSharedBuf = (HANDLE) NULL;


BOOL TakeDrawBuffer(void)
{
    if (fBufferTaken) {
        return(FALSE);
    }

    if (hSharedBuf == (HANDLE) NULL) {
        hSharedBuf = LocalAlloc(LMEM_MOVEABLE, MAX_BUFSIZE);
        if (hSharedBuf == (HANDLE) NULL)
            return(FALSE);
    }

    fBufferTaken = TRUE;
    return(TRUE);
}


LPVOID LockDrawBuffer(void)
{
    if (hSharedBuf == (HANDLE) NULL)
        return(NULL);

    return(LocalLock(hSharedBuf));
}


void UnlockDrawBuffer(void)
{
    LocalUnlock(hSharedBuf);
}


void ReturnDrawBuffer(void)
{
    fBufferTaken = FALSE;
}


void FreeDrawBuffer(void)
{
    if (hSharedBuf != (HANDLE) NULL)
        LocalFree(hSharedBuf);
}


/*
 *  CalcThreshold --
 *
 *  We need an iteration threshold beyond which we give up. We want it to
 *  increase the farther we zoom in. This code generates a threshold value
 *  based on the precision of drawing.
 *
 *  RETURNS
 *      threshold calculated based on precision
 */

DWORD CalcThreshold(double precision)
{
    DWORD   thres = 25;
    double  multiplier = (double) 100.0;

    /* for every 100, multiply by 2 */
    while ((precision *= multiplier) < (double)1.0)
        thres *= 2;

    return(thres);
}


/*
 *  QueryThreshold --
 *
 *  Callback for finding out what the current drawing's threshold is.
 */

DWORD QueryThreshold(void)
{
    return(dwThreshold);
}
