
#include "perfmon.h"
#include <lmcons.h>
#include <lmerr.h>
#include <lmapibuf.h>
#include <lmwksta.h>
#include <stdio.h>         // for sprintf
#include <locale.h>        // for setlocale
#include "utils.h"

#include "perfdata.h"      // for OpenSystemPerfData
#include "alert.h"         // for AlertInsertLine
#include "report.h"        // for ReportInsertLine
#include "grafdata.h"      // for GraphInsertLine
#include "log.h"           // for OpenLog
#include "fileopen.h"      // for FileGetName
#include "fileutil.h"      // for FileRead etc
#include "command.h"       // for PrepareMenu
#include "playback.h"      // for PlayingBackLog & LogPositionSystemTime
#include "system.h"
#include "globals.h"
#include "pmemory.h"       // for MemoryFree
#include "status.h"        // for StatusLineReady
#include "pmhelpid.h"

// test for delimiter, end of line and non-digit characters
// used by IsNumberInUnicodeList routine
//
#define DIGIT       1
#define DELIMITER   2
#define INVALID     3

// globals used for International Date and Time formats
enum DATE_STYLE
   {
   YEAR_FIRST,       // YYMMDD
   DAY_FIRST,        // DDMMYY
   MONTH_FIRST       // MMDDYY
   } DateStyle ;

TCHAR szInternational[] = TEXT("Intl") ;
TCHAR sz1159[6] ;       // AM String
TCHAR sz2359[6] ;       // PM String
int   iTime ;           // = 0 for 12-hour format,  <> 0 for 24-hour format
int   YearCharCount ;   // = 4 for 1990, = 2 for 90

TCHAR szDateFormat[ResourceStringLen] ;
TCHAR szTimeFormat[ResourceStringLen] ;

TCHAR LeadingZeroStr [] = TEXT("%02d") ;
TCHAR NoLeadingZeroStr [] = TEXT("%d") ;

#define EvalThisChar(c,d) ( \
     (c == d) ? DELIMITER : \
     (c == 0) ? DELIMITER : \
     (c < (WCHAR)'0') ? INVALID : \
     (c > (WCHAR)'9') ? INVALID : \
     DIGIT)

#define SIZE_OF_BIGGEST_INTEGER  16 
// #define SIZE_OF_BIGGEST_INTEGER (16*sizeof(WCHAR)) 


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

BOOL AddObjectToSystem ( PLINE , PPERFSYSTEM );
BOOL GetLogFileComputer (HWND hWndParent, LPTSTR lpComputerName, DWORD BufferSize) ;


HWND PerfmonViewWindow (void)
/*
   Effect:        Return the current data window, i.e. the window currently
                  visible as the client area of Perfmon.  This is either a
                  chart, log, alert, or report window.
*/
   {  // PerfmonDataWindow
   switch (iPerfmonView)
      {  // switch
      case IDM_VIEWLOG:
         return (hWndLog) ;

      case IDM_VIEWALERT:
         return (hWndAlert) ;

      case IDM_VIEWREPORT:
         return (hWndReport) ;

//      case IDM_VIEWCHART:
      default:
         return (hWndGraph) ;
      }  // switch
   }  // PerfmonViewWindow




BOOL ChooseComputer (HWND hWndParent, LPTSTR lpszComputer)
/*
   Effect:        Display the choose Domain/Computer dialog
                  If the user selects a computer,  copy the 
                  computer name to lpszComputer and return
                  nonnull. If the user cancels, return FALSE.

   Assert:        lpszComputer is at least MAX_SYSTEM_NAME_LENGTH + 1
                  characters.
*/
   {  // ChooseComputer
   BOOL                     bSuccess ;
   WCHAR                    wszWideComputer[MAX_COMPUTERNAME_LENGTH + 3] ;

   // bring up the select Log Computer dialog
   bSuccess = GetLogFileComputer (hWndParent, wszWideComputer,
         sizeof(wszWideComputer) / sizeof(WCHAR)) ;

   if (bSuccess)
      {
      lstrcpy (lpszComputer, wszWideComputer) ;
      }

   return (bSuccess) ;
   }  // ChooseComputer
   

