/*****************************************************************************
 *
 *  Report.c - This file contains the report window handler.  Some of the
 *       support routines are in RptFct.c
 *
 *  Microsoft Confidential
 *  Copyright (c) 1992-1996 Microsoft Corporation
 *
 *  Author -
 *
 *       Hon-Wah Chan
 *
 ****************************************************************************/

#include "perfmon.h"
#include <stdio.h>      // for sprintf
#include <string.h>     // for strncpy
#include "report.h"     // Exported declarations for this file

#include "addline.h"    // for AddLine, EditLine
#include "perferr.h"    // for PostError
#include "fileutil.h"   // for FileHandleCreate
#include "line.h"       // for LineAppend
#include "pmemory.h"    // for MemoryXXX (mallloc-type) routines
#include "perfdata.h"   // for UpdateLines
#include "perfmops.h"   // for DoWindowDrag
#include "playback.h"   // for PlaybackLines, PlayingBackLog
#include "system.h"     // for SystemGet
#include "utils.h"
#include "menuids.h"    // for IDM_VIEWREPORT
#include "fileopen.h"   // for FileGetName
#include "counters.h"   // for CounterEntry


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

TCHAR          szSystemFormat [ResourceStringLen] ;
TCHAR          szObjectFormat [ResourceStringLen] ;


//=============================//
// Report Class                //
//=============================//


TCHAR   szReportWindowClass[] = TEXT("PerfRpt") ;
#define dwReportClassStyle           (CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS)
#define iReportClassExtra            (0)
#define iReportWindowExtra           (0)
#define dwReportWindowStyle          (WS_CHILD | WS_VSCROLL | WS_HSCROLL)


#define szValuePlaceholder          TEXT("-999999999.999")
#define szValueLargeHexPlaceholder  TEXT(" xBBBBBBBBDDDDDDDD")

#define szHexFormat                 TEXT("x%08lX")
#define szLargeHexFormat            TEXT("x%08lX%08lX")
#define szLargeValueFormat          TEXT("%12.0f")
#define eStatusLargeValueMax        ((FLOAT) 999999999.0)
#define szValueFormat               TEXT("%12.3f")


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


PREPORT AllocateReportData (HWND hWndReport)
   {
   PREPORT        pReport ;

   pReport = ReportData (hWndReport) ;

   pReport->hWnd = hWndReport ;
   pReport->iStatus = iPMStatusClosed ;
   pReport->bManualRefresh = FALSE ;
   pReport->bModified = FALSE ;

   pReport->Visual.iColorIndex = 0 ;
   pReport->Visual.iWidthIndex = -1 ;
   pReport->Visual.iStyleIndex = -1 ;

   pReport->iIntervalMSecs = iDefaultReportIntervalSecs * 1000 ;
   pReport->pSystemFirst = NULL ;
   pReport->pLineFirst = NULL ;

   pReport->CurrentItemType = REPORT_TYPE_NOTHING ;
   pReport->CurrentItem.pLine = NULL ;

   return (pReport) ;
   }  // AllocateReportData


void FreeReportData (PREPORT pReport)
   {  // FreeReportData
   }  // FreeReportData



BOOL LineCounterRemove (PPLINE ppLineFirst,
                        PLINE pLineRemove)
   {
   PLINE          pLine ;

   if (*ppLineFirst == pLineRemove)
      {
      *ppLineFirst = (*ppLineFirst)->pLineCounterNext ;
      return (TRUE) ;
      }

   for (pLine = *ppLineFirst ;
        pLine->pLineCounterNext ;
        pLine = pLine->pLineCounterNext)
      {   // for
      if (pLine->pLineCounterNext == pLineRemove)
         {
         pLine->pLineCounterNext = pLineRemove->pLineCounterNext ;
         return (TRUE) ;
         }  // if
      }  // for

   return (FALSE) ;
   }  // LineCounterRemove



void DrawCounter (HDC hDC, PREPORT pReport,
                  PCOUNTERGROUP pCounterGroup)
   {  // DrawCounter
     
   RECT  Rect ;


   if (!pCounterGroup->pLineFirst)
      return ;

   SelectFont (hDC, pReport->hFont) ;
   TextOut (hDC, xCounterMargin, pCounterGroup->yLine,
            pCounterGroup->pLineFirst->lnCounterName,
            lstrlen (pCounterGroup->pLineFirst->lnCounterName)) ;

   if (pCounterGroup == pReport->CurrentItem.pCounter)
      {
      ReportCounterRect (pReport, pCounterGroup, &Rect) ;
      DrawFocusRect (hDC, &Rect) ;
      }

   }  // DrawCounter


void DrawObject (HDC hDC, PREPORT pReport,
                 POBJECTGROUP pObjectGroup)
   {  // DrawObject
   TCHAR          szLine [LongTextLen] ;
   PCOUNTERGROUP  pCounterGroup ;
   PCOLUMNGROUP   pColumnGroup ;

   if (!pObjectGroup->pCounterGroupFirst)
      {
      return ;
      }

   SelectFont (hDC, pReport->hFontHeaders) ;

   SetTextAlign (hDC, TA_RIGHT) ;

   for (pColumnGroup = pObjectGroup->pColumnGroupFirst ;
        pColumnGroup ;
        pColumnGroup = pColumnGroup->pColumnGroupNext)
      {  // for
      // Draw Parent
      if (pColumnGroup->lpszParentName)
         TextOut (hDC, 
                  ValueMargin (pReport) + 
                  pColumnGroup->xPos + pColumnGroup->xWidth,
                  pObjectGroup->yFirstLine - pReport->yLineHeight,
                  pColumnGroup->lpszParentName,
                  lstrlen (pColumnGroup->lpszParentName)) ;

      // Draw Instance
      if (pColumnGroup->lpszInstanceName)
         {
         TextOut (hDC, 
                  ValueMargin (pReport) + 
                  pColumnGroup->xPos + pColumnGroup->xWidth,
                  pObjectGroup->yFirstLine,
                  pColumnGroup->lpszInstanceName,
                  lstrlen (pColumnGroup->lpszInstanceName)) ;
         }

      if (pColumnGroup == pReport->CurrentItem.pColumn)
         {
         RECT  Rect ;

         ReportColumnRect (pReport, pColumnGroup, &Rect) ;
         DrawFocusRect (hDC, &Rect) ;
         }

      }  // for
   SetTextAlign (hDC, TA_LEFT) ;

   TSPRINTF (szLine, szObjectFormat, pObjectGroup->lpszObjectName) ;
   TextOut (hDC, 
            xObjectMargin, pObjectGroup->yFirstLine,
            szLine, lstrlen (szLine)) ;

   if (pObjectGroup == pReport->CurrentItem.pObject)
      {
      RECT  Rect ;

      ReportObjectRect (pReport, pObjectGroup, &Rect) ;
      DrawFocusRect (hDC, &Rect) ;
      }

   SelectFont (hDC, pReport->hFont) ;

   for (pCounterGroup = pObjectGroup->pCounterGroupFirst ;
        pCounterGroup ;
        pCounterGroup = pCounterGroup->pCounterGroupNext)
      {  // for
      DrawCounter (hDC, pReport, pCounterGroup) ;
      }  // for

   }  // DrawObject


void DrawSystem (HDC hDC, PREPORT pReport, PSYSTEMGROUP pSystemGroup)
   {  // DrawSystem
   TCHAR          szLine [LongTextLen] ;
   POBJECTGROUP   pObjectGroup ;

   SelectFont (hDC, pReport->hFontHeaders) ;

   if (!pSystemGroup->pObjectGroupFirst)
      return ;

   SetTextAlign (hDC, TA_LEFT) ;
   TSPRINTF (szLine, szSystemFormat, pSystemGroup->lpszSystemName) ;
   TextOut (hDC, 
            xSystemMargin, pSystemGroup->yFirstLine,
            szLine, lstrlen (szLine)) ;

   if (pSystemGroup == pReport->CurrentItem.pSystem)
      {
      RECT  Rect ;

      ReportSystemRect (pReport, pSystemGroup, &Rect) ;
      DrawFocusRect (hDC, &Rect) ;
      }

   for (pObjectGroup = pSystemGroup->pObjectGroupFirst ;
        pObjectGroup ;
        pObjectGroup = pObjectGroup->pObjectGroupNext)
      {  // for
      DrawObject (hDC, pReport, pObjectGroup) ;
      }  // for
   }  // DrawSystem

