///////////////////////////////////////////////////////////////////////////////
/// @file deMachineWindows.cpp
///
/// @brief Windows System configuration analyzer
///
/// @author Lightning
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Nov 2001
/// @author Lightning
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////




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

#include <windows.h> // for various stuff
#include "deMachineWindows.hpp"
#include "deMachineWindowsCPU.hpp"



//=================================================================================
// deMachineWindows::deMachineWindows()
//
// Constructor 
//=================================================================================
deMachineWindows::deMachineWindows()
{
   deMachineWindowsCPU *CPUDetector;


   CreationResult = deFALSE;
   CPUDetector = NULL;
//   SystemInfo.DriveInfo = NULL;

   CPUDetector = new deMachineWindowsCPU();

   if (!CPUDetector)
   {
      // error - CPUDetector create failed
      return;
   }

   if (!CPUDetector->GetCreationResult())
   {
      // error - CPUDetector create failed
      delete CPUDetector;
      return;
   }

   if (!CPUDetector->GetCPUInfo(&SystemInfo.CPUInfo))
   {
      // error - GetCPUInfo failed
      delete CPUDetector;
      return;
   }

   delete CPUDetector;

   if (!GetOSInfo())
   {
      // error - GetOSInfo failed
      return;
   }

   if (!GetMemoryInfo())
   {
      // error - GetMemoryInfo failed
      return;
   }

   if (!GetDXInfo())
   {
      // error - GetDXInfo failed
      return;
   }


   if (!GetDriveInfo())
   {
      // error - GetDriveInfo failed
      return;
   }

   CreationResult = deTRUE;
   return;
}