void SystemTimeDateString (SYSTEMTIME *pSystemTime,
                           LPTSTR lpszDate)
   {
   int      wYear ;
   
   wYear = pSystemTime->wYear ;
   if (YearCharCount == 2)
      {
      wYear %= 100 ;
      }

   switch (DateStyle)
      {
      case YEAR_FIRST:
         TSPRINTF (lpszDate, szDateFormat,
             wYear, pSystemTime->wMonth, pSystemTime->wDay) ;
         break ;

      case DAY_FIRST:
         TSPRINTF (lpszDate, szDateFormat,
             pSystemTime->wDay, pSystemTime->wMonth, wYear) ;
         break ;

      case MONTH_FIRST:
      default:
         TSPRINTF (lpszDate, szDateFormat,
             pSystemTime->wMonth, pSystemTime->wDay, wYear) ;
         break ;
      }
   }


void SystemTimeTimeString (SYSTEMTIME *pSystemTime,
                           LPTSTR lpszTime)
   {
   int            iHour ;
   BOOL           bPM ;

   if (iTime)
      {
      // 24 hor format
      TSPRINTF (lpszTime, szTimeFormat,
                pSystemTime->wHour,
                pSystemTime->wMinute,
                (FLOAT)pSystemTime->wSecond +
                (FLOAT)pSystemTime->wMilliseconds / (FLOAT) 1000.0) ;

      }
   else
      {
      // 12 hour format
      iHour = pSystemTime->wHour ;
      bPM = (iHour >= 12) ;

      if (iHour > 12)
         iHour -= 12 ;
      else if (!iHour)
         iHour = 12 ;

      TSPRINTF (lpszTime, szTimeFormat,
                iHour, pSystemTime->wMinute,
                (FLOAT)pSystemTime->wSecond + 
                (FLOAT)pSystemTime->wMilliseconds / (FLOAT) 1000.0 ,
                bPM ? sz2359 : sz1159) ;
      }
   }
            

void ShowPerfmonMenu (BOOL bMenu)
   {  // ShowPerfmonMenu
   if (!bMenu)
      {
      WindowEnableTitle (hWndMain, FALSE) ;
//      SetMenu(hWndMain, NULL) ;
      }
   else
      {
      WindowEnableTitle (hWndMain, TRUE) ;
      switch (iPerfmonView)
         {  // switch
         case IDM_VIEWCHART:
            SetMenu (hWndMain, hMenuChart) ;
            break ;

         case IDM_VIEWALERT:
            SetMenu (hWndMain, hMenuAlert) ;
            break ;

         case IDM_VIEWLOG:
            SetMenu (hWndMain, hMenuLog) ;
            break ;

         case IDM_VIEWREPORT:
            SetMenu (hWndMain, hMenuReport) ;
            break ;
         }  // switch
      }  // else

   if (bMenu != Options.bMenubar)
      {
      PrepareMenu (GetMenu (hWndMain)) ;
      }

   Options.bMenubar = bMenu ;
   }  // ShowPerfmonMenu



void SmallFileSizeString (int iFileSize,
                          LPTSTR lpszFileText)
   {  // SmallFileSizeString
   if (iFileSize < 1000000)
      TSPRINTF (lpszFileText, TEXT(" %1.1fK "), ((FLOAT) iFileSize) / 1000.0) ;
   else
      TSPRINTF (lpszFileText, TEXT(" %1.1fM "), ((FLOAT) iFileSize) / 1000000.0) ;
   }  // SmallFileSizeString   



BOOL DoWindowDrag (LPARAM lParam)
   {
   if (!Options.bMenubar && !IsZoomed (hWndMain))
      {
      SendMessage (hWndMain, WM_NCLBUTTONDOWN, HTCAPTION, lParam) ;
      return (TRUE) ;
      }
   else
      return (FALSE) ;
   }



// Filetimes are in 100NS units
#define FILETIMES_PER_SECOND     10000000


int SystemTimeDifference (SYSTEMTIME *pst1, SYSTEMTIME *pst2)
   {
   LARGE_INTEGER  li1, li2 ;
   LARGE_INTEGER  liDifference, liDifferenceSeconds ;
   DWORD          uRemainder ;


   SystemTimeToFileTime (pst1, (FILETIME *) &li1) ;
   SystemTimeToFileTime (pst2, (FILETIME *) &li2) ;

   liDifference = LargeIntegerSubtract (li2, li1) ;
   liDifference.LowPart += (FILETIMES_PER_SECOND / 2) ;

   liDifferenceSeconds =
     ExtendedLargeIntegerDivide (liDifference, FILETIMES_PER_SECOND, 
                                    &uRemainder) ;

   return (liDifferenceSeconds.LowPart) ;
   }
 