void DrawReportValue (HDC hDC, PREPORT pReport, PLINE pLine)
   {  // DrawReportValue
   TCHAR          szValue [20] ;
   FLOAT          eValue ;
   RECT           rectValue ;

   // skip until we have collect enough samples for the first data
   if (pLine->bFirstTime == 0)
      {
      eValue = CounterEntry (pLine) ;
      if (pLine->lnCounterType == PERF_COUNTER_RAWCOUNT_HEX ||
          pLine->lnCounterType == PERF_COUNTER_LARGE_RAWCOUNT_HEX)
         {
         DWORD LowValue  ;
         DWORD HighValue ;

         LowValue  = (DWORD) (pLine->lnaCounterValue[0]) ;
         HighValue = (DWORD) (pLine->lnaCounterValue[0] >> 32);

         if (pLine->lnCounterType == PERF_COUNTER_RAWCOUNT_HEX ||
             HighValue == 0)
            {
            TSPRINTF (szValue,
                      szHexFormat,
                      LowValue) ;
            }
         else
            {

            TSPRINTF (szValue,
                      szLargeHexFormat,
                      HighValue,
                      LowValue) ;
            }
         }
      else
         {
         TSPRINTF (szValue,
                   (eValue > eStatusLargeValueMax) ?
                       szLargeValueFormat : szValueFormat,
                   eValue) ;
         ConvertDecimalPoint (szValue) ;
         }
      }
   else
      {
      // draw "- - - -"
      lstrcpy(szValue, DashLine);
      }

   ReportLineValueRect (pReport, pLine, &rectValue) ;
   
   ExtTextOut (hDC, 
               rectValue.right - 2, rectValue.top,
               ETO_CLIPPED | ETO_OPAQUE,
               &rectValue,
               szValue, lstrlen (szValue), NULL) ;

   if (pReport->CurrentItemType == REPORT_TYPE_LINE &&
       pLine == pReport->CurrentItem.pLine)
      {
      DrawFocusRect (hDC, &rectValue) ;
      }
   }  // DrawReportValue


void DrawReportValues (HDC hDC, PREPORT pReport)
   {
   PSYSTEMGROUP   pSystemGroup ;
   POBJECTGROUP   pObjectGroup ;
   PCOUNTERGROUP  pCounterGroup ;
   PLINE          pLine ;

   SelectFont (hDC, pReport->hFont) ;
   SetTextAlign (hDC, TA_RIGHT) ;

   for (pSystemGroup = pReport->pSystemGroupFirst ;
        pSystemGroup ;
        pSystemGroup = pSystemGroup->pSystemGroupNext)
      {  // for System...
      for (pObjectGroup = pSystemGroup->pObjectGroupFirst ;
           pObjectGroup ;
           pObjectGroup = pObjectGroup->pObjectGroupNext)
         {  // for Object...
         for (pCounterGroup = pObjectGroup->pCounterGroupFirst ;
              pCounterGroup ;
              pCounterGroup = pCounterGroup->pCounterGroupNext)
            {  // for Counter...
            for (pLine = pCounterGroup->pLineFirst ;
                 pLine ;
                 pLine = pLine->pLineCounterNext)
               {  // for Line...
               DrawReportValue (hDC, pReport, pLine) ;
               }  // for Line...
            }  // for Counter...
         }  // for Object      
      }  // for System...

   }


void DrawReportHeaders (HDC hDC, PREPORT pReport)
   {  // DrawReportHeaders
   PSYSTEMGROUP   pSystemGroup ;

   for (pSystemGroup = pReport->pSystemGroupFirst ;
        pSystemGroup ;
        pSystemGroup = pSystemGroup->pSystemGroupNext)
      {  // for
      DrawSystem (hDC, pReport, pSystemGroup) ;
      }  // for         
   }  // DrawReportHeaders


void DrawReport (HDC hDC, PREPORT pReport)
   {
   SetBkColor (hDC, GetSysColor(COLOR_WINDOW)) ;
   DrawReportHeaders (hDC, pReport) ;
   //UpdateLines (&(pReport->pSystemFirst), pReport->pLineFirst) ;
   DrawReportValues (hDC, pReport) ;
   }



void SetLinePosition (HDC hDC,
                      PREPORT pReport, 
                      POBJECTGROUP pObjectGroup,
                      PLINE pLine)
   {  // SetLinePositions
   PCOLUMNGROUP   pColumnGroup ;

   pColumnGroup = GetColumnGroup (pReport, pObjectGroup, pLine) ;
   if (!pColumnGroup)
      {
      pLine->xReportPos = 0 ;
      pLine->iReportColumn = -1 ;
      }
   else
      {
      pLine->xReportPos = pColumnGroup->xPos ;
      pLine->iReportColumn = pColumnGroup->ColumnNumber ;
      }
   }  // SetLinePosition


void SetCounterPositions (HDC hDC,
                          PREPORT pReport,
                          POBJECTGROUP pObjectGroup,
                          PCOUNTERGROUP pCounterGroup,
                          int yLine)
   {  // SetCounterPositions
   PLINE          pLine ;
   int            yPos ;


   if (!pCounterGroup->pLineFirst)
      return ;

   yPos = pCounterGroup->yLine ;

   SelectFont (hDC, pReport->hFontHeaders) ;

   for (pLine = pCounterGroup->pLineFirst ;
        pLine ;
        pLine = pLine->pLineCounterNext)
      {  // for
      SetLinePosition (hDC, pReport, pObjectGroup, pLine) ;
      pLine->yReportPos = yPos ;
      }  // for
   }  // SetCounterPositions


void SetColumnPositions (HDC hDC,
                         PREPORT pReport,
                         POBJECTGROUP pObjectGroup)
   {  // SetColumnPositions
   int            xPos ;
   PCOLUMNGROUP   pColumnGroup ;

   xPos = 0 ;
   for (pColumnGroup = pObjectGroup->pColumnGroupFirst ;
        pColumnGroup ;
        pColumnGroup = pColumnGroup->pColumnGroupNext)
      {  // for
      pColumnGroup->xWidth = max (max (pColumnGroup->ParentNameTextWidth,
                                       pColumnGroup->InstanceNameTextWidth),
                                  pReport->xValueWidth) ;
      pColumnGroup->xPos = xPos ;
      pColumnGroup->yFirstLine = pObjectGroup->yFirstLine ;
      xPos += (pColumnGroup->xWidth + xColumnMargin) ;
      }  // for
   }  // SetColumnPositions


void SetObjectPositions (HDC hDC,
                         PREPORT pReport,
                         POBJECTGROUP pObjectGroup,
                         int yLine)
/*
   Effect:        Determine and set the logical coordinates for the
                  object pObject within the report pReport.

                  For each instance x counter, determine the appropriate
                  column, adding a column description to the object if
                  needed.

   Called By:     SetSystemPositions only.

   See Also:      SetSystemPositions, SetCounterPositions, ColumnGroup.
*/
   {  // SetObjectPositions
   PCOUNTERGROUP  pCounterGroup ;
   int            yPos ;
   PLINE          pLine ;

   // check if there is parnet name for this object type
   // if so, need to add extra space for the parent name
   if (pObjectGroup->pCounterGroupFirst)
      {
      pCounterGroup = pObjectGroup->pCounterGroupFirst ;
      pLine = pCounterGroup->pLineFirst ;
      if (pLine && LineParentName(pLine))
         {
         pObjectGroup->yFirstLine += yLine ;
         }
      }

   SetColumnPositions (hDC, pReport, pObjectGroup) ;

   yPos = pObjectGroup->yFirstLine + yLine ;

   for (pCounterGroup = pObjectGroup->pCounterGroupFirst ;
        pCounterGroup ;
        pCounterGroup = pCounterGroup->pCounterGroupNext)
      {  // for
      pCounterGroup->yLine = yPos + yLine ;
   
      SetCounterPositions (hDC, pReport, pObjectGroup, pCounterGroup, yLine) ;

      yPos = pCounterGroup->yLine ;
      }  // for

   pObjectGroup->yLastLine = yPos + yLine ;
   }  // SetObjectPositions


