/**********************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    IBM Corporation - Initial API and implementation
 **********************************************************************/

# include <update.h>
# include <windows.h>
# include <winioctl.h> // IOCTL codes: MediaType

// Windows Version
int WIN98 = 0;
int WINNT = 1;
int WINME = 2;
int WIN2000 = 3;
int WINXP = 4;

// set to 1 for DEBUG
int DEBUG = 0;

// set to 0 to run on Windows 95 *Unsupported*
int NOWIN95 = 1;


// GLOBAL METHODS
// ---------------

/*
 *
 */
jstring WindowsTojstring( JNIEnv* jnienv, char* buf )
{
  jstring rtn = 0;
  wchar_t* buffer = 0;
  int bufferLen = strlen(buf);  
  if( bufferLen == 0 ){
    rtn = jnienv ->NewStringUTF(buf);
	if (DEBUG)
		printf("WindowsToJString Buffer is empty\n");    
  } else {
    int length = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)buf, bufferLen, NULL, 0 );
    buffer = (wchar_t*)malloc( length*2 + 1 );
    if(int err=MultiByteToWideChar( CP_ACP, 0, (LPCSTR)buf, bufferLen, (LPWSTR)buffer, length ) >0 ){
      rtn = jnienv->NewString((jchar*)buffer, length );
    } else {
		if (DEBUG)
			printf("MultiByteToWideChar %i\n",err);    
    }
  }
  if( buffer )
   free( buffer );
  return rtn;
}



/*
 * calls GetVolumeInformation to retrive the label of the volume
 * Returns NULL if an error occurs
 * @param driveLetter path to the drive "c:\\"
 * @prama jnienv JNIEnvironment
 */
jstring getLabel(TCHAR driveLetter[],JNIEnv * jnienv){

	jstring result = NULL;
	TCHAR buf[128];	
	
	// always return null as UNICODE is not implemented
	// how can we get the label of a volume as UNICODE char ?
	return result;
	
	int err = GetVolumeInformation(
		driveLetter,
		buf,
		sizeof(buf) - 1,
		NULL,
		NULL,
		NULL,
		NULL,
		0);
	if (err){
		result = WindowsTojstring(jnienv, buf);
	} else {
		if (DEBUG)
			printf("Error GetVolumeInformation %i\n",err);				
 	}
	return result;
}

/*
 * returns the Version of Windows
 * int 0 WIN98;
 * int 1 WINNT;
 * int 2 WINME;
 * int 3 WIN2000;
 * int 4 WINXP;
 * returns -1 otherwise
 */
int getWindowsVersion(){
	OSVERSIONINFOEX osvi;
	int UNKNOWN = -1;
	
	ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
	
	if(!(GetVersionEx((OSVERSIONINFO *)&osvi))){
		if (DEBUG)
			printf("UNKNOWN VERSION: Cannot execute GetVersionEx\n");
		// if OSVERSIONEX doesn't work, try OSVERSIONINFO
		osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);			 	
		if(!(GetVersionEx((OSVERSIONINFO *)&osvi))){
		if (DEBUG)
			printf("UNKNOWN VERSION: Cannot execute GetVersionEx\n");
			return UNKNOWN;
		}
	}
	
	switch(osvi.dwPlatformId){
		case VER_PLATFORM_WIN32_NT:
			if (DEBUG)
				printf("VERSION NT: Maj %i Min %i\n",osvi.dwMajorVersion,osvi.dwMinorVersion);				 	
			if(osvi.dwMajorVersion<=4)
				return WINNT;
			if(osvi.dwMajorVersion==5){
				if (osvi.dwMinorVersion==0)
					return WIN2000;
				if (osvi.dwMinorVersion==1)
					return WINXP;
			} else {
				return UNKNOWN;
			};
			break;
		case VER_PLATFORM_WIN32_WINDOWS:
			if (DEBUG)
				printf("VERSION Non NT: Maj %i Min %i\n",osvi.dwMajorVersion,osvi.dwMinorVersion);				 	
			if(osvi.dwMajorVersion==4){
				if (osvi.dwMinorVersion==10)
					return WIN98;
				if (osvi.dwMinorVersion==90)
					return WINME;
			} else {
				return UNKNOWN;
			}
			break;
		default:
			if (DEBUG)
				printf("VERSION UNKNOWN: Maj %i Min %i\n",osvi.dwMajorVersion,osvi.dwMinorVersion);				 	
			return UNKNOWN;
	}
	return UNKNOWN;
} 