BOOL InsertLine (PLINE pLine)
{  // InsertLine

    BOOL bReturn;
   
    switch (pLine->iLineType) {  // switch
        case LineTypeChart:
            bReturn = ChartInsertLine (pGraphs, pLine) ;
            break ;

        case LineTypeAlert:
            bReturn = AlertInsertLine (hWndAlert, pLine) ;
            break ;

        case LineTypeReport:
            bReturn = ReportInsertLine (hWndReport, pLine) ;
            break ;

    }  // switch

    return bReturn;

}  // InsertLine
   

BOOL OpenWorkspace (HANDLE hFile, DWORD dwMajorVersion, DWORD dwMinorVersion)
   {
   DISKWORKSPACE  DiskWorkspace ;
   
   if (!FileRead (hFile, &DiskWorkspace, sizeof(DiskWorkspace)))
      {
      goto Exit0 ;
      }

   if (DiskWorkspace.ChartOffset == 0 &&
       DiskWorkspace.AlertOffset == 0 &&
       DiskWorkspace.LogOffset == 0 &&
       DiskWorkspace.ReportOffset == 0)
      {
      goto Exit0 ;
      }

   switch (dwMajorVersion)
      {  // switch
      case (1):

         if (dwMinorVersion == 1 || dwMinorVersion == 2)
            {
            // setup the window position and size
            SetWindowPlacement (hWndMain, &(DiskWorkspace.WindowPlacement)) ;
            }

         // change to the view as stored in the workspace file
         SendMessage (hWndMain, WM_COMMAND,
            (LONG)DiskWorkspace.iPerfmonView, 0L) ;
         iPerfmonView = DiskWorkspace.iPerfmonView ;

         if (DiskWorkspace.ChartOffset)
            {
            if (FileSeekBegin(hFile, DiskWorkspace.ChartOffset) == 0xFFFFFFFF)
               {
               goto Exit0 ;
               }

            if (!OpenChart (hWndGraph,
                           hFile,
                           dwMajorVersion,
                           dwMinorVersion,
                           FALSE))
               {
               goto Exit0 ;
               }
            }
         if (DiskWorkspace.AlertOffset)
            {
            if (FileSeekBegin(hFile, DiskWorkspace.AlertOffset) == 0xffffffff)
               {
               goto Exit0 ;
               }
            if (!OpenAlert (hWndAlert,
                       hFile,
                       dwMajorVersion,
                       dwMinorVersion,
                       FALSE))
               {
               goto Exit0 ;
               }
            }
         if (DiskWorkspace.LogOffset)
            {
            if (FileSeekBegin(hFile, DiskWorkspace.LogOffset) == 0xffffffff)
               {
               goto Exit0 ;
               }
            if (!OpenLog (hWndLog,
                          hFile,
                          dwMajorVersion,
                          dwMinorVersion,
                          FALSE))
               {
               goto Exit0 ;
               }
            }
         if (DiskWorkspace.ReportOffset)
            {
            if (FileSeekBegin(hFile, DiskWorkspace.ReportOffset) == 0xffffffff)
               {
               goto Exit0 ;
               }
            if (!OpenReport (hWndReport,
                        hFile,
                        dwMajorVersion,
                        dwMinorVersion,
                        FALSE))
               {
               goto Exit0 ;
               }
            }
         break ;

      default:
         goto Exit0 ;
         break ;
      }

   CloseHandle (hFile) ;
   return (TRUE) ;


Exit0:
   CloseHandle (hFile) ;
   return (FALSE) ;

   }  // OpenWorkspace