void SetSystemPositions (HDC hDC,
                         PREPORT pReport, 
                         PSYSTEMGROUP pSystemGroup,
                         int yLine)
   {  // SetSystemPositions
   POBJECTGROUP   pObjectGroup ;
   int            yPos ;

   yPos = pSystemGroup->yFirstLine ;

   for (pObjectGroup = pSystemGroup->pObjectGroupFirst ;
        pObjectGroup ;
        pObjectGroup = pObjectGroup->pObjectGroupNext)
      {  // for
      pObjectGroup->yFirstLine = yPos + yLine ;
   
      SetObjectPositions (hDC, pReport, pObjectGroup, yLine) ;

      yPos = pObjectGroup->yLastLine ;
      }  // for

   pSystemGroup->yLastLine = yPos + yLine ;
   }  // SetSystemPositions


void static SetScrollRanges (HWND hWnd)
   {  // SetScrollRanges
   PREPORT        pReport ;
   RECT           rectClient ;
   int            xWidth, yHeight ;

   GetClientRect (hWnd, &rectClient) ;
   xWidth = rectClient.right - rectClient.left ;
   yHeight = rectClient.bottom - rectClient.top ;

   pReport = ReportData (hWnd) ;

   SetScrollRange (hWnd, SB_VERT, 
                   0, max (0, pReport->yHeight - yHeight), 
                   TRUE) ;
   SetScrollRange (hWnd, SB_HORZ,
                   0, max (0, pReport->xWidth - xWidth),
                   TRUE) ;
   }  // SetScrollRanges

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


void static OnCreate (HWND hWnd)
   {  // OnCreate
   HDC            hDC ;
   PREPORT        pReport ;

   pReport = AllocateReportData (hWnd) ;
   if (!pReport)
      return ;

   pReport->hFont = hFontScales ;
   pReport->hFontHeaders = hFontScalesBold ;

   pReport->pLineFirst = NULL ;
   pReport->pSystemFirst = NULL ;

   pReport->pSystemGroupFirst = NULL ;

   hDC = GetDC (hWnd) ;

   SelectFont (hDC, pReport->hFont) ;


   pReport->yLineHeight = FontHeight (hDC, TRUE) ;
   pReport->xValueWidth = TextWidth (hDC, szValuePlaceholder) ;
   ReleaseDC (hWnd, hDC) ;

   pReport->xWidth = 0 ;
   pReport->yHeight = 0 ;

   StringLoad (IDS_SYSTEMFORMAT, szSystemFormat) ;
   StringLoad (IDS_OBJECTFORMAT, szObjectFormat) ;
   }  // OnCreate


void static OnPaint (HWND hWnd)
   {
   HDC            hDC ;
   PAINTSTRUCT    ps ;
   PREPORT        pReport ;



   pReport = ReportData (hWnd) ;

   hDC = BeginPaint (hWnd, &ps) ;
   //hDC = hReportDC ;
   SetWindowOrgEx (hDC, 
                   GetScrollPos (hWnd, SB_HORZ), 
                   GetScrollPos (hWnd, SB_VERT),
                   NULL) ;

   DrawReport (hDC, pReport) ;

   EndPaint (hWnd, &ps) ;
   }


void static UpdateReportValues (PREPORT pReport)
/*
   Effect:        Redraw all the visible report values of pReport.
                  Since drawing the values completely covers any 
                  previous values, there is no need to erase (or flicker)
                  between updating values.

   Called By:     ReportTimer, OnVScroll, OnHScroll.
*/
   {  // UpdateReportValues
   HDC            hDC ;

   hDC = GetDC (pReport->hWnd) ;

   SetBkColor (hDC, GetSysColor(COLOR_WINDOW)) ;
   SetWindowOrgEx (hDC, 
                   GetScrollPos (pReport->hWnd, SB_HORZ), 
                   GetScrollPos (pReport->hWnd, SB_VERT),
                   NULL) ;

   DrawReportValues (hDC, pReport) ;
   ReleaseDC (pReport->hWnd, hDC) ;

   }  // UpdateReportValues



void static OnHScroll (HWND hWnd,
                       int iScrollCode,
                       int iScrollNewPos)
   {  // OnHScroll
   PREPORT        pReport ;
   int            iScrollAmt, iScrollPos, iScrollRange ;
   int            iScrollLo ;
   RECT           rectClient ;
   int            xWidth ;

   pReport = ReportData (hWnd) ;

   GetClientRect (hWnd, &rectClient) ;
   xWidth = rectClient.right - rectClient.left ;

   if (pReport->xWidth <= xWidth)
      {
      // no horz scroll bar, forget it
      return ;
      }

   iScrollPos = GetScrollPos (hWnd, SB_HORZ) ;

   GetScrollRange (hWnd, SB_HORZ, &iScrollLo, &iScrollRange) ;


   switch (iScrollCode)
      {
      case SB_LINEUP:
           iScrollAmt = - Report.yLineHeight ;
           break ;

      case SB_LINEDOWN:
           iScrollAmt = Report.yLineHeight ;
           break ;

      case SB_PAGEUP:
           iScrollAmt = - (rectClient.right - rectClient.left) / 2 ;
           break ;

      case SB_PAGEDOWN:
           iScrollAmt = (rectClient.right - rectClient.left) / 2 ;
           break ;

      case SB_THUMBPOSITION:
           iScrollAmt = iScrollNewPos - iScrollPos ;
           break ;

      default:
           iScrollAmt = 0 ;
      }  // switch

     iScrollAmt = PinInclusive (iScrollAmt,
                                -iScrollPos,
                                iScrollRange - iScrollPos) ;
     if (iScrollAmt)
        {
        iScrollPos += iScrollAmt ;
        ScrollWindow (hWnd, -iScrollAmt, 0, NULL, NULL) ;
        SetScrollPos (hWnd, SB_HORZ, iScrollPos, TRUE) ;
        UpdateWindow (hWnd) ;
#if 0
        UpdateReportValues (pReport) ;
#endif
        }
   }  // OnHScroll



void static OnVScroll (HWND hWnd,
                       int iScrollCode,
                       int iScrollNewPos)
   {  // OnVScroll
   PREPORT        pReport ;
   int            iScrollAmt, iScrollPos, iScrollRange ;
   int            iScrollLo ;
   RECT           rectClient ;

   pReport = ReportData (hWnd) ;

   iScrollPos = GetScrollPos (hWnd, SB_VERT) ;
   GetScrollRange (hWnd, SB_VERT, &iScrollLo, &iScrollRange) ;

   GetClientRect (hWnd, &rectClient) ;

   switch (iScrollCode)
      {
      case SB_LINEUP:
           iScrollAmt = - Report.yLineHeight ;
           break ;

      case SB_LINEDOWN:
           iScrollAmt = Report.yLineHeight ;
           break ;

      case SB_PAGEUP:
           iScrollAmt = - (rectClient.bottom - rectClient.top) / 2 ;
           break ;

      case SB_PAGEDOWN:
           iScrollAmt = (rectClient.bottom - rectClient.top) / 2 ;
           break ;

      case SB_THUMBPOSITION:
           iScrollAmt = iScrollNewPos - iScrollPos ;
           break ;

      default:
           iScrollAmt = 0 ;
      }  // switch

     iScrollAmt = PinInclusive (iScrollAmt,
                                -iScrollPos,
                                iScrollRange - iScrollPos) ;
     if (iScrollAmt)
        {
        iScrollPos += iScrollAmt ;
        ScrollWindow (hWnd, 0, -iScrollAmt, NULL, NULL) ;
        SetScrollPos (hWnd, SB_VERT, iScrollPos, TRUE) ;

//        WindowInvalidate (hWnd) ;
        UpdateWindow (hWnd) ;
#if 0
        UpdateReportValues (pReport) ;        
#endif
        }
   }  // OnVScroll

