/*
==============================================================================

  Application:

            Microsoft Windows NT (TM) Performance Monitor

  File:
            status.c - Status window procedure and supporting routines.

            This file contains code creating the status window, which is
            a child of the legend window. The status window shows the 
            time duration of the chart, and the last, avg, min and max
            of the currently-selected chart line.


  Copyright 1992, Microsoft Corporation. All Rights Reserved.
==============================================================================
*/


//==========================================================================//
//                                  Includes                                //
//==========================================================================//


#include <stdio.h>
#include <stdlib.h>   // for mbstowcs


#include "perfmon.h"
#include "valuebar.h"

#include "grafdata.h"      // for CurrentGraphLine
#include "graph.h"
#include "playback.h"      // for PlayingBackLog
#include "legend.h"
#include "utils.h"



//==========================================================================//
//                                  Constants                               //
//==========================================================================//
HDC   hVBarDC ;

#define szGraphStatusClass          TEXT("PerfmonGraphStatusClass")
#define dwGraphStatusClassStyle     (CS_HREDRAW | CS_VREDRAW | CS_OWNDC)
#define iGraphStatusClassExtra      (0)
#define iGraphStatusWindowExtra     (0)
#define dwGraphStatusWindowStyle    (WS_CHILD | WS_VISIBLE) 


#define szStatusValueFormat         TEXT("%10.3f")
#define szStatusMediumnValueFormat  TEXT("%10.0f")
#define szStatusLargeValueFormat    TEXT("%10.4e")
#define eStatusValueTooLarge        ((FLOAT) 1.0E+20)
#define eStatusValueMax             ((FLOAT) 999999.999)
#define eStatusLargeValueMax        ((FLOAT) 9999999999.0)


#define szValueTooHigh              TEXT("+ + + +")
#define szValueTooLow               TEXT("- - - -")

#define StatusLastElt               0
#define StatusAvgElt                1
#define StatusMinElt                2
#define StatusMaxElt                3
#define StatusTimeElt               4

#define StatusNumElts               5

#define StatusDrawAvg(hDC, eValue, bForceRedraw)   \
   DrawStatusValue (hDC, StatusAvgElt, eValue, bForceRedraw)

#define StatusDrawMax(hDC, eValue, bForceRedraw)   \
   DrawStatusValue (hDC, StatusMaxElt, eValue, bForceRedraw)

#define StatusDrawMin(hDC, eValue, bForceRedraw)   \
   DrawStatusValue (hDC, StatusMinElt, eValue, bForceRedraw)

//==========================================================================//
//                                  Typedefs                                //
//==========================================================================//


typedef struct StatusEltStruct
   {
   TCHAR          szText [20] ;
   int            xTextPos ;
   int            xValuePos ;
   FLOAT          eValue ;
   } StatusEltType ;


// This structure represents the "instance data" for a StatusWindow. The
// information is collected in a structure to ease the conversion of this
// code when later adding multiple graph windows. For now, one copy of this
// struct is defined as local data to this file.
typedef struct StatusDataStruct
   {
   StatusEltType  aElts [StatusNumElts] ;
   SIZE           sizeValue ;
   int            yHeight ;
   } StatusDataType ;



//==========================================================================//
//                                Local Data                                //
//==========================================================================//


StatusDataType    StatusData ;


//==========================================================================//
//                                   Macros                                 //
//==========================================================================//


#define StatusTopMargin()     (2)
#define StatusBottomMargin()  (4)

#define StatusLeftMargin()    (3)
#define StatusTextMargin()    (5 + ThreeDPad)
#define StatusValueMargin()   (3 + ThreeDPad)


//==========================================================================//
//                              Local Functions                             //
//==========================================================================//


void DrawStatusValue (HDC hDC,
                      int iEltOffset,
                      FLOAT eValue,
                      BOOL bForceRedraw)