BOOL SaveWorkspace (void)
   {
   DISKWORKSPACE  DiskWorkspace ;
   PERFFILEHEADER FileHeader ;
   HANDLE         hFile ;
   long           DiskWorkspacePosition ;
   TCHAR          szFileName[FilePathLen] ;
   BOOL           bWriteErr = TRUE ;

   if (!FileGetName (PerfmonViewWindow(), IDS_WORKSPACEFILE, szFileName))
      {
      return (FALSE) ;
      }

   hFile = FileHandleCreate (szFileName) ;
   if (!hFile)
      {
      DlgErrorBox (PerfmonViewWindow (), ERR_CANT_OPEN, szFileName) ;
      return (FALSE) ;
      }

   memset (&FileHeader, 0, sizeof (FileHeader)) ;
   lstrcpy (FileHeader.szSignature, szPerfWorkspaceSignature) ;
   FileHeader.dwMajorVersion = WorkspaceMajorVersion ;
   FileHeader.dwMinorVersion = WorkspaceMinorVersion ;

   if (!FileWrite (hFile, &FileHeader, sizeof (PERFFILEHEADER)))
      {
      goto Exit0 ;
      }

   // reserve space in the file.  We will fill up info
   // and write into this guy later.
   memset (&DiskWorkspace, 0, sizeof(DiskWorkspace)) ;
   DiskWorkspacePosition = FileTell (hFile) ;
   GetWindowPlacement (hWndMain, &(DiskWorkspace.WindowPlacement)) ;
   if (!FileWrite (hFile, &DiskWorkspace, sizeof (DISKWORKSPACE)))
      {
      goto Exit0 ;
      }

   // put in chart data
   DiskWorkspace.ChartOffset = FileTell (hFile) ;
   if (!SaveChart (hWndGraph, hFile, 0))
      {
      goto Exit0 ;
      }

   // put in alert data
   DiskWorkspace.AlertOffset = FileTell (hFile) ;
   if (!SaveAlert (hWndAlert, hFile, 0))
      {
      goto Exit0 ;
      }

   // put in log data
   DiskWorkspace.LogOffset = FileTell (hFile) ;
   if (!SaveLog (hWndLog, hFile, 0))
      {
      goto Exit0 ;
      }

   // put in report data
   DiskWorkspace.ReportOffset = FileTell (hFile) ;
   if (!SaveReport (hWndReport, hFile, 0))
      {
      goto Exit0 ;
      }

   // put in the disk header info
   DiskWorkspace.iPerfmonView = iPerfmonView ;
   FileSeekBegin (hFile, DiskWorkspacePosition) ;
   if (!FileWrite (hFile, &DiskWorkspace, sizeof (DISKWORKSPACE)))
      {
      goto Exit0 ;
      }
   bWriteErr = FALSE ;

Exit0:
   if (bWriteErr)
      {
      DlgErrorBox (PerfmonViewWindow (), ERR_SETTING_FILE, szFileName) ;
      }

   CloseHandle (hFile) ;

   }  // SaveWorkspace

void SetPerfmonOptions (OPTIONS *pOptions)
   {
   Options = *pOptions ;
   ShowPerfmonMenu (Options.bMenubar) ;
   SizePerfmonComponents () ;
   WindowSetTopmost (hWndMain, Options.bAlwaysOnTop) ;
   }  // SetPerfmonOptions

void ChangeSaveFileName (LPTSTR szFileName, int iPMView)
   {
   LPTSTR   *ppFullName ;
   LPTSTR   *ppFileName ;
   BOOL     errorInput = FALSE ;


   switch (iPMView)
      {
      case IDM_VIEWCHART:
         ppFileName = &pChartFileName ;
         ppFullName = &pChartFullFileName ;
         break ;

      case IDM_VIEWALERT:
         ppFileName = &pAlertFileName ;
         ppFullName = &pAlertFullFileName ;
         break ;

      case IDM_VIEWREPORT:
         ppFileName = &pReportFileName ;
         ppFullName = &pReportFullFileName ;
         break ;

      case IDM_VIEWLOG:
         ppFileName = &pLogFileName ;
         ppFullName = &pLogFullFileName ;
         break ;

      default:
         errorInput = TRUE ;
         break ;
      }

   if (errorInput)
      {
      return ;
      }

   // release last filename
   if (*ppFullName)
      {
      MemoryFree (*ppFullName) ;
      *ppFileName = NULL ;
      *ppFullName = NULL ;
      }

   // allocate new file name and display it
   if (szFileName && (*ppFullName = StringAllocate (szFileName)))
      {
      *ppFileName = ExtractFileName (*ppFullName) ;
      }

   StatusLineReady (hWndStatus) ;

   }     // ChangeSaveFileName