void static OnKeyDown (HWND hWnd, DWORD wParam)
   {
   switch (wParam)
      {
      case VK_UP:
         OnVScroll (hWnd, SB_LINEUP, 0) ;
         break ;

      case VK_DOWN:
         OnVScroll (hWnd, SB_LINEDOWN, 0) ;
         break ;

      case VK_LEFT:
         OnHScroll (hWnd, SB_LINEUP, 0) ;
         break ;

      case VK_RIGHT:
         OnHScroll (hWnd, SB_LINEDOWN, 0) ;
         break ;

      case VK_PRIOR:
         OnVScroll (hWnd, SB_PAGEUP, 0) ;
         break ;

      case VK_NEXT:
         OnVScroll (hWnd, SB_PAGEDOWN, 0) ;
         break ;
      }
   }  // OnKeyDown
       
         
LRESULT APIENTRY ReportWndProc (HWND hWnd,
                               WORD wMsg,
                               DWORD wParam,
                               LONG lParam)
   {  // ReportWndProc
   BOOL           bCallDefProc ;
   LRESULT        lReturnValue ;

   bCallDefProc = FALSE ;
   lReturnValue = 0L ;

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

      case WM_LBUTTONDOWN:

         if (!OnReportLButtonDown (hWnd, LOWORD (lParam), HIWORD (lParam)))
            {
            // mouse click do not hit on any entries, see if we 
            // need to drag Perfmon
            if (!(Options.bMenubar))
               {
               DoWindowDrag (hWnd, lParam) ;
               }
            }
         break ;

      case WM_LBUTTONDBLCLK:
         SendMessage (hWndMain, WM_LBUTTONDBLCLK, wParam, lParam) ;
         break ;

      case WM_PAINT:
         OnPaint (hWnd) ;
         break ;

      case WM_SIZE:
         SetScrollRanges (hWnd) ;
         break ;

      case WM_HSCROLL:
         OnHScroll (hWnd, LOWORD (wParam), HIWORD (wParam)) ;
         break ;

      case WM_VSCROLL:
         OnVScroll (hWnd, LOWORD (wParam), HIWORD (wParam)) ;
         break ;

      case WM_TIMER:
         ReportTimer (hWnd, FALSE) ;
         break ;

      case WM_KEYDOWN:
         OnKeyDown (hWnd, wParam) ;
         break ;

      case WM_DESTROY:
         KillTimer (hWnd, ReportTimerID) ;
         break ;

      default:
         bCallDefProc = TRUE ;
      }  // switch


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

   return (lReturnValue);
   }  // ReportWndProc



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

#if 0
PREPORT ReportData (HWND hWndReport)
   {
   return (&Report) ;
   }
#endif

void SetReportTimer (PREPORT pReport)
   {  
   if (pReport->iStatus == iPMStatusCollecting)
      KillTimer (pReport->hWnd, ReportTimerID) ;

   SetTimer (pReport->hWnd, ReportTimerID, 
             pReport->iIntervalMSecs , NULL) ;
   pReport->iStatus = iPMStatusCollecting ;
   }


void ClearReportTimer (PREPORT pReport)
   {
   pReport->iStatus = iPMStatusClosed ;
   KillTimer (pReport->hWnd, ReportTimerID) ;
   }


BOOL ReportInitializeApplication (void)
   {  // ReportInitializeApplication
   BOOL           bSuccess ;
   WNDCLASS       wc ;

   //=============================//
   // Register ReportWindow class  //
   //=============================//


   wc.style         = dwReportClassStyle ;
   wc.lpfnWndProc   = (WNDPROC) ReportWndProc ;
   wc.hInstance     = hInstance ;
   wc.cbClsExtra    = iReportWindowExtra ;
   wc.cbWndExtra    = iReportClassExtra ;
   wc.hIcon         = NULL ;
   wc.hCursor       = LoadCursor(NULL, IDC_ARROW) ;
//   wc.hbrBackground = GetStockObject (WHITE_BRUSH) ;
   wc.hbrBackground =   (HBRUSH) (COLOR_WINDOW + 1) ;
   wc.lpszMenuName  = NULL ;
   wc.lpszClassName = szReportWindowClass ;

   bSuccess = RegisterClass (&wc) ;


   //=============================//
   // Register Child classes      //
   //=============================//

   return (bSuccess) ;
   }  // ReportInitializeApplication





HWND CreateReportWindow (HWND hWndParent)
/*
   Effect:        Create the graph window. This window is a child of 
                  hWndMain and is a container for the graph data,
                  graph label, graph legend, and graph status windows.

   Note:          We dont worry about the size here, as this window
                  will be resized whenever the main window is resized.

*/
   {
   return (CreateWindow (szReportWindowClass,       // window class
                         NULL,                     // caption
                         dwReportWindowStyle,       // style for window
                         0, 0,                     // initial position
                         0, 0,                     // initial size
                         hWndParent,               // parent
                         NULL,                     // menu
                         hInstance,               // program instance
                         NULL)) ;                  // user-supplied data                                              
   }  // CreateReportWindow



void SetReportPositions (HDC hDC,
                         PREPORT pReport)
   {
   PSYSTEMGROUP   pSystemGroup ;
   int            yLine ;
   int            yPos ;

  
//   pReport->xMaxCounterWidth = 0 ;

   yLine = pReport->yLineHeight ;
   yPos = 2 * yLine ;

   for (pSystemGroup = pReport->pSystemGroupFirst ;
        pSystemGroup ;
        pSystemGroup = pSystemGroup->pSystemGroupNext)
      {  // for
      pSystemGroup->yFirstLine = yPos + yLine ;

      SetSystemPositions (hDC, pReport, pSystemGroup, yLine) ;

      yPos = pSystemGroup->yLastLine ;
      }  // for 

   pReport->yHeight = yPos ;

   SetScrollRanges (pReport->hWnd) ;
   }  // SetReportPositions



void PlaybackReport (HWND hWndReport)
   {  // PlaybackReport
   PREPORT        pReport ;

   pReport = ReportData (hWndReport) ;

   PlaybackLines (pReport->pSystemFirst, 
                  pReport->pLineFirst, 
                  PlaybackLog.StartIndexPos.iPosition) ;
   PlaybackLines (pReport->pSystemFirst, 
                  pReport->pLineFirst, 
                  PlaybackLog.StopIndexPos.iPosition) ;
   }  // PlaybackReport


BOOL CurrentReportItem (HWND hWndReport)
   {  // CurrentReportItem
   PREPORT        pReport ;

   pReport = ReportData (hWndReport) ;
   if (!pReport)
      return (FALSE) ;

   return (pReport->CurrentItemType != REPORT_TYPE_NOTHING) ;
   }  // CurrentReportItem



BOOL AddReport (HWND hWndParent)
   {
   PREPORT        pReport ;
   LPTSTR         pCurrentSystem ;
   POBJECTGROUP  pParentObject ;

   pReport = ReportData (hWndReport) ;

   if (pReport->CurrentItemType == REPORT_TYPE_LINE)
      {
      pCurrentSystem = pReport->CurrentItem.pLine->lnSystemName ;
      }
   else if (pReport->CurrentItemType == REPORT_TYPE_SYSTEM)
      {
      pCurrentSystem = pReport->CurrentItem.pSystem->lpszSystemName ;
      }
   else if (pReport->CurrentItemType == REPORT_TYPE_OBJECT)
      {
      pCurrentSystem = pReport->CurrentItem.pObject->pParentSystem->lpszSystemName ;
      }
   else if (pReport->CurrentItemType == REPORT_TYPE_COLUMN)
      {
      pParentObject =  pReport->CurrentItem.pColumn->pParentObject ;
      pCurrentSystem =  pParentObject->pParentSystem->lpszSystemName ;
      }
   else if (pReport->CurrentItemType == REPORT_TYPE_COUNTER)
      {
      pParentObject =  pReport->CurrentItem.pCounter->pParentObject ;
      pCurrentSystem =  pParentObject->pParentSystem->lpszSystemName ;
      }
   else
      {
      pCurrentSystem = NULL ;
      }


   return (AddLine (hWndParent, 
                    &(pReport->pSystemFirst), 
                    &(pReport->Visual),
                    pCurrentSystem,
                    LineTypeReport)) ;
   }


BOOL ToggleReportRefresh (HWND hWnd)
   {  // ToggleReportRefresh
   PREPORT        pReport ;

   pReport = ReportData (hWnd) ;

   if (pReport->bManualRefresh)
      SetReportTimer (pReport) ;
   else
      ClearReportTimer (pReport) ;

   pReport->bManualRefresh = !pReport->bManualRefresh ;
   return (pReport->bManualRefresh) ;
   }  // ToggleReportRefresh

