#define _WIN32_WINNT 0x0501
#include <jni.h>
#include <Windows.h>
#include <stdio.h>
#include <Winuser.h>
#include <iphlpapi.h>
#include <IPIfCons.h>
#include <iptypes.h>
#include <assert.h>
#include "WindowsSystemParameters.h"


/*START: Used for getCPUUsage function */
#define SystemBasicInformation		 0
#define SystemPerformanceInformation 2
#define SystemTimeInformation		 3
#define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 + (double)((x).LowPart))

char mac_address[10];
static char MACData[10];

typedef struct
{
	DWORD	dwUnknown1;
	ULONG	uKeMaximumIncrement;
	ULONG	uPageSize;
	ULONG	uMmNumberOfPhysicalPages;
	ULONG	uMmLowestPhysicalPage;
	ULONG	uMmHighestPhysicalPage;
	ULONG	uAllocationGranularity;
	PVOID	pLowestUserAddress;
	PVOID	pMmHighestUserAddress;
	ULONG	uKeActiveProcessors;
	BYTE	bKeNumberProcessors;
	BYTE	bUnknown2;
	WORD	wUnknown3;
} SYSTEM_BASIC_INFORMATION;

typedef struct
{
	LARGE_INTEGER	liIdleTime;
	DWORD			dwSpare[76];
} SYSTEM_PERFORMANCE_INFORMATION;

typedef struct
{
	LARGE_INTEGER liKeBootTime;
	LARGE_INTEGER liKeSystemTime;
	LARGE_INTEGER liExpTimeZoneBias;
	ULONG		  uCurrentTimeZoneId;
	DWORD		  dwReserved;
} SYSTEM_TIME_INFORMATION;


/*END: Used for getCPUUsage function */
JNIEXPORT jdouble JNICALL Java_WindowsSystemParameters_getDisk(JNIEnv *env, jobject obj)
{

  ULARGE_INTEGER freebytes;
  ULARGE_INTEGER totalbytes;
  ULARGE_INTEGER totalfreebytes;
  long int freemegs;
  int success;
  
  success = GetDiskFreeSpaceEx(NULL, &freebytes, &totalbytes, &totalfreebytes);
  if(!success) {
    return -1;
  }
  
  // convert to MB
  freemegs = freebytes.QuadPart/(1024*1024);
  return ((double)freemegs);
}

JNIEXPORT jdouble JNICALL Java_WindowsSystemParameters_getIdleTime(JNIEnv *env, jobject obj)

{

  LASTINPUTINFO lastInput;
  lastInput.cbSize = sizeof(LASTINPUTINFO);
  
  // subtract the time for the last input  
  if(GetLastInputInfo(&lastInput))
    return (GetTickCount() - lastInput.dwTime);
  
  return -1;
}






/*
	Checks and sets CPU usage
	code taken from URL below

	NtQuerySystemInformation iss available for use in Windows 2000 and Windows XP. 
	It may be altered or unavailable in subsequent versions. Applications should 
	use the alternate functions listed in this topic.
	http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/getsysteminfo.asp

	sleep for 1 second and calculate usage based on idle time 
*/
//http://stage.itp.nyu.edu/~ms1671/spring2003/sculpting/final/cpusage.cpp

JNIEXPORT jdouble JNICALL Java_WindowsSystemParameters_getCpu
(JNIEnv *env, jobject obj)
 {

  SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
  SYSTEM_TIME_INFORMATION SysTimeInfo;
  SYSTEM_BASIC_INFORMATION SysBaseInfo;
  double dbIdleTime;
  double dbSystemTime;
  LONG stat;
  LARGE_INTEGER liOldIdleTime = {0,0};
  LARGE_INTEGER liOldSystemTime = {0,0};
  int i;
  double avgtime = 0;
  typedef LONG (WINAPI *PROCNTQSI)(UINT,PVOID,ULONG,PULONG);
  
  PROCNTQSI NtQuerySystemInformation;
  NtQuerySystemInformation = (PROCNTQSI)GetProcAddress(
						       GetModuleHandle("ntdll"),
						       "NtQuerySystemInformation"
						       );
  
  stat = NtQuerySystemInformation(SystemBasicInformation,&SysBaseInfo,sizeof(SysBaseInfo),NULL);
  if (stat != NO_ERROR) {
    return -1;
  }
  
  for(i = 0; i < 2; i++)
    {
      // get new system time		
      stat = NtQuerySystemInformation(SystemTimeInformation,&SysTimeInfo,sizeof(SysTimeInfo),0);
      if (stat!=NO_ERROR)
	return -1;
      
      // get new CPU's idle time
      stat = NtQuerySystemInformation(SystemPerformanceInformation,&SysPerfInfo,sizeof(SysPerfInfo),NULL);
      if (stat != NO_ERROR)
	return -1;
      
      // if it's a first call - skip it
      if (liOldIdleTime.QuadPart != 0)
	{
	  // CurrentValue = NewValue - OldValue
	  dbIdleTime = Li2Double(SysPerfInfo.liIdleTime) - Li2Double(liOldIdleTime);
	  dbSystemTime = Li2Double(SysTimeInfo.liKeSystemTime) - Li2Double(liOldSystemTime);
	  
	  // CurrentCpuIdle = IdleTime / SystemTime
	  dbIdleTime = dbIdleTime / dbSystemTime;
	  
	  // CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors
	  dbIdleTime = 100.0 - dbIdleTime * 100.0 / (double)SysBaseInfo.bKeNumberProcessors;
	}
      // store new CPU's idle and system time
      liOldIdleTime = SysPerfInfo.liIdleTime;
      liOldSystemTime = SysTimeInfo.liKeSystemTime;
      
      // wait one second
      Sleep(1000);
    }
  return dbIdleTime;
}