BOOL
IsNumberInUnicodeList (
    IN DWORD   dwNumber,
    IN LPWSTR  lpwszUnicodeList
)
/*++

IsNumberInUnicodeList

Arguments:
        
    IN dwNumber
        DWORD number to find in list

    IN lpwszUnicodeList
        Null terminated, Space delimited list of decimal numbers

Return Value:

    TRUE:
            dwNumber was found in the list of unicode number strings

    FALSE:
            dwNumber was not found in the list.

--*/
{
    DWORD   dwThisNumber;
    WCHAR   *pwcThisChar;
    BOOL    bValidNumber;
    BOOL    bNewItem;
    WCHAR   wcDelimiter;    // could be an argument to be more flexible

    if (lpwszUnicodeList == 0) return FALSE;    // null pointer, # not founde

    pwcThisChar = lpwszUnicodeList;
    dwThisNumber = 0;
    wcDelimiter = (WCHAR)' ';
    bValidNumber = FALSE;
    bNewItem = TRUE;
    
    while (TRUE) {
        switch (EvalThisChar (*pwcThisChar, wcDelimiter)) {
            case DIGIT:
                // if this is the first digit after a delimiter, then 
                // set flags to start computing the new number
                if (bNewItem) {
                    bNewItem = FALSE;
                    bValidNumber = TRUE;
                }
                if (bValidNumber) {
                    dwThisNumber *= 10;
                    dwThisNumber += (*pwcThisChar - (WCHAR)'0');
                }
                break;
            
            case DELIMITER:
                // a delimter is either the delimiter character or the 
                // end of the string ('\0') if when the delimiter has been
                // reached a valid number was found, then compare it to the
                // number from the argument list. if this is the end of the
                // string and no match was found, then return.
                //
                if (bValidNumber) {
                    if (dwThisNumber == dwNumber) return TRUE;
                    bValidNumber = FALSE;
                }
                if (*pwcThisChar == 0) {
                    return FALSE;
                } else {
                    bNewItem = TRUE;
                    dwThisNumber = 0;
                }
                break;

            case INVALID:
                // if an invalid character was encountered, ignore all
                // characters up to the next delimiter and then start fresh.
                // the invalid number is not compared.
                bValidNumber = FALSE;
                break;

            default:
                break;

        }
        pwcThisChar++;
    }

}   // IsNumberInUnicodeList

BOOL
AppendObjectToValueList (
    DWORD   dwObjectId,
    PWSTR   pwszValueList
)
/*++

AppendObjectToValueList

Arguments:
        
    IN dwNumber
        DWORD number to insert in list

    IN PUNICODE_STRING
        pointer to unicode string structure that contains buffer that is
        Null terminated, Space delimited list of decimal numbers that
        may have this number appended to. 

Return Value:

    TRUE:
            dwNumber was added to list

    FALSE:
            dwNumber was not added. (because it's already there or
                an error occured)

--*/
{
    WCHAR           tempString [SIZE_OF_BIGGEST_INTEGER] ;
    DWORD           dwStrLen, dwNewStrLen;

    if (!pwszValueList) {
        return FALSE;
    }

    if (IsNumberInUnicodeList(dwObjectId, pwszValueList)) {
        return FALSE;   // object already in list
    } else {
        // append the new object id the  value list
        TSPRINTF (tempString, TEXT("%d "), dwObjectId) ;

        // see if string will fit (compare in bytes)

        dwStrLen = MemorySize (pwszValueList) - sizeof (UNICODE_NULL);

        dwNewStrLen = (lstrlen (pwszValueList) + lstrlen (tempString)) *
            sizeof (WCHAR);

        if (dwNewStrLen <= dwStrLen) {
            lstrcat (pwszValueList, tempString);
            return TRUE;
        } else {
            SetLastError (ERROR_OUTOFMEMORY); 
            return FALSE;
        }
    }
}

BOOL
AddObjectToSystem (
    PLINE pLine,
    PPERFSYSTEM pFirstSystem
)
{
    PPERFSYSTEM     pSystem;
    
    if ((ARGUMENT_PRESENT (pLine)) && (ARGUMENT_PRESENT(pFirstSystem))) {
        pSystem = SystemGet (pFirstSystem, pLine->lnSystemName);

        if (pSystem) {
            return AppendObjectToValueList (
                pLine->lnObject.ObjectNameTitleIndex,
                pSystem->lpszValue);
        } else {
            return FALSE;
        }
    } else {
        return FALSE;
    }
}

BOOL
RemoveObjectsFromSystem (
    PPERFSYSTEM pSystem
)
{
    DWORD   dwBufferSize = 0;

    if (ARGUMENT_PRESENT (pSystem)) {
        // don't do foreign computers
        if (pSystem->lpszValue && !strsame (pSystem->lpszValue, L"Foreign")){
            dwBufferSize = MemorySize (pSystem->lpszValue);

            memset (pSystem->lpszValue, 0, dwBufferSize);
            return TRUE;
        } else {
            return FALSE;
        }
    } else {
        return FALSE;
    }


}    