BOOL ReportRefresh (HWND hWnd)
   {  // ReportRefresh
   PREPORT        pReport ;

   pReport = ReportData (hWnd) ;

   return (pReport->bManualRefresh) ;
   }  // ReportRefresh



void ReportTimer (HWND hWnd, BOOL bForce)
   {
   PREPORT        pReport ;

   pReport = ReportData (hWnd) ;

   if (PlayingBackLog () || !pReport)
      {
      return;
      }

   if (bForce || !pReport->bManualRefresh)
      {  // if
      UpdateLines (&(pReport->pSystemFirst), pReport->pLineFirst) ;
      if (iPerfmonView == IDM_VIEWREPORT && !bPerfmonIconic)
         {
         // only need to draw the data when we are viewing it...
         UpdateReportValues (pReport) ;
         }
      }  // if
   }  // ReportTimer

BOOL SaveReport (HWND hWndReport, HANDLE hInputFile, BOOL bGetFileName)
   {
   PREPORT        pReport ;
   PLINE          pLine ;
   HANDLE         hFile ;
   DISKREPORT     DiskReport ;
   PERFFILEHEADER FileHeader ;
   TCHAR          szFileName [256] ;
   BOOL           newFileName = FALSE ;

   pReport = ReportData (hWndReport) ;
   if (!pReport)
      {
      return (FALSE) ;
      }

   if (hInputFile)
      {
      // use the input file handle if it is available
      // this is the case for saving workspace data
      hFile = hInputFile ;
      }
   else
      {
      if (pReportFullFileName)
         {
         lstrcpy (szFileName, pReportFullFileName) ;
         }
      if (bGetFileName || pReportFullFileName == NULL)
         {
         if (!FileGetName (hWndReport, IDS_REPORTFILE, szFileName))
            {
            return (FALSE) ;
            }
         newFileName = TRUE ;
         }

      hFile = FileHandleCreate (szFileName) ;

      if (hFile && newFileName)
         {
         ChangeSaveFileName (szFileName, IDM_VIEWREPORT) ;
         }
      else if (!hFile)
         {
         DlgErrorBox (hWndReport, ERR_CANT_OPEN, szFileName) ;
         }
      }


   if (!hFile)
      return (FALSE) ;


   if (!hInputFile)
      {
      memset (&FileHeader, 0, sizeof (FileHeader)) ;
      lstrcpy (FileHeader.szSignature, szPerfReportSignature) ;
      FileHeader.dwMajorVersion = ReportMajorVersion ;
      FileHeader.dwMinorVersion = ReportMinorVersion ;
   
      if (!FileWrite (hFile, &FileHeader, sizeof (PERFFILEHEADER)))
         {
         goto Exit0 ;
         }
      }

   DiskReport.Visual = pReport->Visual ;
   DiskReport.bManualRefresh = pReport->bManualRefresh ;
   DiskReport.dwIntervalSecs = pReport->iIntervalMSecs ;
   DiskReport.dwNumLines = NumLines (pReport->pLineFirst) ;
   DiskReport.perfmonOptions = Options ;

   if (!FileWrite (hFile, &DiskReport, sizeof (DISKREPORT)))
      {
      goto Exit0 ;
      }

   for (pLine = pReport->pLineFirst ;
        pLine ;
        pLine = pLine->pLineNext)
      {  // for
      if (!WriteLine (pLine, hFile))
         {
         goto Exit0 ;
         }
      }  // for

   if (!hInputFile)
      {
      CloseHandle (hFile) ;
      }

   return (TRUE) ;

Exit0:
   if (!hInputFile)
      {
      CloseHandle (hFile) ;

      // only need to report error if not workspace 
      DlgErrorBox (hWndReport, ERR_SETTING_FILE, szFileName) ;
      }
   return (FALSE) ;
   }  // SaveReport  


void ReportAddAction (PREPORT pReport)
   {
   HDC      hDC ;

   //=============================//
   // Calculate report positions  //
   //=============================//

   hDC = GetDC (hWndReport) ;
   SetReportPositions (hDC, pReport) ;
   ReleaseDC (hWndReport, hDC) ;
   
   if (PlayingBackLog ())
      {
      PlaybackReport (hWndReport) ;
      }
   else if (pReport->iStatus == iPMStatusClosed)
      {
      SetReportTimer (pReport) ;
      }

   WindowInvalidate (hWndReport) ;
   }


BOOL OpenReportVer1 (HANDLE hFile,
                     DISKREPORT *pDiskReport,
                     PREPORT pReport,
                     DWORD dwMinorVersion)
   {
   HDC   hDC ;

   pReport->Visual = pDiskReport->Visual ;
   pReport->iIntervalMSecs = pDiskReport->dwIntervalSecs ;
   if (dwMinorVersion < 3)
      {
      // convert this to msec
      pReport->iIntervalMSecs *= 1000 ;
      }
   pReport->bManualRefresh = pDiskReport->bManualRefresh ;

   bDelayAddAction = TRUE ;
   ReadLines (hFile, pDiskReport->dwNumLines,
             &(pReport->pSystemFirst), &(pReport->pLineFirst), IDM_VIEWREPORT) ;

   if (pReport->pLineFirst)
      {
      // set focus on the first line
      pReport->CurrentItem.pLine = pReport->pLineFirst ;
      pReport->CurrentItemType = REPORT_TYPE_LINE ;
      }
   bDelayAddAction = FALSE ;

   //=============================//
   // Calculate report positions  //
   //=============================//

   hDC = GetDC (hWndReport) ;
   SetReportPositions (hDC, pReport) ;
   ReleaseDC (hWndReport, hDC) ;

   if (PlayingBackLog ())

      {
      PlaybackReport (hWndReport) ;
      }
   else if (pReport->iStatus == iPMStatusClosed)
      {
      SetReportTimer (pReport) ;
      }

   WindowInvalidate (hWndReport) ;

   return (TRUE) ;
   }  // OpenReportVer1



BOOL OpenReport (HWND hWndReport, 
                 HANDLE hFile, 
                 DWORD dwMajorVersion,
                 DWORD dwMinorVersion,
                 BOOL bReportFile)
   {
   PREPORT        pReport ;
   DISKREPORT     DiskReport ;
   BOOL           bSuccess = TRUE ;

   pReport = ReportData (hWndReport) ;
   if (!pReport)
      {
      bSuccess = FALSE ;
      goto Exit0 ;
      }

   if (!FileRead (hFile, &DiskReport, sizeof (DISKREPORT)))
      {
      bSuccess = FALSE ;
      goto Exit0 ;
      }


   switch (dwMajorVersion)
      {
      case (1):
         
         SetHourglassCursor() ;
     
         ResetReportView (hWndReport) ;

         OpenReportVer1 (hFile, &DiskReport, pReport, dwMinorVersion) ;

         // change to report view if we are opening a 
         // report file
         if (bReportFile && iPerfmonView != IDM_VIEWREPORT)
            {
            SendMessage (hWndMain, WM_COMMAND, (LONG)IDM_VIEWREPORT, 0L) ;
            }

         if (iPerfmonView == IDM_VIEWREPORT)
            {
            SetPerfmonOptions (&DiskReport.perfmonOptions) ;
            }

         SetArrowCursor() ;

         break ;
      }  // switch

Exit0:
      
   if (bReportFile)
      {
      CloseHandle (hFile) ;
      }

   return (bSuccess) ;
   }  // OpenReport

void ResetReportView (HWND hWndReport)
   {  // ResetReportView
   PREPORT        pReport ;

   pReport = ReportData (hWndReport) ;
   
   if (!pReport)
      {
      return ;
      }
   
   ChangeSaveFileName (NULL, IDM_VIEWREPORT) ;

   if (pReport->pSystemGroupFirst)
      {
      ResetReport (hWndReport) ;
      }
   }  // ResetReportView