/*
 * Returns the type of Removable Drive 
 * Returns 
 * org_eclipse_update_configuration_LocalSystemInfo_VOLUME_FLOPPY_3
 * org_eclipse_update_configuration_LocalSystemInfo_VOLUME_FLOPPY_5
 * org_eclipse_update_configuration_LocalSystemInfo_VOLUME_REMOVABLE
 */
jlong getFloppy(TCHAR driveLetter[]){

	TCHAR floppyPath[8];
	HANDLE handle;
	DISK_GEOMETRY geometry[20];
	DWORD dw;
	jlong UNKNOWN = org_eclipse_update_configuration_LocalSystemInfo_VOLUME_REMOVABLE;

	sprintf(floppyPath, "\\\\.\\%c:", driveLetter[0]);
	if (DEBUG)
		printf("Path %s\n",floppyPath);
	// BUG 25719
    SetErrorMode( SEM_FAILCRITICALERRORS ); 		
	handle=CreateFile(floppyPath,0,FILE_SHARE_READ,NULL,OPEN_ALWAYS,0,NULL);
	if (handle==INVALID_HANDLE_VALUE){
		if (DEBUG)
			printf("Invalid Handle %s\n",floppyPath);
		return UNKNOWN;
	} else {
		if(DeviceIoControl(handle,
			IOCTL_DISK_GET_MEDIA_TYPES,0,0,
			geometry,sizeof(geometry),&dw,0) 
			&& dw>0) {
			switch(geometry[0].MediaType){
			 case F5_160_512:
			 case F5_180_512:
			 case F5_320_512:
			 case F5_320_1024:
			 case F5_360_512:
			 case F5_1Pt2_512:
				if (DEBUG)
					printf("Found 5 1/4 Drive\n");				 	
			 	return org_eclipse_update_configuration_LocalSystemInfo_VOLUME_FLOPPY_5;
			 case F3_720_512:			 				 				 				 				 
			 case F3_1Pt44_512:
			 case F3_2Pt88_512:
			 case F3_20Pt8_512:				 				 				 
				if (DEBUG)
					printf("Found 3 1/2 Drive\n");				 	
			 	return org_eclipse_update_configuration_LocalSystemInfo_VOLUME_FLOPPY_3;
			 default:
			 	return UNKNOWN;
			}
		}
	}
	return UNKNOWN;	
}

/*
 * Returns the UNC name of a remote drive
 * (\\Machine\path\path1\path2$)
 * returns NULL if an error occurs
 */
 jstring getRemoteNetworkName(TCHAR driveLetter[],JNIEnv * jnienv){
 	
 	unsigned long size =256;
 	TCHAR buf[256];	
 	TCHAR drivePath[2];
 	DWORD err;
 	jstring result = NULL;
 	
 	// always return NULL as UNICODE not implemented
	// how can we get the label of a remote network name as UNICODE char ? 	
 	return result;
 	
	sprintf(drivePath, "%c:", driveLetter[0]); 	
 	err = WNetGetConnection(drivePath,buf,&size);
 	
 	if (err==WN_SUCCESS){
		result = WindowsTojstring(jnienv,buf);
	} else {
		if (DEBUG)
			printf("Error WNEtGetConnection %i",err);				
 	}
	return result;
 }


// JNI METHODS
// ---------------

/*
 * Class:     org_eclipse_update_configuration_LocalSystemInfo
 * Method:    nativeGetFreeSpace
 * Signature: (Ljava/io/File;)J
 */