BOOL
BuildValueListForSystems (
    PPERFSYSTEM pSystemListHead,
    PLINE       pLineListHead
)
/*++

BuildValueListForSystem

Abstract:

    Walks down line list and builds the list of objects to query from
    that system containing that line.

Arguments:

    pSystemListHead

        head of system linked list
        each system will have it's "Value Name" list appended

    pLineListHead

        head of line list that will be searched for creating the new
        valuelist.


Return Value:


--*/
{

    PPERFSYSTEM     pSystem;    // system that contains current line
    PLINE           pThisLine;  // current line

    if ((ARGUMENT_PRESENT (pLineListHead)) && (ARGUMENT_PRESENT(pSystemListHead))) {
        // clear system entries:
        for (pSystem = pSystemListHead; pSystem; pSystem = pSystem->pSystemNext) {
            if (pSystem) {
                RemoveObjectsFromSystem (pSystem);
            }
        }

        // add new enties

        for (pThisLine = pLineListHead; pThisLine; pThisLine = pThisLine->pLineNext) {

            pSystem = SystemGet (pSystemListHead, pThisLine->lnSystemName);
            if (pSystem) {
                AppendObjectToValueList (
                    pThisLine->lnObject.ObjectNameTitleIndex,
                    pSystem->lpszValue);

            }
        }
        return TRUE;
    } else {    // argument(s) missing
        return FALSE;
    }
}

// define in Addline.c
extern   PLINESTRUCT       pLineEdit ;
#define  bEditLine (pLineEdit != NULL)

BOOL
SetSystemValueNameToGlobal (
    PPERFSYSTEM pSystem
)
{

    if (!bEditLine && ARGUMENT_PRESENT(pSystem)) {
        if (pSystem->lpszValue && RemoveObjectsFromSystem(pSystem)) {
            lstrcpy (
                pSystem->lpszValue,
                TEXT("Global ")) ;
            return TRUE;
        } else {
            return FALSE;
        }
    } else {
        return FALSE;
    }
}

BOOL
RemoveUnusedSystems (
    PPERFSYSTEM pSystemHead,
    PLINE       pLineHead
)
/*++

    walks system list and removes systems with no lines from list

--*/
{
    PPERFSYSTEM pSystem;
    PPERFSYSTEM pLastSystem;
    PLINE       pLine;
    BOOL        bSystemFound;

    pLastSystem = NULL;

    if ((ARGUMENT_PRESENT (pLineHead)) && (ARGUMENT_PRESENT(pSystemHead))) {
        for (pSystem = pSystemHead;
             pSystem;
             pLastSystem = pSystem, pSystem = pSystem->pSystemNext) {
            
            if (pSystem) {
                bSystemFound = FALSE;
                // walk lines to see if this system has a line
                for (pLine = pLineHead; pLine; pLine = pLine->pLineNext) {
                    // if system in line is this system, then bailout
                    if (strsame (pLine->lnSystemName, pSystem->sysName)) {
                        bSystemFound = TRUE;
                        break;
                    }
                }
                
                if (!bSystemFound) {    // delete this unused system

                    // fix pointers
                    pLastSystem->pSystemNext = pSystem->pSystemNext;

                    SystemFree (pSystem, TRUE);

                    // set pointer back to a valid structure
                    pSystem = pLastSystem; 
                }
            }
        }
    }
    return TRUE;
}

void CreatePerfmonSystemObjects ()
{
   ColorBtnFace = GetSysColor (COLOR_BTNFACE) ;
   hBrushFace = CreateSolidBrush (ColorBtnFace) ;
   hPenHighlight = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNHIGHLIGHT)) ;
   hPenShadow = CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNSHADOW)) ;
}

void DeletePerfmonSystemObjects ()
{
   if (hBrushFace)
      {
      DeleteBrush (hBrushFace) ;
      hBrushFace = 0 ;
      }
   if (hPenHighlight)
      {
      DeletePen (hPenHighlight) ;
      hPenHighlight = 0 ;
      }
   if (hPenShadow)
      {
      DeletePen (hPenShadow) ;
      hPenShadow = 0 ;
      }
}

