/*

	JJBM, 03/12/2006, NokiaFT.cpp

*/
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <wchar.h>

#include "ConnAPI.h"

DMHANDLE h_DM;
DWORD dwDeviceCount;
FSHANDLE h_FS;

const WCHAR *strImagenes = L"\\\\E:\\Imágenes";
const WCHAR *strVideos = L"\\\\E:\\Videoclips";
const WCHAR *strHistorial = L"F:\\Imagenes\\Movil\\Historial";

WCHAR *msg_error( DWORD error );

bool Inicializar() {
	DWORD dwResult = CONAInitialize( CONA_API_VERSION, L"Nokia", NULL );
    if( dwResult != CONA_OK ) {
		wprintf(L"Error inicializando Nokia Con API.\n");
		return false;
	}
	wprintf( L"Nokia Con API inicializada con exito.\n" );
	return true;
}

bool Deinicializar() {
	DWORD dwResult = CONAUninitialize(0);
	if( dwResult != CONA_OK ) {
		wprintf(L"Error deinicializando Nokia Con API.\n");
		return false;
	}
	wprintf(L"Nokia Con API deinicializada con exito.\n");
	return true;
}

bool EstablecerManejador() {
	DWORD dwResult = CONAOpenDM( &h_DM );
    if( dwResult != CONA_OK ) {
		wprintf(L"Error inicializando manejador.\n");
		return false;
	}
	wprintf(L"Manejador inicializado con exito.\n");
	return true;
}

bool CerrarManejador() {
	DWORD dwResult = CONACloseDM( h_DM );
    if( dwResult != CONA_OK ) {
		wprintf(L"Error deinicializando manejador.\n");
		return false;
	}
	wprintf(L"Manejador deinicializado con exito.\n");
	return true;
}

bool ListarAbrirDispositivos( DWORD Conectar = -1 ) {

	DWORD dwResult = CONAGetDeviceCount( h_DM, &dwDeviceCount);
	CONAPI_DEVICE* pDevices = NULL;

    if( dwResult != CONA_OK ) {
		wprintf(L"Error obteniendo numero de dispositivos.\n");
		return false;
	}

	if( Conectar == -1 )
		wprintf( L"Hay visibles %i dispositivos.\n", dwDeviceCount );

	if( dwDeviceCount > 0 ) {

		pDevices = new CONAPI_DEVICE[ dwDeviceCount ];
		CONAGetDevices( h_DM, &dwDeviceCount, pDevices );

		for( DWORD j = 0; j < dwDeviceCount; j++ ) {

			if( Conectar == -1 ) {

				wprintf( L"\n= Dispositivo [%i] \"%s\"============\n",
					j,
					pDevices[j].pstrSerialNumber );

				for( DWORD i = 0; i < pDevices[j].dwNumberOfItems; i++ ) {
					wprintf( L"\t- Elemento [%i] ------------\n", i );
					wprintf( L"\t\tNombre: %s\n", pDevices[j].pItems[i].pstrDeviceName );
					wprintf( L"\t\tID: 0x%X\n", pDevices[j].pItems[i].dwDeviceID );
					wprintf( L"\t\tMedia: 0x%X\n", pDevices[j].pItems[i].dwMedia );
				}

			} else
			if( Conectar == j ) {

				DWORD dwID = 0;
				DWORD dwMedia = CONAPI_MEDIA_ALL;

				wprintf( L"\nConectando con \"%s\" ...", pDevices[j].pstrSerialNumber );

				dwResult = CONAOpenFS(
					pDevices[j].pstrSerialNumber,
					&dwMedia,
					&h_FS,
					&dwID
				);

				if( dwResult == CONA_OK ) {
					wprintf(L"\nConectando correctamente con el dispositivo.\n");
					return true;
				}

				wprintf(L"\nERROR conectando con dispositivo.\n");
				return false;

			}

		}

		if( Conectar == -1 )
			wprintf(L"\n");

		CONAFreeDeviceStructure( dwDeviceCount, pDevices );

		delete [] pDevices;

	}

	return Conectar == -1; // sólo es Ok si no queríamos conectar con ninguno.
}

