/*static char *SCCSID = "@(#)sem_pnt.c	6.12 92/02/19";*/
/*==============================================================*\
 *  SEM_PNT.C - routines for the painting of the main window    *
 *      (C) Copyright IBM Corporation 1992.                     *
 *--------------------------------------------------------------*
 *                                                              *
 *  This module contains the code for the main client window    *
 *  painting                                                    *
 *                                                              *
 *--------------------------------------------------------------*
 *                                                              *
 *  This source file contains the following functions:          *
 *                                                              *
 *      DrawResource(PRECTL, COLOR) - Draws colored square      *
 *      DrawStats(ULONG)  update the statistics for a thread    *
 *      ChangeResource(COLOR) - Changes resource color          *
 *      InitSemaphExample(VOID) - Sets initial colors           *
 *      SetRectPositions(HWND) - Calculate rectangle locations  *
 *      MyOffsetRect(PRECTL, LONG, LONG) - Offsets consumer     *
 *                                         rectangles from      *
 *                                         resource             *
 *      MainPaint(hwnd) - main WM_PAINT processing routine      *
 *                                                              *
\*==============================================================*/

/*--------------------------------------------------------------*\
 *  Include files, macros, defined constants, and externs       *
\*--------------------------------------------------------------*/
#include "semaph.h"

/*--------------------------------------------------------------*\
 *  Global variables                                            *
\*--------------------------------------------------------------*/
/* static variables - only visible within this file */
static LONG  yStatsTop, yStatsBtm, xStatsRight, yBoardTop, yDescGlobal;
SHORT  xWidth;

LONG colors[MAXUSERS]= {
    CLR_WHITE, CLR_BLACK, CLR_BLUE, CLR_RED, CLR_PINK, CLR_GREEN,
    CLR_CYAN, CLR_YELLOW, CLR_DARKGRAY, CLR_DARKBLUE, CLR_DARKRED,
    CLR_DARKPINK, CLR_DARKGREEN, CLR_DARKCYAN, CLR_BROWN, CLR_PALEGRAY};

LONG clrText[MAXUSERS]= {
    CLR_BLACK, CLR_WHITE, CLR_YELLOW, CLR_GREEN, CLR_BLACK, CLR_YELLOW,
    CLR_WHITE, CLR_RED, CLR_WHITE, CLR_BLUE, CLR_RED,
    CLR_PINK, CLR_GREEN, CLR_CYAN, CLR_WHITE, CLR_DARKGRAY};

/****************************************************************\
 * Routine to draw the resource rectangle.                      *
 *--------------------------------------------------------------*
 *                                                              *
 *  Name:     DrawResource(PRECTL, COLOR)                       *
 *                                                              *
 *  Purpose:  Color 1 of the 64 rectangles on the playing board.*
 *                                                              *
 *  Usage:    Called from ThreadConsumer in semaph.c when       *
 *            resource becomes owned by a thread.               *
 *                                                              *
 *  Method:   Get the PS from global hwndMain and  WinFillRect. *
 *            This rountine is included so that no access       *
 *            to the resouce is made outside of paint.c.        *
 *            This is to produce code which is more             *
 *            object orientated.                                *
 *                                                              *
 *  Returns:  none                                              *
 *                                                              *
\****************************************************************/
VOID DrawResource(PRECTL prcl, COLOR color)
{
   HPS hps;

   hps = WinGetPS(hwndMain);
   /* this code is related to the WinFillRect in DrawRects.
    * they should stay in synch with each other.
    */
   WinFillRect(hps,prcl,color);
   WinReleasePS(hps);
   return;
}