JNIEXPORT jint JNICALL Java_WindowsSystemParameters_getAc
  (JNIEnv *env, jobject obj)
{
  SYSTEM_POWER_STATUS status;


  if(GetSystemPowerStatus(&status)) {
    return ((int)status.ACLineStatus);    
  }

  return -1;
}


JNIEXPORT jlong JNICALL Java_WindowsSystemParameters_getMaxBatteryCap(JNIEnv *env, jobject obj)
{
	
	HMODULE hPowerFunctions = LoadLibrary("Powrprof.dll");
	typedef LRESULT (WINAPI * CALLNTPOWERINFORMATION)(POWER_INFORMATION_LEVEL, PVOID, 
                 ULONG, PVOID, ULONG);
	CALLNTPOWERINFORMATION pinfo = (CALLNTPOWERINFORMATION)GetProcAddress(hPowerFunctions, "CallNtPowerInformation");
	SYSTEM_BATTERY_STATE status;

	pinfo(5, NULL, 0, &status, sizeof(status));
	return status.MaxCapacity;
}

JNIEXPORT jdouble JNICALL Java_WindowsSystemParameters_getBatteryCapRemaining(JNIEnv *env, jobject obj)
{
  
	HMODULE hPowerFunctions = LoadLibrary("Powrprof.dll");
	typedef LRESULT (WINAPI * CALLNTPOWERINFORMATION)(POWER_INFORMATION_LEVEL, PVOID, 
                 ULONG, PVOID, ULONG);
	CALLNTPOWERINFORMATION pinfo = (CALLNTPOWERINFORMATION)GetProcAddress(hPowerFunctions, "CallNtPowerInformation");
	SYSTEM_BATTERY_STATE status;

	pinfo(5, NULL, 0, &status, sizeof(status));
	return status.RemainingCapacity;
}

/*
 * Class:     monitor_system_WindowsXP
 * Method:    sysGetRemainingCap
 * Signature: ()J
 */
JNIEXPORT jint JNICALL Java_WindowsSystemParameters_getBatteryRemaining(JNIEnv *env, jobject obj)
 {
	SYSTEM_POWER_STATUS status;
    if(GetSystemPowerStatus(&status)) {
     return ((int)status.BatteryLifePercent);    
    }
   return -1;

}

/*
 * Class:     WindowsSystemParameters
 * Method:    getMACAddress
 * Signature: ()D
 */

JNIEXPORT jstring  JNICALL Java_WindowsSystemParameters_getMACAddress
  (JNIEnv *env, jobject obj)
{	
  int i = 2;
   jstring mac;
 
   // Fetches the MAC address and prints it
  IP_ADAPTER_INFO AdapterInfo[16];       // Allocate information
               
  // for up to 16 NICs

  DWORD dwBufLen = sizeof(AdapterInfo);  // Save memory size of buffer
	
  DWORD dwStatus = GetAdaptersInfo(      // Call GetAdapterInfo
    AdapterInfo,                 // [out] buffer to receive data
    &dwBufLen);                 // [in] size of receive data buffer
  //assert(dwStatus == ERROR_SUCCESS);  // Verify return value is
                                      // valid, no buffer overflow



  PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo; // Contains pointer to


  sprintf(MACData,"%02X:%02X:%02X:%02X:%02X:%02X", 
		pAdapterInfo->Address[0], pAdapterInfo->Address[1], pAdapterInfo->Address[2], pAdapterInfo->Address[3], 
		pAdapterInfo->Address[4], pAdapterInfo->Address[5]);
 return ((*env)->NewStringUTF(env,MACData));
 }