bool CerrarDispositivo() {
	DWORD dwResult = CONACloseFS( h_FS );
    if( dwResult != CONA_OK ) {
		wprintf(L"Error cerrando dispositivo.\n");
		return false;
	}
	wprintf(L"Dispositivo cerrado exito.\n");
	return true;
}

WCHAR *TamanoArchivo( DWORD z ) {
	static WCHAR *b = new WCHAR[1024];
	if( ( z >> 10 ) != 0 ) {
		if( ( z >> 20 ) != 0 ) {
			if( ( z >> 30 ) != 0 ) {
				swprintf( b, L"% 7.02f Gb", z / ( 1024.0f * 1024.0f * 1024.0f ) );
			} else
				swprintf( b, L"% 7.02f Mb", z / ( 1024.0f * 1024.0f ) );
		} else
			swprintf( b, L"% 7.02f Kb", z / 1024.0f );
	} else
		swprintf( b, L"% 7.02f b ", z * 1.0f );
	return b;
}

WCHAR *FechaArchivo( FILETIME ft ) {
	static WCHAR *b = new WCHAR[1024];
	SYSTEMTIME st;
	if( FileTimeToSystemTime( &ft, &st ) == FALSE )
		wcscpy( b, L"dd/mm/aaaa hh:mm"); // :ss,mmm" );
	else
		swprintf( b,
			L"%02i/%02i/%02i %02i:%02i", // :%02i,%03i",
			st.wDay,
			st.wMonth,
			st.wYear % 100,
			st.wHour,
			st.wMinute //,st.wSecond,st.wMilliseconds
		);
	return b;
}

void ListarDir( const WCHAR *strFolder = L"\\\\" ) { 
    DWORD dwResult = CONA_OK;
    CONAPI_FOLDER_INFO FolderInfo = {0};
    CONAPI_FILE_INFO FileInfo = {0};
    FINDHANDLE hFindHandle = NULL;

    dwResult = CONAFindBegin( h_FS, 0, &hFindHandle, strFolder);

	wprintf( L"\n\n**** %s ***\n", strFolder );
    if(dwResult == CONA_OK) {
        while((dwResult = CONAFindNextFolder(hFindHandle, &FolderInfo)) == CONA_OK) {
			wprintf( L"%s [--DIR--] %s\n", FechaArchivo( FolderInfo.tFolderTime ), FolderInfo.pstrName );
            dwResult = CONAFreeFolderInfoStructure(&FolderInfo);
        }
		while((dwResult = CONAFindNextFile(hFindHandle, &FileInfo)) == CONA_OK) {
			wprintf( L"%s%s %s\n",
				FechaArchivo( FileInfo.tFileTime ),
				TamanoArchivo( FileInfo.dwFileSize ),
				FileInfo.pstrName );
			dwResult = CONAFreeFileInfoStructure(&FileInfo);
		}
        CONAFindEnd(hFindHandle);
		wprintf( L"\n" );
    }
}

bool CheckCase( const WCHAR a, const WCHAR b, bool checkCase = false ) {
	if( checkCase )
		return a == b;
	return towupper(a) == towupper(b);
}

bool con_prefijo( const WCHAR *W, const WCHAR *P, bool checkCase = false ) {
	WCHAR *w = (WCHAR *) W, *p = (WCHAR *) P;
	while( *p != '\0' )
		if( !CheckCase( *w++, *p++, checkCase ) )
			break;
	return *p == '\0';
}

bool con_sufijo( const WCHAR *w, const WCHAR *s, bool checkCase = false ) {
	WCHAR *iw = (WCHAR *) w, *is = (WCHAR *) s;
	while( *iw != '\0' ) iw++;
	while( *is != '\0' ) is++;
	while( iw >= w && is >= s )
		if( CheckCase( *iw, *is, checkCase ) ) {
			iw--;
			is--;
		} else
			break;
	return is < s;
}