/*
   Called By:     StatusDrawTime, StatusDrawLast, StatusDrawAvg,
                  StatusDrawMin, StatusDrawMax.
*/
   {  // DrawStatusValue
   RECT           rectValue ;
   TCHAR          szValue [20] ;

   if (!bForceRedraw && eValue == StatusData.aElts[iEltOffset].eValue)
      return ;
   StatusData.aElts[iEltOffset].eValue = eValue ;

   rectValue.left = StatusData.aElts[iEltOffset].xValuePos ;
   rectValue.top = StatusTopMargin () + ThreeDPad + 1;
   rectValue.right = rectValue.left + StatusData.sizeValue.cx ;
   rectValue.bottom = rectValue.top + StatusData.sizeValue.cy ;


   if (eValue > eStatusValueMax)
      {
      if (eValue > eStatusValueTooLarge)
         {
         lstrcpy (szValue, szValueTooHigh) ;
         }
      else
         {
         TSPRINTF (szValue,
            (eValue > eStatusLargeValueMax) ? szStatusLargeValueFormat :
            szStatusMediumnValueFormat,
            eValue) ;
         }
      }
   else if (eValue < -eStatusValueMax)
      lstrcpy (szValue, szValueTooLow) ;
   else 
      TSPRINTF (szValue, szStatusValueFormat, eValue) ;

   ExtTextOut (hDC, rectValue.right, rectValue.top, ETO_OPAQUE, &rectValue,
               szValue, lstrlen (szValue), NULL) ;
   }  // DrawStatusValue


//==========================================================================//
//                              Message Handlers                            //
//==========================================================================//


//void static OnCreate (HWND hWnd)
void OnVBarCreate (HWND hWnd)
/*
   Effect:        Perform any actions needed when a status window is created.
                  In particular, set the instance data to initial values,
                  determine the size and placement of the various elements
                  of the status display.

   Called By:     GraphStatusWndProc only, in response to a WM_CREATE message.
*/
   {  // OnCreate
   TCHAR          szValue [20] ;
   HDC            hDC ;
   int            iLen ;
   int            i ;


   hDC = hVBarDC = GetDC (hWnd) ;
   SelectFont (hDC, hFontScales) ;
   SetBkColor (hDC, ColorBtnFace) ;
   SetTextAlign (hDC, TA_RIGHT | TA_TOP) ;

   //=============================//
   // Load Text Labels            //
   //=============================//

   StringLoad (IDS_STATUSLAST, StatusData.aElts[StatusLastElt].szText) ;
   StringLoad (IDS_STATUSAVG, StatusData.aElts[StatusAvgElt].szText) ;
   StringLoad (IDS_STATUSMIN, StatusData.aElts[StatusMinElt].szText) ;
   StringLoad (IDS_STATUSMAX, StatusData.aElts[StatusMaxElt].szText) ;
   StringLoad (IDS_STATUSTIME, StatusData.aElts[StatusTimeElt].szText) ;

   //=============================//
   // Determine Status Height     //
   //=============================//

   StatusData.yHeight = StatusTopMargin () + 
                        StatusBottomMargin () +
                        FontHeight (hDC, TRUE) +
                        2 * ThreeDPad ;

   //=============================//
   // Set position/size of elts   //
   //=============================//

   // Determine the bounding box for each status value by using a max value.
   iLen = TSPRINTF (szValue, szStatusLargeValueFormat, -eStatusValueMax) ;

   GetTextExtentPoint (hDC, szValue, lstrlen(szValue), &StatusData.sizeValue) ;
   for (i = 0 ;
        i < StatusNumElts ;
        i++)
      {  // for
      StatusData.aElts[i].eValue = (FLOAT) 0.0 ;

      if (i)
         StatusData.aElts[i].xTextPos = 
            StatusTextMargin () + 
            StatusData.aElts[i - 1].xValuePos +
            StatusData.sizeValue.cx ;
      else
         StatusData.aElts[i].xTextPos = StatusLeftMargin () ;
      StatusData.aElts[i].xValuePos = StatusData.aElts[i].xTextPos +
                                     StatusValueMargin () +
                                     TextWidth (hDC, StatusData.aElts[i].szText) ;
      }  // for
   }  // OnCreate