JNIEXPORT jlong JNICALL Java_org_eclipse_update_configuration_LocalSystemInfo_nativeGetFreeSpace(
	JNIEnv * jnienv,
	jclass javaClass,
	jobject file) {

	// to retrive the String
	jclass cls;
	jmethodID id;
	jobject obj;

	// java.io.File.getAbsolutePath()
	const TCHAR * lpDirectoryName;

	// Windows Parameters
	__int64 i64FreeBytesAvailableToCaller;
	__int64 i64TotalNumberOfBytes;
	__int64 i64TotalNumberOfFreeBytes;

	// the result
	jlong result = org_eclipse_update_configuration_LocalSystemInfo_SIZE_UNKNOWN;

	// first, obtain the Path from the java.io.File parameter
	cls = jnienv -> GetObjectClass(file);
	id = jnienv -> GetMethodID(cls, "getAbsolutePath", "()Ljava/lang/String;");
	obj = jnienv -> CallObjectMethod(file, id);
	lpDirectoryName = jnienv -> GetStringUTFChars((jstring) obj, 0);
	if (DEBUG)
		printf("Directory: [%s]\n",lpDirectoryName);

	if (int win = (int)getWindowsVersion()<0 && NOWIN95){
		// windows 95 or other
		if (DEBUG)
			printf("Unsupported Windows: %i\n",win);		
		return result;
	}

	int err = GetDiskFreeSpaceEx(
				lpDirectoryName,
				(PULARGE_INTEGER) & i64FreeBytesAvailableToCaller,
				(PULARGE_INTEGER) & i64TotalNumberOfBytes,
				(PULARGE_INTEGER) & i64TotalNumberOfFreeBytes);
			
	if (err) {
		result = (jlong) i64FreeBytesAvailableToCaller;
	}

	return result;
}

/*
 * Class:     org_eclipse_update_configuration_LocalSystemInfo
 * Method:    nativeGetLabel
 * Signature: (Ljava/io/File;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_org_eclipse_update_configuration_LocalSystemInfo_nativeGetLabel(
	JNIEnv * jnienv,
	jclass javaClass,
	jobject file) {

	// to retrive the String
	jclass cls;
	jmethodID id;
	jobject obj;

	// java.io.File.getAbsolutePath()
	const TCHAR * lpDirectoryName;

	// obtain the String from the parameter
	cls = jnienv -> GetObjectClass(file);
	id = jnienv -> GetMethodID(cls, "getAbsolutePath", "()Ljava/lang/String;");
	obj = jnienv -> CallObjectMethod(file, id);
	lpDirectoryName = jnienv -> GetStringUTFChars((jstring) obj, 0);
	if (DEBUG)
		printf("Directory: [%s]\n",lpDirectoryName);

	//
	jstring result = NULL;
	int floppy;
	
	if (int win = (int)getWindowsVersion()<0 && NOWIN95){
		// windows 95 or other
		if (DEBUG)
			printf("Unsupported Windows: %i\n",win);		
		return result;
	}
		
	// Make sure we have a String of the Form: <letter>:
	if (':' == lpDirectoryName[1]) {
		TCHAR driveLetter[4]; // i.e. -> C:\\
		memcpy(driveLetter, lpDirectoryName, 2);
		strcpy(driveLetter + 2, "\\");
		switch (GetDriveType(driveLetter)) {
			case DRIVE_REMOTE :
				// check name of machine and path of remote
				if (DEBUG)
					printf("Remote Drive");
				result = getRemoteNetworkName(driveLetter,jnienv);				
				break;
			default :
				if (DEBUG)
					printf("Another Drive at %s", driveLetter);
				result = getLabel(driveLetter,jnienv);
				break;
		} 
	} 

	return result;
}

/*
 * Class:     org_eclipse_update_configuration_LocalSystemInfo
 * Method:    nativeGetType
 * Signature: (Ljava/io/File;)I
 */