void Phone2PC( const WCHAR *prefijo, const WCHAR *ruta, const WCHAR *desde,
		const WCHAR *cmpPrefijo = NULL,
		const WCHAR *cmpSufijo = NULL,
		bool borrarOrigen = true,
		int tipoNombre = 0,
			//	0	<AAMMDDhhmmss>_<prefijo>.<ext>
			//	1	<idem que entrada>
			//	2	<prefijo>_<contador++>.<ext>
		int contadorNombre = 0 ) {
    DWORD dwResult = CONA_OK;
    CONAPI_FILE_INFO FileInfo = {0};
    FINDHANDLE hFindHandle = NULL;
	WCHAR *origen = new WCHAR[ 1024 ], *destino = new WCHAR[ 1024 ];

    dwResult = CONAFindBegin( h_FS, 0, &hFindHandle, desde );

    if( dwResult == CONA_OK ) {

		wprintf( L"Current (%s)\n", msg_error( CONASetCurrentFolder( h_FS, desde ) ) );

		while( ( dwResult = CONAFindNextFile( hFindHandle, &FileInfo ) ) == CONA_OK ) {

			bool procesar = true;

			if( cmpPrefijo != NULL )
				procesar &= con_prefijo( FileInfo.pstrName, cmpPrefijo );

			if( cmpSufijo != NULL )
				procesar &= con_sufijo( FileInfo.pstrName, cmpSufijo );

			if( procesar ) {

				SYSTEMTIME st;
				memset( &st, 0, sizeof( SYSTEMTIME ) );

				FileTimeToSystemTime( &FileInfo.tFileTime, &st );

				WCHAR *x = FileInfo.pstrName, *xp = NULL;
				while( *x != L'\0' )
					if( *x++ == L'.' )
						xp = x;

				swprintf( origen, L"%s\\%s", ruta, FileInfo.pstrName );

				if( tipoNombre == 0 ) {
					if( xp != NULL )
						swprintf( destino, L"%s\\%02i%02i%02i%02i%02i%02i_%s.%s",
							ruta,
							st.wYear % 100,
							st.wMonth,
							st.wDay,
							st.wHour,
							st.wMinute,
							st.wSecond,
							prefijo,
							xp
						);
					else
						swprintf( destino, L"%s\\%02i%02i%02i%02i%02i%02i_%s",
							ruta,
							st.wYear % 100,
							st.wMonth,
							st.wDay,
							st.wHour,
							st.wMinute,
							st.wSecond,
							prefijo
						);
				}

				if( tipoNombre == 1 )
					swprintf( destino, L"%s\\%s", ruta, FileInfo.pstrName );

				if( tipoNombre == 2 ) {
					if( xp != NULL )
						swprintf( destino, L"%s\\%s_%i.%s", ruta, prefijo, contadorNombre++, xp );
					else
						swprintf( destino, L"%s\\%s_%i", ruta, prefijo, contadorNombre++ );
				}

				wprintf( L"Copiando %s a %s ...", FileInfo.pstrName, ruta );

				dwResult = CONACopyFile(
						h_FS,
						CONA_DIRECT_PHONE_TO_PC,
						FileInfo.pstrName,
						desde,
						destino // y no es preciso renombrar.
					);

				if( dwResult == CONA_OK ) {
					wprintf(L"OK\n");

					bool bError = false;

					/*
					if( tipoNombre != 1 ) {
						char *ori = new char[ 1024 ], *des = new char[ 1024 ];
						wcstombs( ori, origen, 1024 );
						wcstombs( des, destino, 1024 );

						if( rename( ori, des ) == 0 )
							wprintf( L"\tRenombrado a %s\n", destino );
						else {
							wprintf( L"\tERROR renombrado a %s\n", destino );
							bError = false;
						}

						delete [] ori;
						delete [] des;
					}
					*/
					
					if( borrarOrigen && !bError ) {
						dwResult = CONADeleteFile( h_FS, FileInfo.pstrName, NULL );
						if( dwResult == CONA_OK )
							wprintf(L"\tBorrado del movil correctamente.\n");
						else
							wprintf( L"\tERROR borrando del movil (%s)\n", msg_error( dwResult ) );
					}

				} else
					wprintf( L"ERROR (%s)\n", msg_error(dwResult) );

			}

			dwResult = CONAFreeFileInfoStructure(&FileInfo);

		}
        CONAFindEnd(hFindHandle);
    }

	delete [] origen;
	delete [] destino;
}

