//
//  Foreign computer support needs more work (a-robw)
//
#ifdef FOREIGN_COMPUTER_SUPPORT
#undef FOREIGN_COMPUTER_SUPPORT
#endif

#include "perfmon.h"
#include "system.h"     // external declarations for this file

#include "perfdata.h"
#include "perfmops.h"
#include "playback.h"   // for PlayingBackLog
#include "pmemory.h"
#include "utils.h"      // for strsame, et al
#include "sizes.h"


DWORD
SystemCount(
    PPERFSYSTEM pSystemFirst
)
{
    PPERFSYSTEM       pSystem ;
    DWORD           iNumSystems ;

    iNumSystems = 0 ;

    for (pSystem = pSystemFirst ;
         pSystem ;
         pSystem = pSystem->pSystemNext) {
        iNumSystems++ ;
    }

    return iNumSystems ;
}


BOOL
SystemSetupThread (PPERFSYSTEM pSystem)
{
    DWORD           dwThreadID ;
    HANDLE          hThread ;
    HANDLE          hStateDataMutex ;
    HANDLE          hPerfDataEvent ;
    SECURITY_ATTRIBUTES  SecAttr ;
    PPERFDATA       pSystemPerfData ;


    SecAttr.nLength = sizeof (SecAttr) ;
    SecAttr.bInheritHandle = TRUE ;
    SecAttr.lpSecurityDescriptor = NULL ;

    hThread = CreateThread (&SecAttr, 1024L,
        (LPTHREAD_START_ROUTINE)PerfDataThread, (LPVOID)(pSystem), 0L, &dwThreadID);

    if (!hThread) {
        SystemFree (pSystem, TRUE);
        return (FALSE) ;
    }

    // create a State Data Lock mutex
    hStateDataMutex = CreateMutex (&SecAttr, FALSE, NULL);
    if (!hStateDataMutex) {
        CloseHandle (hThread) ;
        SystemFree (pSystem, TRUE);
        return (FALSE);
    }
    hPerfDataEvent = CreateEvent (&SecAttr, TRUE, 0L, NULL) ;
    if (!hPerfDataEvent) {
        CloseHandle (hStateDataMutex) ;
        CloseHandle (hThread) ;
        SystemFree (pSystem, TRUE);
        return (FALSE);
    }

    // allocate Perfdata
    pSystemPerfData = (PPERFDATA) MemoryAllocate (4096L) ;
    if (!pSystemPerfData) {
        CloseHandle (hPerfDataEvent) ;
        CloseHandle (hStateDataMutex) ;
        CloseHandle (hThread) ;
        SystemFree (pSystem, TRUE);
        return (FALSE);
    }
    // now setup the pSystem..
    pSystem->dwThreadID = dwThreadID ;
    pSystem->hThread = hThread ;
    pSystem->hPerfDataEvent = hPerfDataEvent ;
    pSystem->pSystemPerfData = pSystemPerfData ;
    pSystem->hStateDataMutex = hStateDataMutex ;

    return (TRUE) ;
}