// This routine count the number of the same charatcer in the input string
int  SameCharCount (LPTSTR pInputString)
{
   int      Count = 0 ;
   TCHAR    InputChar = *pInputString ;

   if (InputChar)
      {
      while (InputChar == *pInputString)
         {
         Count ++ ;
         pInputString ++ ;
         }
      }
   return (Count) ;
}

// create the format to be used in SystemTimeDateString()
BOOL CreateDateFormat (LPTSTR pShortDate)
{
   int   iIndex ;
   int   iDayCount ;
   int   iMonthCount ;
   int   DateSeparatorCount ;
   TCHAR szDateSeparator [10] ;
   BOOL  bFirstLeading, bSecondLeading, bThirdLeading ;

   // get the date format based on the first char
   if (*pShortDate == TEXT('M') || *pShortDate == TEXT('m'))
      {
      DateStyle = MONTH_FIRST ;
      }
   else if (*pShortDate == TEXT('D') || *pShortDate == TEXT('d'))
      {
      DateStyle = DAY_FIRST ;
      }
   else if (*pShortDate == TEXT('Y') || *pShortDate == TEXT('y'))
      {
      DateStyle = YEAR_FIRST ;
      }
   else
      {
      // bad format 
      return FALSE ;
      }

   bFirstLeading = bSecondLeading = bThirdLeading = FALSE ;

   switch (DateStyle)
      {
      case YEAR_FIRST:
         // YYYY-MM-DD
         YearCharCount = SameCharCount (pShortDate) ;
         pShortDate += YearCharCount ;
         DateSeparatorCount = SameCharCount (pShortDate) ;

         // get the separator string
         for (iIndex = 0; iIndex < DateSeparatorCount; iIndex ++)
            {
            szDateSeparator [iIndex] = *pShortDate++ ;
            }
         szDateSeparator [iIndex] = TEXT('\0') ;

         iMonthCount = SameCharCount (pShortDate) ;
         pShortDate += iMonthCount + DateSeparatorCount ;
         iDayCount = SameCharCount (pShortDate) ;

         if (YearCharCount == 2)
            {
            bFirstLeading = TRUE ;
            }

         if (iMonthCount == 2)
            {
            bSecondLeading = TRUE ;
            }

         if (iDayCount == 2)
            {
            bThirdLeading = TRUE ;
            }

         break ;

      case MONTH_FIRST:
         // MM-DD-YYYY
         iMonthCount = SameCharCount (pShortDate) ;
         pShortDate += iMonthCount ;
         DateSeparatorCount = SameCharCount (pShortDate) ;

         // get the separator string
         for (iIndex = 0; iIndex < DateSeparatorCount; iIndex ++)
            {
            szDateSeparator [iIndex] = *pShortDate++ ;
            }
         szDateSeparator [iIndex] = TEXT('\0') ;
         
         iDayCount = SameCharCount (pShortDate) ;
         pShortDate += iMonthCount + DateSeparatorCount ;
         YearCharCount = SameCharCount (pShortDate) ;


         if (iMonthCount == 2)
            {
            bFirstLeading = TRUE ;
            }

         if (iDayCount == 2)
            {
            bSecondLeading = TRUE ;
            }

         if (YearCharCount == 2)
            {
            bThirdLeading = TRUE ;
            }

         break ;

      case DAY_FIRST:
         // DD-MM-YYYY
         iDayCount = SameCharCount (pShortDate) ;
         pShortDate += iDayCount ;
         DateSeparatorCount = SameCharCount (pShortDate) ;

         // get the separator string
         for (iIndex = 0; iIndex < DateSeparatorCount; iIndex ++)
            {
            szDateSeparator [iIndex] = *pShortDate++ ;
            }
         szDateSeparator [iIndex] = TEXT('\0') ;
         
         iMonthCount = SameCharCount (pShortDate) ;
         pShortDate += iMonthCount + DateSeparatorCount ;
         YearCharCount = SameCharCount (pShortDate) ;

         if (iDayCount == 2)
            {
            bFirstLeading = TRUE ;
            }

         if (iMonthCount == 2)
            {
            bSecondLeading = TRUE ;
            }

         if (YearCharCount == 2)
            {
            bThirdLeading = TRUE ;
            }

         break ;
      }

   // now generate the date format
   lstrcpy (szDateFormat, bFirstLeading ? LeadingZeroStr : NoLeadingZeroStr) ;
   lstrcat (szDateFormat, szDateSeparator) ;
   lstrcat (szDateFormat, bSecondLeading ? LeadingZeroStr : NoLeadingZeroStr) ;
   lstrcat (szDateFormat, szDateSeparator) ;
   lstrcat (szDateFormat, bThirdLeading ? LeadingZeroStr : NoLeadingZeroStr) ;

   return TRUE ;
}