void UploadFiles( WCHAR *cmd ) {
	// "ruta movil entrada" "prefijo" "sufijo" "ruta PC salida"

	WCHAR *origen, *prefijo, *sufijo, *salida;

	{
		while( *cmd != L'\0' && *cmd != L'\"' ) cmd++; if( cmd == L'\0' ) goto Error;
		origen = ++cmd;
		while( *cmd != L'\0' && *cmd != L'\"' ) cmd++; if( cmd == L'\0' ) goto Error;
		*cmd++ = L'\0';
		while( *cmd != L'\0' && *cmd != L'\"' ) cmd++; if( cmd == L'\0' ) goto Error;
		prefijo = ++cmd;
		while( *cmd != L'\0' && *cmd != L'\"' ) cmd++; if( cmd == L'\0' ) goto Error;
		*cmd++ = L'\0';
		while( *cmd != L'\0' && *cmd != L'\"' ) cmd++; if( cmd == L'\0' ) goto Error;
		sufijo = ++cmd;
		while( *cmd != L'\0' && *cmd != L'\"' ) cmd++; if( cmd == L'\0' ) goto Error;
		*cmd++ = L'\0';
		while( *cmd != L'\0' && *cmd != L'\"' ) cmd++; if( cmd == L'\0' ) goto Error;
		salida = ++cmd;
		while( *cmd != L'\0' && *cmd != L'\"' ) cmd++; if( cmd == L'\0' ) goto Error;
		*cmd++ = L'\0';
		goto Ok;
Error:;
		wprintf(L"Error en parametros.\n");
		return;
Ok:;
	}

	Phone2PC( prefijo, salida, origen, prefijo, sufijo, false, 0 );

}

void Dir2PC( WCHAR *cmd, const WCHAR *origen ) {

	WCHAR *prefijo, *ruta;

	{
		while( *cmd != L'\0' && *cmd != L'\"' ) cmd++; if( cmd == L'\0' ) goto Error;
		prefijo = ++cmd;
		while( *cmd != L'\0' && *cmd != L'\"' ) cmd++; if( cmd == L'\0' ) goto Error;
		*cmd++ = L'\0';
		while( *cmd != L'\0' && *cmd != L'\"' ) cmd++; if( cmd == L'\0' ) goto Error;
		ruta = ++cmd;
		while( *cmd != L'\0' && *cmd != L'\"' ) cmd++; if( cmd == L'\0' ) goto Error;
		*cmd++ = L'\0';
		goto Ok;
Error:;
		wprintf(L"Error en parametros.\n");
		return;
Ok:;
	}

	Phone2PC( prefijo, ruta, origen );

}

void UploadImgVid( WCHAR *prefijo ) {
	wprintf(L"\n*** Imagenes *****************\n");
	Phone2PC( prefijo, strHistorial, strImagenes );
	wprintf(L"\n*** Videos *******************\n");
	Phone2PC( prefijo, strHistorial, strVideos );
}

void Img2PC( WCHAR *cmd ) {
	Dir2PC( cmd, strImagenes );
}

void Vid2PC( WCHAR *cmd ) {
	Dir2PC( cmd, strVideos );
}

void AbrirHist() {
	WCHAR *cmd = new WCHAR[ 1024 ];
	//wcstombs( d, strHistorial, 1024 );
	swprintf( cmd, L"cmd /c start \"Abrir\" \"%s\"", strHistorial );
	_wsystem(cmd);
	delete [] cmd;
}