PPERFSYSTEM
SystemCreate (
    LPCTSTR lpszSystemName
)
{
    PPERFSYSTEM     pSystem ;
    PPERFDATA       pLocalPerfData = NULL;
    DWORD           Status ;
    DWORD           dwMemSize;
    TCHAR           GlobalValueBuffer[] = L"Global" ;
    TCHAR           ForeignValueBuffer[8+MAX_SYSTEM_NAME_LENGTH+1] =
                    L"Foreign " ;

    // attempt to allocate system data structure

    pSystem = MemoryAllocate (sizeof (PERFSYSTEM)) ;
    if (!pSystem) {
        SetLastError (ERROR_OUTOFMEMORY) ;
        return (NULL) ;
    }

    // initialize name and help table pointers

    pSystem->CounterInfo.pNextTable = NULL;
    pSystem->CounterInfo.dwLangId = 0;
    pSystem->CounterInfo.dwLastId = 0;
    pSystem->CounterInfo.TextString = NULL;

    lstrcpy (pSystem->sysName, lpszSystemName) ;

    // try to open key to registry, error code is in GetLastError()

    pSystem->sysDataKey = OpenSystemPerfData(lpszSystemName);

    // if a Null Key was returned then:
    //  a) there's no such computer
    //  b) the system is a foreign computer
    //
    //  before giving up, then see if it's a foreign computer

    if (!pSystem->sysDataKey) {

        // build foreign computer string

        lstrcat(ForeignValueBuffer, lpszSystemName) ;

        // assign System value name pointer to the local variable for trial

        pSystem->lpszValue = ForeignValueBuffer;

        // try to get data from the computer to see if it's for real
        // otherwise, give up and return NULL

        pLocalPerfData = MemoryAllocate (STARTING_SYSINFO_SIZE);
        if (pLocalPerfData == NULL) { // no mem so give up
            pSystem->lpszValue = NULL;
            SystemFree (pSystem, TRUE);
            SetLastError (ERROR_OUTOFMEMORY);
            return (NULL);
        } else {
            pSystem->sysDataKey = HKEY_PERFORMANCE_DATA; // local machine
            bCloseLocalMachine = TRUE ;

            dwMemSize = STARTING_SYSINFO_SIZE;
            Status = GetSystemPerfData (
                    pSystem->sysDataKey,
                    pSystem->lpszValue,
                    pLocalPerfData,
                    &dwMemSize);

            // success means a valid buffer came back
            // more data means someone tried (so it's probably good (?)

            if ((Status == ERROR_MORE_DATA) || (Status == ERROR_SUCCESS)) {
                if (Status == ERROR_SUCCESS) {
                    // see if a perf buffer was returned
                    if ((dwMemSize > 0) &&
                        pLocalPerfData->Signature[0] == (WCHAR)'P' &&
                        pLocalPerfData->Signature[1] == (WCHAR)'E' &&
                        pLocalPerfData->Signature[2] == (WCHAR)'R' &&
                        pLocalPerfData->Signature[3] == (WCHAR)'F' ) {
                        // valid buffer so continue
                    } else {
                        // invalid so unable to connect to that machine
                        pSystem->lpszValue = NULL;
                        SystemFree (pSystem, TRUE);
                        SetLastError (ERROR_BAD_NET_NAME); // unable to find name
                        return (NULL);
                    }
                } else {
                    // assume that if MORE_DATA is returned that SOME
                    // data was attempted so the buffer must be valid
                    // (we hope)
                }
                MemoryFree ((LPMEMORY)pLocalPerfData) ;
                pLocalPerfData = NULL;

                // if we are reading from a setting file, let this pass thru'
                if (bDelayAddAction == TRUE) {
                   pSystem->sysDataKey = NULL ;
                   pSystem->FailureTime = GetTickCount();
                   pSystem->dwSystemState = SYSTEM_DOWN;

                   // Free any memory that may have created
                   SystemFree (pSystem, FALSE) ;

                   pSystem->lpszValue = MemoryAllocate (TEMP_BUF_LEN*sizeof(WCHAR));

                   if (!pSystem->lpszValue) {
                      // unable to allocate memory
                      SystemFree (pSystem, TRUE);
                      SetLastError (ERROR_OUTOFMEMORY);
                      return (NULL) ;
                   } else {
                      lstrcpy (pSystem->lpszValue, GlobalValueBuffer);
                   }

                   // Setup the thread's stuff
                   if (SystemSetupThread (pSystem))
                      return (pSystem) ;
                   else
                      return NULL;
                }
            } else {
                // some other error was returned so pack up and leave
                pSystem->lpszValue = NULL;
                SystemFree (pSystem, TRUE);
                SetLastError (ERROR_BAD_NET_NAME); // unable to find name
                return (NULL);
            }
            
            if (pLocalPerfData != NULL) {
                MemoryFree ((LPMEMORY)pLocalPerfData);    // don't really need anything from it
            }

            // ok, so we've established that a foreign data provider
            // exists, now to finish the initialization.

            // change system name in structure to get counter names

            lstrcpy (pSystem->sysName, LocalComputerName);

            Status = GetSystemNames(pSystem);   // get counter names & explain text
            if (Status != ERROR_SUCCESS) {
                // unable to get names so bail out
                pSystem->lpszValue = NULL;
                SystemFree (pSystem, TRUE);
                SetLastError (Status);
                return (NULL) ;
            }

            // restore computer name for displays, etc.

            lstrcpy (pSystem->sysName, lpszSystemName);

            // allocate value string buffer
            pSystem->lpszValue = MemoryAllocate (TEMP_BUF_LEN*sizeof(WCHAR));
            if (!pSystem->lpszValue) {
                // unable to allocate memory
                SystemFree (pSystem, TRUE);
                SetLastError (ERROR_OUTOFMEMORY);
                return (NULL) ;
            } else {
                lstrcpy (pSystem->lpszValue, ForeignValueBuffer);
            }
        }
    } else {
        // if here, then a connection to the system's registry was established
        // so continue with the system data structure initialization

        // get counter names & explain text from local computer

        Status = GetSystemNames(pSystem);
        if (Status != ERROR_SUCCESS) {
            // unable to get names so bail out
            SystemFree (pSystem, TRUE);
            SetLastError (Status);
            return (NULL) ;
        }

        // allocate value string buffer
        pSystem->lpszValue = MemoryAllocate(TEMP_BUF_LEN*sizeof(WCHAR));

        if (!pSystem->lpszValue) {
            // unable to allocate memory
            SystemFree (pSystem, TRUE);
            SetLastError (ERROR_OUTOFMEMORY);
            return (NULL) ;
        } else {
            SetSystemValueNameToGlobal (pSystem);
        }
    }

    // initialize remaining system pointers

    pSystem->pSystemNext = NULL ;
    pSystem->FailureTime = 0;

    // setup data for thread data collection
    if (!PlayingBackLog()) {
        // create a thread for data collection
        if (!SystemSetupThread (pSystem))
           return (NULL) ;
    }

    SetLastError (ERROR_SUCCESS);

    return (pSystem) ;
}  // SystemCreate