/****************************************************************\
 * update the statistics (number of hits) for a thread          *
 *--------------------------------------------------------------*
 *                                                              *
 *  Name:     DrawStats(ULONG)                                  *
 *                                                              *
 *  Purpose:  update statistics display for for a thread        *
 *                                                              *
 *  Usage:    Called from ThreadConsumer in semaph.c when       *
 *            resource becomes owned by a thread.               *
 *                                                              *
 *                                                              *
 *  Method:   Get the PS, set colors, print number of hits for  *
 *            the thread identified by usMyID.                  *
 *            This rountine is included so that no access       *
 *            to the resouce is made outside of paint.c.        *
 *                                                              *
 *  Returns:                                                    *
 *                                                              *
\****************************************************************/
VOID DrawStats(ULONG usMyID)
{
   HPS hps;
   POINTL  ptl;
   THRDATA *pThr = &thrConsumers[usMyID];
   CHAR    szBuf [CNT_SZHITS];

   ptl.x = pThr->rcl.xLeft;
   ptl.y = pThr->rcl.yBottom;
   /* sprintf is non-reentrant, so it is used only when
    * have the mutex.
    */
   sprintf (szBuf, "%lu", pThr->lHits);
   hps = WinGetPS(hwndMain);
   GpiSetBackColor (hps, colors [usMyID]);
   GpiSetColor (hps, clrText [usMyID]);
   GpiCharStringPosAt (hps, &ptl, &pThr->rcl, CHS_CLIP | CHS_OPAQUE,
    strlen (szBuf), szBuf, NULL);
   WinReleasePS(hps);
   return;
}

/****************************************************************\
 *  Routine to paint rectangles in main window.                 *
 *--------------------------------------------------------------*
 *                                                              *
 *  Name:     DrawRects(HPS)                                    *
 *                                                              *
 *  Purpose:  Does painting of rectangles.                      *
 *                                                              *
 *  Usage:    Called from MainPaint when a paint message        *
 *            is received after the semaphore sample has        *
 *            been started.                                     *
 *                                                              *
 *  Method:   Uses WinFillRect API to color all owned squares on*
 *            the square "playing board". Updates stats for     *
 *            every active consumer thread. Prints the static   *
 *            informational text.                               *
 *                                                              *
 *  Returns:  none.                                             *
 *                                                              *
\****************************************************************/
VOID DrawRects(HPS hps)
{
   INT i;
   POINTL  ptl;
                            /* Fill all rectangles in consumer array....
                                as well as rectangle for resource.       */
   if (fSemaphoreWasStarted)
      {
       for ( i = 0; i < MAXRESOURCES; i++ )
       {
           if (aSquares[i].usOwner < MAXUSERS &&
            aSquares[i].usOwner != (USHORT)UNOWNED) {
               /* this line is identical to that in DrawResource.
               * they should stay in synch with each other.
               */
               WinFillRect (hps, &aSquares[i].rcl, colors[aSquares[i].usOwner]);
           }
       }
   }
                        /* print dividing line at bottom of stat display */
   ptl.x = 0;
   ptl.y = yStatsBtm - 2;
   GpiMove (hps, &ptl);
   ptl.x = xWidth;
   GpiLine (hps, &ptl);

   /* print statistics for each user */
   for (i = 0; i < (INT) cNumUsers; i++)
   {
       DrawStats ((USHORT) i);
   }
   return;
}