bool EjecutarCmd( WCHAR *cmd ) {
	if( wcscmp( cmd, L"?" ) == 0 ) {
		wprintf(L"?\n\tmuestra la ayuda\n\n");
		wprintf(L"exit\n\tsalir\n\n");
		wprintf(L"dir <ruta>\n\tlista los archivos y subdirectorios de la ruta indicada.\n\tP.e.: \\\\E:\\Datos\n\n");
		wprintf( L"imglst\n\tlista los archivos del directorio %s\n\n", strImagenes );
		wprintf( L"img2pc \"<prefijo>\" \"<ruta>\"\n\tcopia los archivos del directorio de imagenes (%s) al directorio indicado dando a cada archivo el nombre <prefijo>_AAMMDD_hhmmss.<ext>\n\n", strImagenes );
		wprintf( L"vidlst\n\tlista los archivos del directorio %s\n\n", strVideos );
		wprintf( L"vid2pc \"<prefijo>\" \"<ruta>\"\n\tcopia los archivos del directorio de videos (%s) al directorio indicado dando a cada archivo el nombre <prefijo>_AAMMDD_hhmmss.<ext>\n\n", strVideos );
		wprintf( L"up <prefijo>\n\tcopia los archivos de los directorios de imagenes y del de videos al hostorico del PC, cada archivo el nombre <prefijo>_AAMMDD_hhmmss.<ext>\n\n" );
		wprintf( L"fileup \"<ruta_entrada>\" \"<prefijo>\" \"<sufijo>\" \"<ruta_salida>\"\n\tcopia los archivos de la ruta de entrada cuyo nombre empieza y termina segun prefijo y sufijo indicados a la ruta de salida.\n\n" );
		wprintf( L"hist\n\tabre la carpeta de historico.\n\n" );
	}
	if( wcscmp( cmd, L"exit" ) == 0 )		return false;
	if( wcsstr( cmd, L"dir" ) == cmd )		ListarDir( cmd + 4 );
	if( wcsstr( cmd, L"imglst" ) == cmd )	ListarDir( strImagenes );
	if( wcsstr( cmd, L"img2pc" ) == cmd )	Img2PC( cmd );
	if( wcsstr( cmd, L"vidlst" ) == cmd )	ListarDir( strVideos );
	if( wcsstr( cmd, L"vid2pc" ) == cmd )	Vid2PC( cmd );
	if( wcsstr( cmd, L"fileup" ) == cmd )		UploadFiles( cmd );
	if( wcsstr( cmd, L"up" ) == cmd )		UploadImgVid( cmd + 3 );
	if( wcsstr( cmd, L"hist" ) == cmd )		AbrirHist();
	return true;
}

void Navegador() {
	WCHAR c;
	WCHAR *cmd = new WCHAR[ 1024 ];
	for(;;) {
		wprintf( L"$ " );
		wscanf( L"%[^\n]%c", cmd, &c );
		if( !EjecutarCmd(cmd) )
			break;
	}
	delete [] cmd;
}

void pasar_a_wc( WCHAR *o, char *i ) {
	while( *i != '\0' )
		*o++ = (WCHAR) *i++;
	*o = L'\0';
}

void main( int argc, char **argv ) {

	if( Inicializar() ) {

		if( EstablecerManejador() ) {

			if( ListarAbrirDispositivos() ) {

				if( dwDeviceCount <= 0 )

					wprintf(L"No hay dispositivos.\n");

				else {

					int k;

					if( dwDeviceCount == 1 ) {
						wprintf(L"\nSolo hay un dispositivo.\n");
						k = '0';
					} else {
						wprintf(L"\nSelecciona un dispositivo: ");
						k = getch();
						wprintf( L"%c\n", k );
					}
				
					if( ListarAbrirDispositivos( k - '0' ) ) {

						if( argc == 1 )
							// ejecutar consola
							Navegador();
						else {
							// ejecutar comando desde parámetros

							WCHAR cmd[2048]; //, tmp[1024];
							int i = 1;
							wcscpy( cmd, L"" );
							for(;;) {
								wprintf( L"ARG(%i)\n\t%s\n", i, argv[i] );
								// pasar_a_wc( tmp, argv[i++] );
								if( i > 1 ) wcscat( cmd, L"\"" );
								WCHAR buf[1024];
								mbstowcs( buf, argv[i++], 1024 );
								wcscat( cmd, buf );
								if( i > 1 ) wcscat( cmd, L"\"" );
								if( i >= argc )
									break;
								wcscat( cmd, L" " );
							}
							wprintf( L"\n\nEjecutando comando:\n\t%s\n\n", cmd );
							EjecutarCmd( cmd );

						}

						CerrarDispositivo();

					}

				}

			}

			CerrarManejador();

		}

		Deinicializar();

	}

}