PPERFSYSTEM
SystemGet (
    PPERFSYSTEM pSystemFirst,
    LPCTSTR lpszSystemName
)
{
    PPERFSYSTEM       pSystem ;

    if (!pSystemFirst) {
        return (NULL) ;
    }

    for (pSystem = pSystemFirst ;
         pSystem ;
         pSystem = pSystem->pSystemNext) {
        if (strsamei (pSystem->sysName, lpszSystemName)) {
            return (pSystem) ;
        }
    }  // for

    return (NULL) ;
}

PPERFSYSTEM
SystemAdd (
    PPPERFSYSTEM ppSystemFirst,
    LPCTSTR lpszSystemName,
    HWND    hDlg                    // owner window for error messages
)
{
    PPERFSYSTEM       pSystem ;
    PPERFSYSTEM       pSystemPrev = NULL;
    TCHAR             szMessage[256];
    DWORD             dwLastError;

    if (!*ppSystemFirst) {
        *ppSystemFirst = SystemCreate (lpszSystemName) ;
        dwLastError = GetLastError();
        // save return value
        pSystem = *ppSystemFirst;

    } else {
        for (pSystem = *ppSystemFirst ;
            pSystem ;
            pSystem = pSystem->pSystemNext) {
            pSystemPrev = pSystem ;
            if (strsamei (pSystem->sysName, lpszSystemName)) {
                return (pSystem) ;
            }
        }  // for

        pSystemPrev->pSystemNext = SystemCreate (lpszSystemName) ;
        // save return value
        pSystem = pSystemPrev->pSystemNext;
    }

    // display message box here if an error occured trying to add
    // this system

    if (pSystem == NULL) {
        dwLastError = GetLastError();
        if (dwLastError == ERROR_ACCESS_DENIED) {
            DlgErrorBox (hDlg, ERR_ACCESS_DENIED);
        } else {
            DlgErrorBox (hDlg, ERR_UNABLE_CONNECT);
        }
        SetLastError (dwLastError); // to propogate up to caller
    }

    return (pSystem);
}