//=================================================================================
// deMachineWindows::GetOSInfo()
//
// Get Windows OS specifics 
//=================================================================================
deBoolean deMachineWindows::GetOSInfo(void)
{
   OSVERSIONINFO OSVersionInfo;
   char CharDecimal[10];
   char CharValue[10];
   float DecimalValue = 0.0f;
   char *EndPtr;


   SystemInfo.OSInfo.OSType = deOSInfo::DEOSTYPE_UNKNOWN;
   SystemInfo.OSInfo.Version = 0.0f;
   SystemInfo.OSInfo.Build = 0;
   SystemInfo.OSInfo.AdditionalInfo[0] = NULL;

   OSVersionInfo.dwOSVersionInfoSize = sizeof(OSVersionInfo);

   if (!GetVersionEx(&OSVersionInfo))
   {
      // error - GetVersionEx failed
      return deFALSE;
   }

   CharDecimal[0] = NULL;

   strcat(CharDecimal, ".");   
   itoa(OSVersionInfo.dwMinorVersion, CharValue, 10);
   strcat(CharDecimal, CharValue);
   DecimalValue = (float)strtod(CharDecimal, &EndPtr);

   SystemInfo.OSInfo.Version = (float)OSVersionInfo.dwMajorVersion;
   SystemInfo.OSInfo.Version = SystemInfo.OSInfo.Version + DecimalValue;
   SystemInfo.OSInfo.Build = OSVersionInfo.dwBuildNumber;
   
   strcpy(SystemInfo.OSInfo.AdditionalInfo, OSVersionInfo.szCSDVersion);

   if (SystemInfo.OSInfo.Version == 3.51f)
      SystemInfo.OSInfo.OSType = deOSInfo::DEOSTYPE_WINDOWSNT;

   if (SystemInfo.OSInfo.Version == 4.0f)
   {
      if (OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
         SystemInfo.OSInfo.OSType = deOSInfo::DEOSTYPE_WINDOWS95;
      else
         SystemInfo.OSInfo.OSType = deOSInfo::DEOSTYPE_WINDOWSNT;
   }

   if (SystemInfo.OSInfo.Version == 4.1f)
      SystemInfo.OSInfo.OSType = deOSInfo::DEOSTYPE_WINDOWS98;

   if (SystemInfo.OSInfo.Version == 4.9f)
      SystemInfo.OSInfo.OSType = deOSInfo::DEOSTYPE_WINDOWSME;

   if (SystemInfo.OSInfo.Version == 5.0f)
      SystemInfo.OSInfo.OSType = deOSInfo::DEOSTYPE_WINDOWS2000;

   if (SystemInfo.OSInfo.Version == 5.1f)
      SystemInfo.OSInfo.OSType = deOSInfo::DEOSTYPE_WINDOWSXP;

   return deTRUE;
}



//=================================================================================
// deMachineWindows::GetMemoryInfo()
//
// Get memory specifics 
//=================================================================================
deBoolean deMachineWindows::GetMemoryInfo(void)
{
   MEMORYSTATUS MemoryInfo;

/*   MEMORYSTATUSEX MemoryInfoEX;


   if (SystemInfo.OSInfo.Version >= 5.0f)
   {
      MemoryInfoEX.dwLength = sizeof(MemoryInfoEX);

      GlobalMemoryStatusEx(&MemoryInfoEX);

      SystemInfo.MemoryInfo.MemoryLoad = MemoryInfoEX.dwMemoryLoad;
      SystemInfo.MemoryInfo.TotalPhysical = MemoryInfoEX.ullTotalPhys;
      SystemInfo.MemoryInfo.AvailablePhysical = MemoryInfoEX.ullAvailPhys;
      SystemInfo.MemoryInfo.TotalPageFile = MemoryInfoEX.ullTotalPageFile;
      SystemInfo.MemoryInfo.AvailablePageFile = MemoryInfoEX.ullAvailPageFile;
      SystemInfo.MemoryInfo.TotalVirtual = MemoryInfoEX.ullTotalVirtual;
      SystemInfo.MemoryInfo.AvailableVirtual = MemoryInfoEX.ullAvailVirtual;
      SystemInfo.MemoryInfo.AvailableExtendedVirtual = MemoryInfoEX.ullAvailExtendedVirtual;
   }
   else
   {   */
      GlobalMemoryStatus(&MemoryInfo);

      SystemInfo.MemoryInfo.MemoryLoad = MemoryInfo.dwMemoryLoad;
      SystemInfo.MemoryInfo.TotalPhysical = MemoryInfo.dwTotalPhys;
      SystemInfo.MemoryInfo.AvailablePhysical = MemoryInfo.dwAvailPhys;
      SystemInfo.MemoryInfo.TotalPageFile = MemoryInfo.dwTotalPageFile;
      SystemInfo.MemoryInfo.AvailablePageFile = MemoryInfo.dwAvailPageFile;
      SystemInfo.MemoryInfo.TotalVirtual = MemoryInfo.dwTotalVirtual;
      SystemInfo.MemoryInfo.AvailableVirtual = MemoryInfo.dwAvailVirtual;
      SystemInfo.MemoryInfo.AvailableExtendedVirtual = 0;
//   }

   return deTRUE;
}



//=================================================================================
// deMachineWindows::GetDXInfo()
//
// Get DirectX version
//=================================================================================
deBoolean deMachineWindows::GetDXInfo(void)
{
   HKEY HKey;
   long RetValue;
   char DXVersion[20];
   long DXVersionSize = sizeof(DXVersion);
   char *StrPtr1 = NULL;
   char *StrPtr2 = NULL;
   char Number[5];	//up to 4 digits in each area


   SystemInfo.DXInfo.MajorVersion = 0;
   SystemInfo.DXInfo.MinorVersion = 0;
   SystemInfo.DXInfo.StepVersion = 0;

	memset(DXVersion, 0, sizeof(DXVersion));
	memset(Number, 0, sizeof(Number));

	RetValue = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\DirectX", 0, KEY_READ, &HKey);

	if (RetValue)
   {
      // error - RegOpenKeyEx failed
      return deFALSE;
   }

	RetValue = RegQueryValueEx(HKey, "Version", 0, NULL, (unsigned char *)&DXVersion[0], (unsigned long *)&DXVersionSize);

	if(RetValue)
	{
		RegCloseKey(HKey);
      // error - RegQueryValueEx failed
      return deFALSE;
	}

	RegCloseKey(HKey);

	// Parse up DXVersion and put it into the struct
	StrPtr1 = strchr(DXVersion, (char)'.');

	if(!StrPtr1)
   {
      // error - Parse failed
      return deFALSE;
   }

	//goto the next spot after the "."
	StrPtr1++;

	StrPtr2 = strchr(StrPtr1, (char)'.');

	if(!StrPtr2)
   {
      // error - Parse failed
      return deFALSE;
   }

	strncpy(Number, StrPtr1, StrPtr2 - StrPtr1);

	SystemInfo.DXInfo.MajorVersion = atoi(Number);

	memset(Number, 0, sizeof(Number));

	//set pointer for the character after the number
	StrPtr1 = StrPtr2 + 1;

	//if we are pass the end of the string we need to bail
	if((StrPtr1 - DXVersion) > (long)strlen(DXVersion))
   {
      // error - Second parse failed
      return deFALSE;
   }

	//grab the next . after the number, there should be another
	StrPtr2 = strchr(StrPtr1, (char)'.');

	if(!StrPtr2)
   {
      // error - Second parse failed
      return deFALSE;
   }

	//Pull the number out of the string
	strncpy(Number, StrPtr1, StrPtr2 - StrPtr1);

	SystemInfo.DXInfo.MinorVersion = atoi(Number);

	memset(Number, 0, sizeof(Number));
	
	//set pointer for the character after the number
	StrPtr1 = StrPtr2 + 1;

	//if we are pass the end of the string we need to bail
	if((StrPtr1 - DXVersion) > (long)strlen(DXVersion))
   {
      // error - Third parse failed
      return deFALSE;
   }

	//we shouldn't have another ".", just verify
	StrPtr2 = strchr(StrPtr1, (char)'.');

	if(StrPtr2)
   {
      // error - Third parse failed
      return deFALSE;
   }

	//take the current StrPtr1 spot, subtract the DXVersion value
	//so we know how many characters we are up to then subtract that from the total
	//length of the string so we grab just the end characters
	strncpy(Number, StrPtr1, (long)strlen(DXVersion) - (StrPtr1 - DXVersion));

	SystemInfo.DXInfo.StepVersion = atoi(Number);

	return deTRUE;
}



//=================================================================================
// deMachineWindows::GetDriveInfo()
//
// Get drives info for machine
//=================================================================================
deBoolean deMachineWindows::GetDriveInfo(void)
{
   deDriveInfo DriveInfo;
   long Drives = 0;
   int Counter = 0;
   char DriveLetter[] = "A:\\";
   long DriveType = 0;
   unsigned long SectorsPerCluster = 0;
   unsigned long BytesPerSector = 0;
   unsigned long NumFreeClusters = 0;
   unsigned long NumClusters = 0;
   unsigned int DriveCount = 0;


   //grab a bit list listing all the drives we have
   Drives = GetLogicalDrives();

	if(!Drives)
   {
      // error - GetLogicalDrives failed
      return deFALSE;
   }

//   SystemInfo.DriveInfo = new deDynamicArray(sizeof(deDriveInfo), 1);

	//loop thru the drives we have and get the information about them
	for(Counter = 0; Counter < 26; Counter++)
	{
		if((Drives & 1) == 0)
		{
			Drives = Drives >> 1;
			continue;
		}

		Drives = Drives >> 1;

      DriveInfo.TotalSize = 0;
      DriveInfo.FreeSize = 0;
      DriveInfo.Drive[0] = NULL;
      DriveInfo.Type = deDriveInfo::DEDRIVE_UNKNOWN;
      
		//grab the type
		DriveLetter[0] = (char)(65 + Counter);
		DriveType = GetDriveType(DriveLetter);
      strcpy(DriveInfo.Drive, DriveLetter);

      switch (DriveType)
      {
         case DRIVE_UNKNOWN:
            DriveInfo.Type = deDriveInfo::DEDRIVE_UNKNOWN;
            break;

         case DRIVE_NO_ROOT_DIR:
            DriveInfo.Type = deDriveInfo::DEDRIVE_NO_ROOT_DIR;
            break;

         case DRIVE_REMOVABLE:
            DriveInfo.Type = deDriveInfo::DEDRIVE_REMOVABLE;
            break;

         case DRIVE_FIXED:
            DriveInfo.Type = deDriveInfo::DEDRIVE_FIXED;
            break;

         case DRIVE_REMOTE:
            DriveInfo.Type = deDriveInfo::DEDRIVE_REMOTE;
            break;

         case DRIVE_CDROM:
            DriveInfo.Type = deDriveInfo::DEDRIVE_CDROM;
            break;

         case DRIVE_RAMDISK:
            DriveInfo.Type = deDriveInfo::DEDRIVE_RAMDISK;
            break;

         default:
            DriveInfo.Type = deDriveInfo::DEDRIVE_UNKNOWN;
            break;
      }


		//if the drive is a local drive or a ram drive then grab the space. The reason
		//we do not grab the space on network drives is that it could take awhile if the
		//drive has not already been accessed and we don't want to wait for that
		if(DriveInfo.Type == deDriveInfo::DEDRIVE_FIXED || DriveInfo.Type == deDriveInfo::DEDRIVE_RAMDISK)
		{
			//check and see if we can call the better function in getting disk space
			//as Windows 95 before OSR2 doesn't support the GetDiskFreeSpaceEx function

			if(GetProcAddress(GetModuleHandle("kernel32.dll"), "GetDiskFreeSpaceExA"))
			{
				GetDiskFreeSpaceEx(DriveLetter, (PULARGE_INTEGER)&DriveInfo.FreeSize, (PULARGE_INTEGER)&DriveInfo.TotalSize, NULL);
			}
			else
			{
				GetDiskFreeSpace(DriveLetter, &SectorsPerCluster, &BytesPerSector, &NumFreeClusters, &NumClusters);
				DriveInfo.FreeSize = (__int64)NumFreeClusters * SectorsPerCluster * BytesPerSector;
				DriveInfo.TotalSize = (__int64)NumClusters * SectorsPerCluster * BytesPerSector;
			}
		}

		SystemInfo.DriveInfo.Resize(DriveCount + 1);
		SystemInfo.DriveInfo[DriveCount] = DriveInfo;

      DriveCount++;
	}

	return deTRUE;
}



//=================================================================================
// deMachineWindows::~deMachineWindows()
//
// Destructor 
//=================================================================================
deMachineWindows::~deMachineWindows()
{
//   if (SystemInfo.DriveInfo)
//     delete SystemInfo.DriveInfo;
}



//=================================================================================
// deMachineWindows::GetSystemInfo()
//
// Set the SystemInfo from what is stored
//=================================================================================
const deSystemInfo * deMachineWindows::GetSystemInfo()
{
   return &SystemInfo; 
}