WCHAR *msg_error( DWORD error ) {
	switch( error ) {
	case CONA_OK: return L"Function completed successfully.  ";
	case ECONA_CANCELLED: return L"Cancelled; see CONACancel function.  ";
	case ECONA_CONNECTION_BUSY: return L"Connection to device is busy.  ";
	case ECONA_CONNECTION_LOST: return L"Connection to device is lost.  ";
	case ECONA_CURRENT_FOLDER_NOT_FOUND: return L"Invalid current folder (or strPath). E.g, MMC card removed.  ";
	case ECONA_FAILED_TIMEOUT: return L"Timeout.  ";
	case ECONA_FILE_ALREADY_EXIST: return L"File exists in target path (only if CONA_OVERWRITE or CONA_RENAME is not used).  ";
	case ECONA_FILE_BUSY: return L"Some other application has reserved the file.  ";
	case ECONA_FILE_COPYRIGHT_PROTECTED: return L"No permission to read or overwrite the file (only if CONA_OVERWRITE is used) in the device. The file is copyright protected.  ";
	case ECONA_FILE_NAME_INVALID: return L"File name includes invalid character(s).  ";
	case ECONA_FILE_NAME_TOO_LONG: return L"File name with current/target path is too long. The maximum length is 245 characters.  ";
	case ECONA_FILE_NOT_FOUND: return L"File not found.  ";
	case ECONA_FILE_NO_PERMISSION: return L"No permission to read file or overwrite file (only if CONA_OVERWRITE is used) in the device.  ";
	case ECONA_FILE_NO_PERMISSION_ON_PC: return L"No permission to read file or overwrite file (only if CONA_OVERWRITE is used) in the PC.  ";
	case ECONA_FILE_TOO_BIG_DEVICE: return L"File too big (limited file size).  ";
	case ECONA_FILE_TYPE_NOT_SUPPORTED: return L"Unsupported file type.  ";
	case ECONA_FOLDER_NOT_FOUND: return L"The folder is not found in PC or device.  ";
	case ECONA_FOLDER_NO_PERMISSION: return L"No permission to perform operation in the target folder of device.  ";
	case ECONA_FOLDER_NO_PERMISSION_ON_PC: return L"Failed, no permission access to PC folder.  ";
	case ECONA_FOLDER_PATH_TOO_LONG: return L"Path is too long. The maximum length is 245 characters.  ";
	case ECONA_INVALID_DATA_DEVICE: return L"Invalid folder content from device.  ";
	case ECONA_INVALID_HANDLE: return L"Invalid file system handle.  ";
	case ECONA_INVALID_PARAMETER: return L"Invalid parameter.  ";
	case ECONA_MEMORY_FULL: return L"Memory full in target device (or PC).  ";
	case ECONA_NOT_ENOUGH_MEMORY: return L"The memory allocation failed in PC.  ";
	case ECONA_NOT_INITIALIZED: return L"The PC Suite Connectivity API is not initialized.  ";
	case ECONA_NOT_SUPPORTED_DEVICE: return L"The device does not support the operation.  ";
	case ECONA_UNKNOWN_ERROR: return L"Unknown error.  ";
	case ECONA_UNKNOWN_ERROR_DEVICE: return L"Device rejects the operation.  ";
	}
	return L"Error desconido.";
}