void
SystemFree (
    PPERFSYSTEM pSystem,
    BOOL        bDeleteTheSystem
)
{  // SystemFree

    PCOUNTERTEXT pCounter, pNextCounter;

    if (!pSystem) {
        // can't proceed
        return ;
    }

    if (pSystem->sysDataKey && pSystem->sysDataKey != HKEY_PERFORMANCE_DATA) {
        // close the remote computer key
        RegCloseKey (pSystem->sysDataKey);
        pSystem->sysDataKey = 0 ;
    }

    for (pCounter = pSystem->CounterInfo.pNextTable, pNextCounter = NULL;
         pCounter;
         pCounter = pNextCounter) {
        pNextCounter = pCounter->pNextTable;
        MemoryFree (pCounter);
    }
    pSystem->CounterInfo.pNextTable = NULL ;

    if (pSystem->CounterInfo.TextString) {
        MemoryFree (pSystem->CounterInfo.TextString);
        pSystem->CounterInfo.TextString = NULL ;
    }

    if (pSystem->CounterInfo.HelpTextString) {
        MemoryFree (pSystem->CounterInfo.HelpTextString);
        pSystem->CounterInfo.HelpTextString = NULL ;
    }
    pSystem->CounterInfo.dwLastId = 0 ;
    pSystem->CounterInfo.dwHelpSize = 0 ;
    pSystem->CounterInfo.dwCounterSize = 0 ;

    if (bDeleteTheSystem) {
#if 0
        // cleanup all the data collection variables
        if (pSystem->hPerfDataEvent)
            CloseHandle (pSystem->hPerfDataEvent) ;

        if (pSystem->hStateDataMutex)
            CloseHandle (pSystem->hStateDataMutex) ;

        if (pSystem->hThread)
            CloseHandle (pSystem->hThread) ;

        if (pSystem->pSystemPerfData)
            MemoryFree (pSystem->pSystemPerfData);

        if (pSystem->lpszValue) {
            MemoryFree (pSystem->lpszValue);
            pSystem->lpszValue = NULL ;
        }
        MemoryFree (pSystem) ;
#endif
        if (pSystem->hThread) {
            // let the thread clean up memory
            PostThreadMessage (
               pSystem->dwThreadID,
               WM_FREE_SYSTEM,
               (WPARAM)0,
               (LPARAM)0) ;
        } else {
            // if no thread, clean up memory here.
            // Should not happen.
            if (pSystem->pSystemPerfData)
                MemoryFree ((LPMEMORY)pSystem->pSystemPerfData);

            if (pSystem->lpszValue) {
                MemoryFree (pSystem->lpszValue);
                pSystem->lpszValue = NULL ;
            }
            MemoryFree (pSystem) ;
        }
    }
}

void
DeleteUnusedSystems (
    PPPERFSYSTEM  ppSystemFirst ,
    int           iNoUseSystems
)
{
    PPERFSYSTEM   pPrevSys, pCurrentSys, pNextSys ;

    // delete all the marked system from the list header until
    // we hit one that is not marked
    while ((*ppSystemFirst)->bSystemNoLongerNeeded) {
       // delect from the list header
       pCurrentSys = *ppSystemFirst ;
       *ppSystemFirst = pCurrentSys->pSystemNext ;
       SystemFree (pCurrentSys, TRUE) ;
       iNoUseSystems-- ;
       if (iNoUseSystems <= 0 || !(*ppSystemFirst)) {
          // done
          break ;
       }
    }

    if (iNoUseSystems <= 0 || !(*ppSystemFirst)) {
       return ;
    }

    // now walk the list and delete each marked system
    for (pPrevSys = *ppSystemFirst, pCurrentSys = pPrevSys->pSystemNext ;
         pCurrentSys && iNoUseSystems > 0 ;
         pCurrentSys = pNextSys) {

       if (pCurrentSys->bSystemNoLongerNeeded) {
          // the current system is marked, updated the list and free
          // this system.  No need to change pPrevSys here
          pNextSys = pPrevSys->pSystemNext = pCurrentSys->pSystemNext ;
          SystemFree (pCurrentSys, TRUE) ;
          iNoUseSystems-- ;
       } else {
          // pCurrentSys is OK, update the 2 list pointers and
          // carry on looping
          pPrevSys = pCurrentSys ;
          pNextSys = pCurrentSys->pSystemNext ;
       }
    }
}