/****************************************************************\
 * Routine to size and set the positions of rectangle.          *
 *--------------------------------------------------------------*
 *                                                              *
 * Name:    SetRectPositions (xClient, yClient, yChar, yDesc)   *
 *          SHORT   xClient;    width of client window          *
 *          SHORT   yClient;    height of client window         *
 *          SHORT   yChar;      maximum baseline extender       *
 *          SHORT   yDesc;      maximum char descender          *
 *                                                              *
 * Purpose: Given above information about the window, calculate *
 *          the area need for the square game board, the        *
 *          statistics display, and informative text.           *
 *                                                              *
 * Usage:   Called from MainWndProc whenever a size message     *
 *          is received.                                        *
 *                                                              *
 * Method:  Determines space needed for statistics display from *
 *          the font information given as parameters to this    *
 *          procedure.  Then calculates the 18 values which     *
 *          describe a chess by distances from a corner along 2 *
 *          adjacent sides. These are stored in arrays aX and aY*
 *          Given these values, rectangles are calculated for   *
 *          each square on the grid, and stored as part of the  *
 *          information about those squares. Then the size of   *
 *          the rectangles used for the stat display at the top *
 *          of the window are calculated, and stored in the     *
 *          struct describing the thread. Finally, the position *
 *          of the line of informative text which divides the   *
 *          two regions of colored squares is derived.          *
 *                                                              *
 * Returns: none                                                *
 *                                                              *
\****************************************************************/
VOID SetRectPositions(SHORT xClient, SHORT yClient, SHORT yChar, SHORT yDesc)
{
   INT     i;
   INT     dx;
   LONG aX [CNT_POINTS_EDGE];
   LONG aY [CNT_POINTS_EDGE];
   SHORT xScale, yScale;
                                                 /* top of stat display */
   yStatsTop = yClient;
                                              /* bottom of stat display */
   yStatsBtm = yClient - yChar;
                                               /* width of stat display */
   xStatsRight = xClient;

               /* dx is the "scaling factor" for the statistics display */
   dx = (INT) ( (double)xStatsRight / (double)cNumUsers );

                                     /* rectangles for the stat display */
   for (i = 0; i < (INT) cNumUsers; i++)
   {
      thrConsumers[i].rcl.yBottom = yStatsBtm;
      thrConsumers[i].rcl.yTop = yStatsTop;
      thrConsumers[i].rcl.xLeft = i * dx;
      thrConsumers[i].rcl.xRight = (i+1) * dx;
   }
                                              /* consume any unused edge */
   thrConsumers [cNumUsers - 1].rcl.xRight = xClient;
   yClient = yStatsBtm - 2;
          /* the scaling factors determine how large each square will be */
   xScale =  xClient / CNT_SQUARES_EDGE;
   yScale =  yClient / CNT_SQUARES_EDGE;

                                /* the entire game board is described by
                                   the points that are calculated here.  */
   for (i = 0; i < CNT_POINTS_EDGE; i++)
   {
       aX[i] = i * xScale;
       aY[i] = i * yScale;
   }
                                           /* consume any unused portion */
   aX[CNT_SQUARES_EDGE] = aX[CNT_SQUARES_EDGE] + xClient % CNT_SQUARES_EDGE;
   aY[CNT_SQUARES_EDGE] = aY[CNT_SQUARES_EDGE] + yClient % CNT_SQUARES_EDGE - 1;

                                /* now plug the points just calculated into
                                   the rectangles that make up the board */
   for (i = 0; i < MAXRESOURCES; i++)
   {
       aSquares[i].rcl.xLeft = aX [i % CNT_SQUARES_EDGE];
       aSquares[i].rcl.xRight = aX [(i % CNT_SQUARES_EDGE) + 1];
       aSquares[i].rcl.yBottom = aY [i / CNT_SQUARES_EDGE];
       aSquares[i].rcl.yTop = aY [(i / CNT_SQUARES_EDGE) + 1];
   }
                                           /* store the descender for later */
   yDescGlobal = yDesc;
                                  /* store top of board for later reference */
   yBoardTop = aY[CNT_SQUARES_EDGE];
       /* finally, store the width of the client window for later reference */
   xWidth = xClient;
   return;
}

/****************************************************************\
 *   client painting routine                                    *
 *--------------------------------------------------------------*
 *                                                              *
 *  Name:   MainPaint(hwnd)                                     *
 *                                                              *
 *  Purpose: Paints the main client window.                     *
 *                                                              *
 *  Usage:  Routine is called whenver the client window         *
 *          procedure receives a WM_PAINT message               *
 *                                                              *
 *  Method:                                                     *
 *          - begins painting by calling WinBeginPaint          *
 *            and retriving the HPS for the window              *
 *          - performs any painting desired.  Currently the     *
 *            only painting it does is to draw rectangles on    *
 *            the screen if semaphore is started.               *
 *          - ends painting by calling WinEndPaint              *
 *                                                              *
 *  Returns:                                                    *
 *                                                              *
\****************************************************************/
VOID MainPaint( HWND hwnd )
{
   HPS hps=0L;

   hps = WinBeginPaint(hwnd, 0L, 0L);
   GpiErase (hps);
   DrawRects (hps);
   WinEndPaint(hps);
   return;
}                                               /* MainPaint() */