void ResetReport (HWND hWndReport)
   {  // ResetReport
   PREPORT        pReport ;
   PSYSTEMGROUP   pSystemGroup, pSystemGroupDelete ;
   POBJECTGROUP   pObjectGroup, pObjectGroupDelete ;
   PCOUNTERGROUP  pCounterGroup, pCounterGroupDelete ;
   HDC            hDC ;

   pReport = ReportData (hWndReport) ;
   if (!pReport)
      return ;

   ClearReportTimer (pReport) ;

   pSystemGroup = pReport->pSystemGroupFirst ;
   while (pSystemGroup)
      { 
      pObjectGroup = pSystemGroup->pObjectGroupFirst ;
      while (pObjectGroup)
         {
         pCounterGroup = pObjectGroup->pCounterGroupFirst ;
         while (pCounterGroup)
            {
            pCounterGroupDelete = pCounterGroup ;
            pCounterGroup = pCounterGroup->pCounterGroupNext ;
            MemoryFree (pCounterGroupDelete) ;
            }  // while pCounter...
         
         pObjectGroupDelete = pObjectGroup ;
         pObjectGroup = pObjectGroup->pObjectGroupNext ;
         ColumnGroupRemove (pObjectGroupDelete->pColumnGroupFirst) ;
         MemoryFree (pObjectGroupDelete->lpszObjectName) ;
         MemoryFree (pObjectGroupDelete) ;
         }  // while pObject

      pSystemGroupDelete = pSystemGroup ;
      pSystemGroup = pSystemGroup->pSystemGroupNext ;
      MemoryFree (pSystemGroupDelete->lpszSystemName) ;
      MemoryFree (pSystemGroupDelete) ;
      }  // while pSystem...

   FreeLines (pReport->pLineFirst) ;
   pReport->pLineFirst = NULL ;

   FreeSystems (pReport->pSystemFirst) ;
   pReport->pSystemFirst = NULL ;

   pReport->pSystemGroupFirst = NULL ;
   pReport->CurrentItemType   = REPORT_TYPE_NOTHING ;
   pReport->CurrentItem.pLine = NULL ;

   // reset scrolling ranges
   pReport->xWidth = 0 ;
   pReport->yHeight = 0 ;
   pReport->xMaxCounterWidth = 0 ;
   hDC = GetDC (hWndReport) ;
   SetReportPositions (hDC, pReport) ;

   SelectFont (hDC, pReport->hFont) ;
   pReport->xValueWidth = TextWidth (hDC, szValuePlaceholder) ;

   ReleaseDC (hWndReport, hDC) ;

   WindowInvalidate (hWndReport) ;
   }  // ResetReport
   
void ClearReportDisplay (HWND hWndReport)
   {  // ResetReport
   PREPORT        pReport ;
   PLINE          pLine;

   if (PlayingBackLog())
      {
      return ;
      }

   pReport = ReportData (hWndReport) ;
   if (!pReport || !pReport->pLineFirst)
      return ;

   for (pLine = pReport->pLineFirst ;
        pLine ;
        pLine = pLine->pLineNext)
      {
      // reset the new data counts
      pLine->bFirstTime = 2 ;
      }

   // re-draw the values
   UpdateReportValues (pReport) ;

   }  // ClearReportDisplay
 
//=========================================
// we don't print. we just export
//
// if need printing, define KEEP_PRINT
//=========================================
#ifdef KEEP_PRINT
BOOL PrintReportDisplay (HDC hDC,
                         PREPORT pReport)
   {
   SetReportPositions (hDC, pReport) ;
   DrawReport (hDC, pReport) ;
   return TRUE ;
   }  // PrintReportDisplay



BOOL PrintReport (HWND hWndParent,
                  HWND hWndReport)
   {
   PREPORT        pReport ;
   HDC            hDC ;
   int            xPageWidth ;
   int            yPageHeight ;
   int            xValueWidth ;

   HFONT          hFont, hFontHeaders ;
   int            yLineHeight ;

   pReport = ReportData (hWndReport) ;
   if (!pReport)
      return (FALSE) ;

   hDC = PrintDC () ;
   if (!hDC)
      {
      PostError () ;
      return (FALSE) ;
      }
      
   xPageWidth = GetDeviceCaps (hDC, HORZRES) ;
   yPageHeight = GetDeviceCaps (hDC, VERTRES) ;

   
   StartJob (hDC, TEXT("Performance Monitor Report")) ;
   StartPage (hDC) ;   


   hFont = pReport->hFont ;
   hFontHeaders = pReport->hFontHeaders ;
   yLineHeight = pReport->yLineHeight ;
   xValueWidth = pReport->xValueWidth ;

   pReport->hFont = hFontPrinterScales ;
   pReport->hFontHeaders = hFontPrinterScalesBold ;

   SelectFont (hDC, pReport->hFont) ;
   pReport->yLineHeight = FontHeight (hDC, TRUE) ;

   pReport->xValueWidth = TextWidth (hDC, szValuePlaceholder) ;

   PrintReportDisplay (hDC, pReport) ;

   EndPage (hDC) ;
   EndJob (hDC) ;
   
   DeleteDC (hDC) ;


   pReport->hFont = hFont ;
   pReport->hFontHeaders = hFontHeaders ;
   pReport->yLineHeight = yLineHeight ;

   pReport->xValueWidth = xValueWidth ;

   hDC = GetDC (hWndReport) ;
   SetReportPositions (hDC, pReport) ;
   ReleaseDC (hWndReport, hDC) ;

   return (FALSE) ;
   }  // PrintReport

// we don't print. we just export
#endif



BOOL ReportInsertLine (HWND hWnd, PLINE pLine)
/*
   Effect:        Insert the line pLine into the data structures for the
                  Report of window hWnd. The line is added to the list of
                  lines, and also added to the report structure in the 
                  appropriate System, Object, and Counter.

   Returns:       Whether the function was successful. If this function 
                  returns FALSE, the line was not added.
*/
   {  // ReportInsertLine
   HDC            hDC ;
   PREPORT        pReport ;
   PSYSTEMGROUP   pSystemGroup ;
   POBJECTGROUP   pObjectGroup ;
   PCOUNTERGROUP  pCounterGroup ;
   PLINE          pLineEquivalent ;
   int            OldCounterWidth ;
   BOOL           bNewCounterGroup ;

   pReport = ReportData (hWnd) ;
   pReport->bModified = TRUE ;

   pLineEquivalent = FindEquivalentLine (pLine, pReport->pLineFirst) ;
   if (pLineEquivalent) 
      {
      return (FALSE) ; 
      }
   else
      {
      //=============================//
      // Add line, line's system     //
      //=============================//

      LineAppend (&pReport->pLineFirst, pLine) ;
      SystemAdd (&pReport->pSystemFirst, pLine->lnSystemName) ;


      //=============================//
      // Find correct spot; add line //
      //=============================//

      pSystemGroup = GetSystemGroup (pReport, pLine->lnSystemName) ;
      pObjectGroup = GetObjectGroup (pSystemGroup, pLine->lnObjectName) ;
      pCounterGroup = GetCounterGroup (pObjectGroup, 
                                 pLine->lnCounterDef.CounterNameTitleIndex,
                                 &bNewCounterGroup,
                                 pLine->lnCounterName) ;

      if (!pCounterGroup)
         return (FALSE) ;

      LineCounterAppend (&pCounterGroup->pLineFirst, pLine) ;
   
      //=============================//
      // Calculate report positions  //
      //=============================//
      hDC = GetDC (hWnd) ;
      SelectFont (hDC, pReport->hFontHeaders) ;

      if (bNewCounterGroup)
         {
         // re-calc. the max. counter group width 
         OldCounterWidth = pReport->xMaxCounterWidth ;
         pReport->xMaxCounterWidth = 
            max (pReport->xMaxCounterWidth,
                 TextWidth (hDC, pLine->lnCounterName)) ;
         if (OldCounterWidth < pReport->xMaxCounterWidth)
            {
            // adjust the report width with the new counter width
            pReport->xWidth +=
               (pReport->xMaxCounterWidth - OldCounterWidth);
            }
         }

      if (pLine->lnCounterType == PERF_COUNTER_LARGE_RAWCOUNT_HEX)
         {
         SelectFont (hDC, pReport->hFont) ;
         pReport->xValueWidth = TextWidth (hDC, szValueLargeHexPlaceholder) ;
         }

      if (!bDelayAddAction)
         {
         SetReportPositions (hDC, pReport) ;
         }
      ReleaseDC (hWnd, hDC) ;

      pReport->CurrentItem.pLine = pLine ;
      pReport->CurrentItemType = REPORT_TYPE_LINE ;
      
      if (!bDelayAddAction)
         {
         if (PlayingBackLog ())
            {
            PlaybackReport (hWndReport) ;
            }
         else if (pReport->iStatus == iPMStatusClosed)
            {
            SetReportTimer (pReport) ;
            }
         
         WindowInvalidate (hWnd) ;
         }
      }  // else

      return (TRUE) ;
   }  // ReportInsertLine