BOOL CreateTimeFormat (LPTSTR pTimeSeparator, int iLeadingZero)
{
   // create the format to be used in SystemTimeTimeString 
   if (iLeadingZero)
      {
      lstrcpy (szTimeFormat, LeadingZeroStr) ;
      }
   else
      {
      lstrcpy (szTimeFormat, NoLeadingZeroStr) ;
      }

   lstrcat (szTimeFormat, pTimeSeparator) ;
   lstrcat (szTimeFormat, LeadingZeroStr) ;
   lstrcat (szTimeFormat, pTimeSeparator) ;
//   lstrcat (szTimeFormat, LeadingZeroStr) ;

   // for the msec
   lstrcat (szTimeFormat, TEXT("%02.1f")) ;
   if (iTime == 0)
      {
      lstrcat (szTimeFormat, TEXT(" %s ")) ;
      }

   return TRUE ;
}  // CreateTimeFormats

BOOL GetInternational()
{
   TCHAR szShortDate[40] ;
   TCHAR szTime[40] ;   // time separator
   DWORD RetCode ;
   int   iTLZero = 0;      // = 0 for no leading zero, <> 0 for leading zero
   CHAR  aLanguageStr [2] ;
   LPSTR pRetStr ;

   // read the data from the win.ini (which i smapped to registry)
   RetCode = GetProfileString(szInternational,
      TEXT("sShortDate"), szShortDate, szShortDate, sizeof(szShortDate)/sizeof(TCHAR));
   
   if (RetCode)
      {
      RetCode = GetProfileString(szInternational,
         TEXT("sTime"), szTime, szTime, sizeof(szTime)/sizeof(TCHAR));
      }


   if (RetCode)
      {
      iTime   = GetProfileInt(szInternational, TEXT("iTime"), iTime);
      iTLZero = GetProfileInt(szInternational, TEXT("iTLZero"), iTLZero);

      if (iTime == 0)
         {
         // get the AM PM strings for 12-hour format.
         // These two strings could be NULL.
         sz1159[0] = sz2359[0] = TEXT('\0') ;
         GetProfileString(szInternational,
            TEXT("s1159"), sz1159, sz1159, sizeof(sz1159)/sizeof(TCHAR));

         GetProfileString(szInternational,
            TEXT("s2359"), sz2359, sz2359, sizeof(sz2359)/sizeof(TCHAR));
         }
      }

   // create the two formats
   if (RetCode)
      {
      RetCode = (DWORD) CreateDateFormat (szShortDate) ;
      }

   if (RetCode)
      {
      RetCode = (DWORD) CreateTimeFormat (szTime, iTLZero) ;
      }

   // use the system default language numeric
   aLanguageStr[0] = '\0' ;
   pRetStr = setlocale(LC_NUMERIC, aLanguageStr);

   return (RetCode != 0) ;
}  // GetInternational


// this routine is called to get the date/time formats either
// for the resource or from the registry.
void GetDateTimeFormats ()
{
   PALERT        pAlert ;
   PLOG          pLog ;
   if (!GetInternational())
      {
      // GetInternational failed, then get default formats from resource
      iTime = 0 ;
      DateStyle = MONTH_FIRST ;
      YearCharCount = 4 ;
      StringLoad (IDS_S1159, sz1159) ;
      StringLoad (IDS_S2359, sz2359) ;
      StringLoad (IDS_TIME_FORMAT, szTimeFormat) ;
      StringLoad (IDS_SHORT_DATE_FORMAT, szDateFormat) ;
      }
   WindowInvalidate (PerfmonViewWindow()) ;

   // reset all the field taht may be affected by the
   // language numberic changes

   pAlert = AlertData (hWndMain) ;
   pLog = LogData (hWndMain) ;

   if (pAlert)
      {
      DialogSetInterval (hWndAlert, IDD_ALERTINTERVAL, pAlert->iIntervalMSecs) ;
      }

   if (pLog)
      {
      DialogSetInterval (hWndLog, IDD_LOGINTERVAL, pLog->iIntervalMSecs) ;
      }
}  // GetDateTimeFormats