void static OnPaint (HWND hWnd)
/*
   Effect:        Paint the invalid surface of hWnd. Draw each label, each
                  recessed value box, and each value.

   Called By:     GraphStatusWndProc only, in response to a WM_PAINT message.
*/
   {
   HDC            hDC ;
   PAINTSTRUCT    ps ;
   RECT           rectClient ;
   int            i ;
   PLINESTRUCT    pLine;

   hDC = BeginPaint (hWnd, &ps) ;
   SetBkMode (hDC, TRANSPARENT) ;

   GetClientRect (hWnd, &rectClient) ;
   HLine (hDC, GetStockObject (BLACK_PEN), 
          rectClient.left, rectClient.right,
          rectClient.bottom - 1) ;

   if ((pGraphs->gOptions.bStatusBarChecked) &&
       (pGraphs->gOptions.bLegendChecked))
      {  // if
      UINT  hPrevTextAlign = SetTextAlign (hDC, TA_LEFT | TA_TOP) ;

      for (i = 0 ;
           i < StatusNumElts ;
           i++)
         {  // for
         // Draw the label
         TextOut (hDC, StatusData.aElts[i].xTextPos, StatusTopMargin () + ThreeDPad,
                  StatusData.aElts[i].szText,
                  lstrlen (StatusData.aElts[i].szText)) ;

         // Draw the recesed value box
         ThreeDConcave1 (hDC,
                        StatusData.aElts[i].xValuePos - ThreeDPad,
                        StatusTopMargin (),
                        StatusData.aElts[i].xValuePos + StatusData.sizeValue.cx + ThreeDPad,
                        StatusTopMargin () + StatusData.sizeValue.cy + 2 * ThreeDPad ) ;
         }  // for

      // restore TextAlign for drawing values
      SetTextAlign (hDC, hPrevTextAlign) ;

      // Draw the values themselves

      pLine = CurrentGraphLine (hWndGraph) ;
      StatusDrawTime (hDC, TRUE) ;

      StatusDrawLast (hDC, pLine, TRUE) ;

      if (pLine)
         {
         StatusDrawAvg (hDC, pLine->lnAveValue, TRUE) ;
         StatusDrawMin (hDC, pLine->lnMinValue, TRUE) ;
         StatusDrawMax (hDC, pLine->lnMaxValue, TRUE) ;
         }
      else
         {
         StatusDrawAvg (hVBarDC,  (FLOAT)0.0, TRUE) ;
         StatusDrawMax (hVBarDC,  (FLOAT)0.0, TRUE) ;
         StatusDrawMin (hVBarDC,  (FLOAT)0.0, TRUE) ;
         }
      }  // if

   EndPaint (hWnd, &ps) ;
   }  // StatusPaint


//==========================================================================//
//                             Exported Functions                           //
//==========================================================================//



void StatusDrawTime (HDC hDC, BOOL bForceRedraw)
/*
   Called By:     StatusTimer, StatusPaint.
*/
   {  // StatusDrawTime
   FLOAT          eTimeSeconds ;

   if (PlayingBackLog ())
      eTimeSeconds = (FLOAT) PlaybackSelectedSeconds () ;
   else
      eTimeSeconds = pGraphs->gOptions.eTimeInterval *
                     (FLOAT) pGraphs->gMaxValues;

   DrawStatusValue (hDC, StatusTimeElt, eTimeSeconds, bForceRedraw) ;
   }  // StatusDrawTime


void StatusDrawLast (HDC hDC, PLINESTRUCT pLine, BOOL bForceRedraw)
/*
   Called By:     StatusTimer, StatusPaint.
*/
   {
   INT            iKnownValue ;
   int            iMaxValues ;
   FLOAT          eValue ;

   if (!pLine)
      eValue = (FLOAT) 0.0 ;
   else
      {
      iKnownValue = pGraphs->gKnownValue ;
      iMaxValues  = pGraphs->gMaxValues ;
      eValue = pLine->lnValues [iKnownValue % iMaxValues] ;
      }

   DrawStatusValue (hDC, StatusLastElt, eValue, bForceRedraw) ;
   }

#if 0
void StatusDrawAvg (HDC hDC, PLINESTRUCT pLine, BOOL bForceRedraw)
/*
   Called By:     StatusTimer, StatusPaint.
*/
   {
   FLOAT          eValue ;

   if (!pLine)
      eValue = (FLOAT) 0.0 ;
   else
      eValue = pLine->lnAveValue ;

   DrawStatusValue (hDC, StatusAvgElt, eValue, bForceRedraw) ;
   }


void StatusDrawMax (HDC hDC, PLINESTRUCT pLine, BOOL bForceRedraw)
/*
   Called By:     StatusTimer, StatusPaint.
*/
   {
   FLOAT          eValue ;

   if (!pLine)
      eValue = (FLOAT) 0.0 ;
   else
      eValue = pLine->lnMaxValue ;

   DrawStatusValue (hDC, StatusMaxElt, eValue, bForceRedraw) ;
   }