BOOL ExportComputerName (HANDLE hFile, PSYSTEMGROUP pSystemGroup)
{
   int            StringLen ;
   BOOL           bWriteSuccess = TRUE ;

   CHAR           TempBuff [LongTextLen * 2] ;

   TCHAR          UnicodeBuff [LongTextLen] ;

   // export computer name
   strcpy (TempBuff, LineEndStr) ;
   strcat (TempBuff, LineEndStr) ;
   StringLen = strlen (TempBuff) ;
   TSPRINTF (UnicodeBuff, szSystemFormat, pSystemGroup->lpszSystemName) ;
   ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
   strcat (TempBuff, LineEndStr) ;

   if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
      {
      bWriteSuccess = FALSE ; 
      }

   return (bWriteSuccess) ;
}  // ExportComputerName

#define WRITE_FILE_TIME 2
BOOL ExportObjectName (HANDLE hFile, POBJECTGROUP pObjectGroup, int *pColNum)
{
   int            StringLen ;
   BOOL           bNeedToExport ;
   BOOL           bWriteSuccess = TRUE ;
   PCOLUMNGROUP   pColumnGroup ;
   int            ParentNum, InstanceNum ;
   int            TimeToWrite ;

   CHAR           TempBuff [512 * 2] ;

   TCHAR          UnicodeBuff [512] ;

   ParentNum = InstanceNum = 0 ;

   if (pColNum)
      {
      *pColNum = 0 ;
      }

   // export object name
   strcpy (TempBuff, LineEndStr) ;
   StringLen = strlen (TempBuff) ;
   TSPRINTF (UnicodeBuff, szObjectFormat, pObjectGroup->lpszObjectName) ;
   ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
   strcat (TempBuff, LineEndStr) ;

   if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
      {
      goto Exit0 ;
      }
   

   TimeToWrite = 0 ;

   // export column group
   if (pObjectGroup->pColumnGroupFirst)
      {
      strcpy (TempBuff, pDelimiter) ;
      strcat (TempBuff, pDelimiter) ;
      StringLen = strlen (TempBuff) ;

      bNeedToExport = FALSE ;

      // export Parent Names
      for (pColumnGroup = pObjectGroup->pColumnGroupFirst ;
           pColumnGroup ;
           pColumnGroup = pColumnGroup->pColumnGroupNext)
         {  // for

         if (pColumnGroup->lpszParentName)
            {
            ParentNum++ ;
            bNeedToExport = TRUE ;
            ConvertUnicodeStr (&TempBuff[StringLen],
                               pColumnGroup->lpszParentName) ;
            StringLen = strlen (TempBuff) ;
            }
                        
         strcat (&TempBuff[StringLen], pDelimiter) ;
         StringLen = strlen (TempBuff) ;

         // check if we need to export this line before it is filled up
         TimeToWrite++ ;
         if (TimeToWrite > WRITE_FILE_TIME)
            {
            if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
               {
               goto Exit0 ;
               }
            TimeToWrite = 0 ;
            StringLen = 0 ;
            TempBuff[0] = TEXT('\0') ;
            }
         }

      // write the line delimiter
      strcpy (&TempBuff[StringLen], LineEndStr) ;
      if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
         {
         goto Exit0 ;
         }
          
      if (!bNeedToExport)
         {
         ParentNum = 0 ;
         }


      // setup to export Instances
      strcpy (TempBuff, pDelimiter) ;
      strcat (TempBuff, pDelimiter) ;
      StringLen = strlen (TempBuff) ;
      bNeedToExport = FALSE ;
      TimeToWrite = 0 ;

      // export Instance Names
      for (pColumnGroup = pObjectGroup->pColumnGroupFirst ;
           pColumnGroup ;
           pColumnGroup = pColumnGroup->pColumnGroupNext)
         {  // for

         if (pColumnGroup->lpszInstanceName)
            {
            InstanceNum++ ;
            bNeedToExport = TRUE ;
            ConvertUnicodeStr (&TempBuff[StringLen],
                               pColumnGroup->lpszInstanceName) ;
            StringLen = strlen (TempBuff) ;
            }
               
         strcat (&TempBuff[StringLen], pDelimiter) ;
         StringLen = strlen (TempBuff) ;

         // check if we need to export this line before it is filled up
         TimeToWrite++ ;
         if (TimeToWrite > WRITE_FILE_TIME)
            {
            if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
               {
               goto Exit0 ;
               }
            TimeToWrite = 0 ;
            StringLen = 0 ;
            TempBuff[0] = TEXT('\0') ;
            }
         }

      // write the line delimiter
      strcpy (&TempBuff[StringLen], LineEndStr) ;
      if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
         {
         goto Exit0 ;
         }

      if (!bNeedToExport)
         {
         InstanceNum = 0 ;
         }
      }

   if (pColNum)
      {
      *pColNum = max (ParentNum, InstanceNum) ;
      }

   return (TRUE) ;

Exit0:
   return (FALSE) ;

}  // ExportObjectName

BOOL ExportLineName (HANDLE hFile, PLINE pLine, int *pExportCounterName)
{
   FLOAT          eValue ;
   int            StringLen ;
   BOOL           bWriteSuccess = TRUE ;

   CHAR           TempBuff [LongTextLen * 2] ;

   TCHAR          UnicodeBuff [LongTextLen] ;


   strcpy (TempBuff, pDelimiter) ;
   
   if (*pExportCounterName)
      {
      StringLen = strlen (TempBuff) ;
      ConvertUnicodeStr (&TempBuff[StringLen], pLine->lnCounterName) ;
      strcat (TempBuff, pDelimiter) ;
      *pExportCounterName = FALSE ;
      }
   StringLen = strlen (TempBuff) ;

   if (pLine->bFirstTime == 0)
      {
      eValue = CounterEntry (pLine) ;

      if (pLine->lnCounterType == PERF_COUNTER_RAWCOUNT_HEX ||
          pLine->lnCounterType == PERF_COUNTER_LARGE_RAWCOUNT_HEX)
         {
         DWORD LowValue  ;
         DWORD HighValue ;

         LowValue  = (DWORD) (pLine->lnaCounterValue[0]) ;
         HighValue = (DWORD) (pLine->lnaCounterValue[0] >> 32);

         if (pLine->lnCounterType == PERF_COUNTER_RAWCOUNT_HEX ||
             HighValue == 0)
            {
            TSPRINTF (UnicodeBuff,
                      szHexFormat,
                      LowValue) ;
            }
         else
            {
            TSPRINTF (UnicodeBuff,
                      szLargeHexFormat,
                      HighValue,
                      LowValue) ;
            }
         }
      else
         {
         TSPRINTF (UnicodeBuff,
                   (eValue > eStatusLargeValueMax) ?
                       szLargeValueFormat : szValueFormat,
                   eValue) ;
         ConvertDecimalPoint (UnicodeBuff) ;
         }
      ConvertUnicodeStr (&TempBuff[StringLen], UnicodeBuff) ;
      }
   else
      {
      // export "----" for unstable values
      strcat (&TempBuff[StringLen], "----");
      }

   // write the line value
   if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
      {
      goto Exit0 ;
      }

   return (TRUE) ;


Exit0:
   return (FALSE) ;
}  // ExportLineName