void
FreeSystems (
    PPERFSYSTEM pSystemFirst
)
{
    PPERFSYSTEM    pSystem, pSystemNext ;


    for (pSystem = pSystemFirst;
         pSystem;
         pSystem = pSystemNext) {
        pSystemNext = pSystem->pSystemNext ;
        SystemFree (pSystem, TRUE) ;
    }
}  // FreeSystems

PPERFSYSTEM
GetComputer (
    HDLG hDlg,
    WORD wControlID,
    BOOL bWarn,
    PPERFDATA *ppPerfData,
    PPERFSYSTEM *ppSystemFirst
)
/*
   Effect:        Attempt to set the current computer to the one in the
                  hWndComputers dialog edit box. If this computer system
                  can be found, load the objects, etc. for the computer
                  and set pSystem and ppPerfdata to the values for this
                  system.
*/
{  // GetComputer
    TCHAR          szComputer [MAX_SYSTEM_NAME_LENGTH + 1] ;
    PPERFSYSTEM    pSystem;
    TCHAR          tempBuffer [LongTextLen] ;
    DWORD          dwBufferSize = 0;
    LPTSTR         pBuffer = NULL ;
    DWORD          dwLastError;

    DialogText (hDlg, wControlID, szComputer) ;

    // If necessary, add the system to the lists for this view.
    pSystem = SystemGet (*ppSystemFirst, szComputer) ;
    if (!pSystem) {
        pSystem = SystemAdd (ppSystemFirst, szComputer, hDlg) ;
    }

    if (!pSystem && bWarn) {
        dwLastError = GetLastError();

        EditSetModified (GetDlgItem(hDlg, wControlID), FALSE) ;

        // unable to get specified computer so set to:
        //  the first computer in the system list if present 
        //      -- or --
        //  set he local machine if not.

        pSystem = *ppSystemFirst;   // set to first in list

        if (pSystem == NULL) {
            // this would mean the user can't access the local
            // system since normally that would be the first one
            // so the machine name will be restored to the 
            // local machine (for lack of a better one) but the
            // system won't be added unless they want to explicitly

            DialogSetString (hDlg, wControlID, LocalComputerName) ;
        } else {
            // set to name in system structure 
            DialogSetString (hDlg, wControlID, pSystem->sysName);
        }
        
        if (dwLastError != ERROR_ACCESS_DENIED) {
            DlgErrorBox (hDlg, ERR_COMPUTERNOTFOUND) ;
        } else {
            // the appropriate error message has already been displayed
        }

        SetFocus (DialogControl(hDlg, wControlID)) ;
    }

    if (pSystem) {
        if (PlayingBackLog ()) {
            *ppPerfData =
            LogDataFromPosition (pSystem, &(PlaybackLog.StartIndexPos)) ;
        } else {
            if (pSystem->lpszValue) {
               // save the previous lpszValue string before
               // SetSystemValueNameToGlobal mess it up
               dwBufferSize = MemorySize (pSystem->lpszValue) ;
               if (dwBufferSize <= sizeof(tempBuffer)) {
                  pBuffer = tempBuffer ;
               } else {
                  pBuffer = MemoryAllocate (dwBufferSize) ;
               }
               memcpy (pBuffer, pSystem->lpszValue, dwBufferSize) ;
            }

            SetSystemValueNameToGlobal (pSystem);
            UpdateSystemData (pSystem, ppPerfData) ;

            if (pSystem->lpszValue) {
               // retore the previous lpszValue string
               memcpy (pSystem->lpszValue, pBuffer, dwBufferSize) ;
               if (pBuffer != tempBuffer) {
                  MemoryFree (pBuffer) ;
               }
            }
        }
    }
    return (pSystem) ;

}  // GetComputer