void StatusDrawMin (HDC hDC, PLINESTRUCT pLine, BOOL bForceRedraw) 
/*
   Called By:     StatusTimer, StatusPaint.
*/
   {
   FLOAT          eValue ;

   if (!pLine)
      eValue = (FLOAT) 0.0 ;
   else
      eValue = pLine->lnMinValue ;

   DrawStatusValue (hDC, StatusMinElt, eValue, bForceRedraw) ;
   }
#endif


LRESULT APIENTRY GraphStatusWndProc (HWND hWnd,
                                     WORD wMsg,
                                     WPARAM wParam,
                                     LPARAM lParam)
   {  // GraphStatusWndProc
   BOOL           bCallDefProc ;
   LRESULT        lReturnValue ;


   bCallDefProc = FALSE ;
   lReturnValue = 0L ;

   switch (wMsg)
      {  // switch
      case WM_CREATE:
         //OnCreate (hWnd) ;
         OnVBarCreate (hWnd) ;
         break ;


      case WM_PAINT:
         OnPaint (hWnd) ;
         break ;

      case WM_DESTROY:
         ReleaseDC (hWnd, hVBarDC) ;
         break ;

      default:
         bCallDefProc = TRUE ;
      }  // switch


   if (bCallDefProc)
      lReturnValue = DefWindowProc (hWnd, wMsg, wParam, lParam) ;

   return (lReturnValue);
   }  // GraphStatusWndProc


int ValuebarHeight (HWND hWnd)
/*
   Effect:        A status window has a preferred height, based on the font
                  used in its display. Return the preferred height, determined
                  when the window was created.

   Assert:        OnCreate has already been called, and it set 
                  StatusData.yHeight.
*/  
   {
   return (StatusData.yHeight) ;
   }


void StatusTimer (HWND hWnd, BOOL bForceRedraw)
/*
   Effect:        Perform any status-window actions necessary when a timer 
                  tick has been received. In particular, update (redraw)
                  any of the changed values in the status bar.

   Internals:     Each of these called functions compares the value to be
                  displayed with the previous value and doesn't draw if the 
                  values are equal.
*/
   {  // StatusTimer
   PLINESTRUCT pLine;

   

   pLine = CurrentGraphLine (hWndGraph) ;

   StatusDrawLast (hVBarDC, pLine, bForceRedraw) ;

   if (pLine)
      {
      StatusDrawAvg (hVBarDC, pLine->lnAveValue, bForceRedraw) ;
      StatusDrawMin (hVBarDC, pLine->lnMinValue, bForceRedraw) ;
      StatusDrawMax (hVBarDC, pLine->lnMaxValue, bForceRedraw) ;
      }
   else
      {
      StatusDrawAvg (hVBarDC,  (FLOAT)0.0, bForceRedraw) ;
      StatusDrawMax (hVBarDC,  (FLOAT)0.0, bForceRedraw) ;
      StatusDrawMin (hVBarDC,  (FLOAT)0.0, bForceRedraw) ;
      }
   }  // StatusTimer



HWND CreateGraphStatusWindow (HWND hWndGraph)
   {
   return (CreateWindow (szGraphStatusClass,       // class
                         NULL,                     // caption
                         dwGraphStatusWindowStyle, // window style
                         0, 0,                     // position
                         0, 0,                     // size
                         hWndGraph,                // parent window
                         NULL,                     // menu
                         hInstance,               // program instance
                         NULL)) ;                  // user-supplied data
   }




BOOL GraphStatusInitializeApplication (void)
/*
   Called By:     GraphInitializeApplication only
*/
   {
   WNDCLASS       wc ;

   wc.style          = dwGraphStatusClassStyle ;
   wc.lpfnWndProc    = (WNDPROC) GraphStatusWndProc ;
   wc.hInstance      = hInstance ;
   wc.cbClsExtra     = iGraphStatusClassExtra ;
   wc.cbWndExtra     = iGraphStatusWindowExtra ;
   wc.hIcon          = NULL ;
   wc.hCursor        = LoadCursor (NULL, IDC_ARROW) ;
   wc.hbrBackground  = hbLightGray ;
   wc.lpszMenuName   = NULL ;
   wc.lpszClassName  = szGraphStatusClass ;

   return (RegisterClass (&wc)) ;
   }
   
   