// This routine is need to insert the line values into its
// column location.  It is needed because not all the instances (columns)
// are available for the same line.
void SaveColumnLineData (PLINE pLine, LPSTR pColumnLineData)
{
   FLOAT          eValue ;
   LPSTR          pColumnLine ;

   CHAR           TempBuff [LongTextLen * 2] ;

   TCHAR          UnicodeBuff [LongTextLen] ;

   if (!pColumnLineData || pLine->iReportColumn < 0)
      {
      return ;
      }

   // find the offset into the pColumnLineData buffer for current line
   pColumnLine = pColumnLineData + pLine->iReportColumn * ShortTextLen ;

   if (pLine->bFirstTime == 0)
      {

      eValue = CounterEntry (pLine) ;


      if (pLine->lnCounterType == PERF_COUNTER_RAWCOUNT_HEX ||
          pLine->lnCounterType == PERF_COUNTER_LARGE_RAWCOUNT_HEX)
         {
         DWORD LowValue  ;
         DWORD HighValue ;

         LowValue  = (DWORD) (pLine->lnaCounterValue[0]) ;
         HighValue = (DWORD) (pLine->lnaCounterValue[0] >> 32);

         if (pLine->lnCounterType == PERF_COUNTER_RAWCOUNT_HEX ||
             HighValue == 0)
            {
            TSPRINTF (UnicodeBuff,
                      szHexFormat,
                      LowValue) ;
            }
         else
            {
            TSPRINTF (UnicodeBuff,
                      szLargeHexFormat,
                      HighValue,
                      LowValue) ;
            }
         }
      else
         {
         TSPRINTF (UnicodeBuff,
                   (eValue > eStatusLargeValueMax) ?
                       szLargeValueFormat : szValueFormat,
                   eValue) ;
         ConvertDecimalPoint (UnicodeBuff) ;
         }
      ConvertUnicodeStr (TempBuff, UnicodeBuff) ;
      strncpy (pColumnLine, TempBuff, ShortTextLen) ;
      *(pColumnLine + ShortTextLen - 1) = '\0' ;
      }
   else
      {
      // export "----" for unstable values
      strcpy (pColumnLine, "----");
      }
}  // SaveColumnLineData

BOOL ExportColumnLineData (HANDLE hFile,
                           int ColumnTotal,
                           PCOUNTERGROUP pCounterGroup,
                           LPSTR pColumnLineData)
{
   int            iIndex ;
   int            StringLen ;
   CHAR           TempBuff [LongTextLen] ;
   LPSTR          pCurrentLineData ;

   // export the counter name
   strcpy (TempBuff, pDelimiter) ;
   StringLen = strlen (TempBuff) ;
   ConvertUnicodeStr (&TempBuff[StringLen], pCounterGroup->pLineFirst->lnCounterName) ;
   strcat (TempBuff, pDelimiter) ;
   if (!FileWrite (hFile, TempBuff, strlen(TempBuff)))
      {
      goto Exit0 ;
      }

   // go thru each column and export the line value if it has been stored   
   for (iIndex = 0, pCurrentLineData = pColumnLineData ;
      iIndex < ColumnTotal ;
      iIndex++, pCurrentLineData += ShortTextLen )
      {
      if (*pCurrentLineData != 0)
         {
         // data available for this column
         if (!FileWrite (hFile, pCurrentLineData, strlen(pCurrentLineData)))
            {
            goto Exit0 ;
            }
         }

      if (!FileWrite (hFile, pDelimiter, strlen(pDelimiter)))
         {
         goto Exit0 ;
         }
      }

   if (!FileWrite (hFile, LineEndStr, strlen(LineEndStr)))
      {
      goto Exit0 ;
      }

   return (TRUE) ;


Exit0:
   return (FALSE) ;

}  // ExportColumnLineData

void ExportReport (void)
{
   HANDLE         hFile = 0 ;
   PREPORT        pReport ;
   PSYSTEMGROUP   pSystemGroup ;
   POBJECTGROUP   pObjectGroup ;
   PCOUNTERGROUP  pCounterGroup ;
   PLINE          pLine ;
   BOOL           bExportComputer ;
   BOOL           bExportObject ;
   BOOL           bExportCounterName ;
   int            ColumnTotal = 0 ;
   LPSTR          pColumnLineData = NULL ;
   LPTSTR         pFileName = NULL ;
   INT            ErrCode = 0 ;

   if (!(pReport = ReportData (hWndReport)))
      {
      return ;
      }

   // see if there is anything to export..
   if (!(pReport->pSystemGroupFirst))
      {
      return ;
      }

   SetHourglassCursor() ;
   
   if (ErrCode = ExportFileOpen (hWndReport, &hFile,
       pReport->iIntervalMSecs, &pFileName))
      {
      goto Exit0 ;
      }

   if (!pFileName)
      {
      // the case when user cancel
      goto Exit0 ;
      }

   // export each system group

   for (pSystemGroup = pReport->pSystemGroupFirst ;
        pSystemGroup ;
        pSystemGroup = pSystemGroup->pSystemGroupNext)
      {  // for System...

      bExportComputer = TRUE ;

      for (pObjectGroup = pSystemGroup->pObjectGroupFirst ;
           pObjectGroup ;
           pObjectGroup = pObjectGroup->pObjectGroupNext)
         {  // for Object...

         bExportObject = TRUE ;

         for (pCounterGroup = pObjectGroup->pCounterGroupFirst ;
              pCounterGroup ;
              pCounterGroup = pCounterGroup->pCounterGroupNext)
            {  // for Counter...

            bExportCounterName = TRUE ;

            // Column data buffer has been allocated for this object type, 
            // zero out the buffer and prepare for next round.
         
            if (pColumnLineData)
               {
               memset (pColumnLineData, 0, ColumnTotal * ShortTextLen) ;
               }

            for (pLine = pCounterGroup->pLineFirst ;
                 pLine ;
                 pLine = pLine->pLineCounterNext)
               {  // for Line...

               if (bExportComputer)
                  {
                  // only need to do this for the first object
                  bExportComputer = FALSE ;
                  if (!ExportComputerName (hFile, pSystemGroup))
                     {
                     ErrCode = ERR_EXPORT_FILE ;
                     goto Exit0 ;
                     }
                  }

               if (bExportObject)
                  {
                  // only need to do this for the first counter group
                  bExportObject = FALSE ;
                  if (!ExportObjectName (hFile, pObjectGroup, &ColumnTotal))
                     {
                     ErrCode = ERR_EXPORT_FILE ;
                     goto Exit0 ;
                     }

                  if (ColumnTotal > 1)
                     {
                     // special case to setup a column array and export
                     // the line values later
                     pColumnLineData = MemoryAllocate (ColumnTotal * ShortTextLen) ;
                     if (!pColumnLineData)
                        {
                        ErrCode = ERR_EXPORT_FILE ;
                        goto Exit0 ;
                        }
                     }
                  }

               if (ColumnTotal > 1)
                  {
                  // save the line value into its column & export later
                  SaveColumnLineData (pLine, pColumnLineData) ;
                  }
               else
                  {
                  // simple case, export the line now
                  if (!ExportLineName (hFile, pLine, &bExportCounterName))
                     {
                     ErrCode = ERR_EXPORT_FILE ;
                     goto Exit0 ;
                     }
                  }
               }  // for Line...

            if (!bExportCounterName)
               {
               // export the line end
               if (!FileWrite (hFile, LineEndStr, strlen(LineEndStr)))
                  {
                  ErrCode = ERR_EXPORT_FILE ;
                  goto Exit0 ;
                  }
               }

            if (pColumnLineData)
               {
               // now, do the actual export
               if (!ExportColumnLineData (hFile,
                  ColumnTotal,
                  pCounterGroup,
                  pColumnLineData))
                  {
                  ErrCode = ERR_EXPORT_FILE ;
                  goto Exit0 ;
                  }
               }
            }  // for Counter...

         // done with the object, done with the buffer
         if (pColumnLineData)
            {
            MemoryFree (pColumnLineData) ;
            ColumnTotal = 0 ;
            pColumnLineData = NULL ;
            }
         }  // for Object


      }  // for System...

Exit0:

   SetArrowCursor() ;

   if (pColumnLineData)
      {
      MemoryFree (pColumnLineData) ;
      }

   if (hFile)
      {
      CloseHandle (hFile) ;
      }

   if (pFileName)
      {
      if (ErrCode)
         {
         DlgErrorBox (hWndGraph, ErrCode, pFileName) ;
         }

      MemoryFree (pFileName) ;
      }

}  // ExportReport