JNIEXPORT jint JNICALL Java_org_eclipse_update_configuration_LocalSystemInfo_nativeGetType(
	JNIEnv * jnienv,
	jclass javaClass,
	jobject file) {

	// to retrive the String
	jclass cls;
	jmethodID id;
	jobject obj;

	// java.io.File.getAbsolutePath()
	const TCHAR * lpDirectoryName;

	// obtain the String from the parameter
	cls = jnienv -> GetObjectClass(file);
	id = jnienv -> GetMethodID(cls, "getAbsolutePath", "()Ljava/lang/String;");
	obj = jnienv -> CallObjectMethod(file, id);
	lpDirectoryName = jnienv -> GetStringUTFChars((jstring) obj, 0);
	if (DEBUG)
		printf("Directory: [%s]\n",lpDirectoryName);

	int result = org_eclipse_update_configuration_LocalSystemInfo_VOLUME_UNKNOWN;
	if (int win = (int)getWindowsVersion()<0 && NOWIN95){
		// windows 95 or other
		if (DEBUG)
			printf("Unsupported Windows: %i\n",win);		
		return result;
	}
	
	// Make sure we have a String of the Form: <letter>:
	if (':' == lpDirectoryName[1]) {
		TCHAR driveLetter[4]; //C:\\
		memcpy(driveLetter, lpDirectoryName, 2);
		strcpy(driveLetter + 2, "\\");

		switch (GetDriveType(driveLetter)) {
			case DRIVE_REMOVABLE :
				// check if floppy 3.5, floppy 5.25	
				// or other removable device (USB,PCMCIA ...)
				if (DEBUG)
					printf("Removable Device");
				result = getFloppy(driveLetter);					
				break;
			case DRIVE_CDROM :
				result = org_eclipse_update_configuration_LocalSystemInfo_VOLUME_CDROM;
				break;
			case DRIVE_FIXED :
				result = org_eclipse_update_configuration_LocalSystemInfo_VOLUME_FIXED;
				break;
			case DRIVE_REMOTE :
				result = org_eclipse_update_configuration_LocalSystemInfo_VOLUME_REMOTE;
				break;
			case DRIVE_NO_ROOT_DIR :
				result = org_eclipse_update_configuration_LocalSystemInfo_VOLUME_INVALID_PATH;
				break;
			case DRIVE_RAMDISK :
				result = org_eclipse_update_configuration_LocalSystemInfo_VOLUME_RAMDISK;
				break;			
			case DRIVE_UNKNOWN :
			default :
				result = org_eclipse_update_configuration_LocalSystemInfo_VOLUME_UNKNOWN;
				break;
		}
	} else {
		result = org_eclipse_update_configuration_LocalSystemInfo_VOLUME_INVALID_PATH;
	}

	return result;
}

/*
 * Class:     org_eclipse_update_configuration_LocalSystemInfo
 * Method:    nativeListMountPoints
 * Signature: ()[Ljava/lang/String;
 */
JNIEXPORT jobjectArray JNICALL Java_org_eclipse_update_configuration_LocalSystemInfo_nativeListMountPoints(
	JNIEnv * jnienv,
	jclass javaClass) {

	//
	DWORD logDrives;
	UINT drive;
	TCHAR driveName[100];
	jobjectArray returnArray;
	int nDrive = 0;

	// 
	jclass stringClass;
	jobject empty;
	int index = 0;
	jobject str;


	logDrives = GetLogicalDrives();
	for (drive = 0; drive < 32; drive++) {
		if (logDrives & (1 << drive)) {
			nDrive++;
		}
	}

	stringClass = jnienv -> FindClass("java/lang/String");
	empty = jnienv -> NewStringUTF("");
	returnArray = jnienv -> NewObjectArray(nDrive, stringClass, empty);

	if (int win = (int)getWindowsVersion()<0 && NOWIN95){
		// windows 95 or other
		if (DEBUG)
			printf("Unsupported Windows: %i\n",win);	
		return NULL;
	}

	for (drive = 0; drive < 32; drive++) {
		if (logDrives & (1 << drive)) {
			sprintf(driveName, "%c:\\", drive + 'A');
			str = jnienv -> NewStringUTF(driveName);
			jnienv -> SetObjectArrayElement(returnArray, index, str);
			index++;
		}
	}

	return returnArray;
}

