#include <windows.h>
#include <stdio.h>
#include <string>
#include "ConnAPI.h"

// this program lists all files on a nokia phone
//
// this program requires the nokia 'connapi' from 
// PC_Suite_Connectivity_API_1.1.zip
//
// compile like this:
//
//    cl /EHsc /I libs  sdir.cpp libs\ConnAPI.lib
//
char *errstring(DWORD error)
{
switch(error)
{
case ECONA_INIT_FAILED: return "ECONA_INIT_FAILED";
case ECONA_INIT_FAILED_COM_INTERFACE: return "ECONA_INIT_FAILED_COM_INTERFACE";
case ECONA_NOT_INITIALIZED: return "ECONA_NOT_INITIALIZED";
case ECONA_UNSUPPORTED_API_VERSION: return "ECONA_UNSUPPORTED_API_VERSION";
case ECONA_NOT_SUPPORTED_MANUFACTURER: return "ECONA_NOT_SUPPORTED_MANUFACTURER";

case ECONA_UNKNOWN_ERROR: return "ECONA_UNKNOWN_ERROR";
case ECONA_UNKNOWN_ERROR_DEVICE: return "ECONA_UNKNOWN_ERROR_DEVICE";
case ECONA_INVALID_POINTER: return "ECONA_INVALID_POINTER";
case ECONA_INVALID_PARAMETER: return "ECONA_INVALID_PARAMETER";
case ECONA_INVALID_HANDLE: return "ECONA_INVALID_HANDLE";
case ECONA_NOT_ENOUGH_MEMORY: return "ECONA_NOT_ENOUGH_MEMORY";
case ECONA_WRONG_THREAD: return "ECONA_WRONG_THREAD";

case ECONA_CANCELLED: return "ECONA_CANCELLED";
case ECONA_NOTHING_TO_CANCEL: return "ECONA_NOTHING_TO_CANCEL";
case ECONA_FAILED_TIMEOUT: return "ECONA_FAILED_TIMEOUT";
case ECONA_NOT_SUPPORTED_DEVICE: return "ECONA_NOT_SUPPORTED_DEVICE";
case ECONA_NOT_SUPPORTED_PC: return "ECONA_NOT_SUPPORTED_PC";


case ECONA_DEVICE_NOT_FOUND: return "ECONA_DEVICE_NOT_FOUND";
case ECONA_NO_CONNECTION_VIA_MEDIA: return "ECONA_NO_CONNECTION_VIA_MEDIA";
case ECONA_NO_CONNECTION_VIA_DEVID: return "ECONA_NO_CONNECTION_VIA_DEVID";
case ECONA_INVALID_CONNECTION_TYPE: return "ECONA_INVALID_CONNECTION_TYPE";
case ECONA_NOT_SUPPORTED_CONNECTION_TYPE: return "ECONA_NOT_SUPPORTED_CONNECTION_TYPE";
case ECONA_CONNECTION_BUSY: return "ECONA_CONNECTION_BUSY";
case ECONA_CONNECTION_LOST: return "ECONA_CONNECTION_LOST";
case ECONA_CONNECTION_REMOVED: return "ECONA_CONNECTION_REMOVED";
case ECONA_CONNECTION_FAILED: return "ECONA_CONNECTION_FAILED";
case ECONA_SUSPEND: return "ECONA_SUSPEND";
case ECONA_NAME_ALREADY_EXISTS: return "ECONA_NAME_ALREADY_EXISTS";
case ECONA_MEDIA_IS_NOT_WORKING: return "ECONA_MEDIA_IS_NOT_WORKING";
case ECONA_CACHE_IS_NOT_AVAILABLE: return "ECONA_CACHE_IS_NOT_AVAILABLE";
case ECONA_MEDIA_IS_NOT_ACTIVE: return "ECONA_MEDIA_IS_NOT_ACTIVE";


case ECONA_DEVICE_PAIRING_FAILED: return "ECONA_DEVICE_PAIRING_FAILED";
case ECONA_DEVICE_PASSWORD_WRONG: return "ECONA_DEVICE_PASSWORD_WRONG";
case ECONA_DEVICE_PASSWORD_INVALID: return "ECONA_DEVICE_PASSWORD_INVALID";



case ECONA_ALL_LISTED: return "ECONA_ALL_LISTED";
case ECONA_MEMORY_FULL: return "ECONA_MEMORY_FULL";


case ECONA_FILE_NAME_INVALID: return "ECONA_FILE_NAME_INVALID";
case ECONA_FILE_NAME_TOO_LONG: return "ECONA_FILE_NAME_TOO_LONG";
															
															
case ECONA_FILE_ALREADY_EXIST: return "ECONA_FILE_ALREADY_EXIST";
case ECONA_FILE_NOT_FOUND: return "ECONA_FILE_NOT_FOUND";
case ECONA_FILE_NO_PERMISSION: return "ECONA_FILE_NO_PERMISSION";
case ECONA_FILE_COPYRIGHT_PROTECTED: return "ECONA_FILE_COPYRIGHT_PROTECTED";
case ECONA_FILE_BUSY: return "ECONA_FILE_BUSY";
case ECONA_FILE_TOO_BIG_DEVICE: return "ECONA_FILE_TOO_BIG_DEVICE";
case ECONA_FILE_TYPE_NOT_SUPPORTED: return "ECONA_FILE_TYPE_NOT_SUPPORTED";
case ECONA_FILE_NO_PERMISSION_ON_PC: return "ECONA_FILE_NO_PERMISSION_ON_PC";
case ECONA_FILE_EXIST: return "ECONA_FILE_EXIST";


case ECONA_INVALID_DATA_DEVICE: return "ECONA_INVALID_DATA_DEVICE";
case ECONA_CURRENT_FOLDER_NOT_FOUND: return "ECONA_CURRENT_FOLDER_NOT_FOUND";
case ECONA_FOLDER_PATH_TOO_LONG: return "ECONA_FOLDER_PATH_TOO_LONG";
															
															
case ECONA_FOLDER_NAME_INVALID: return "ECONA_FOLDER_NAME_INVALID";
case ECONA_FOLDER_ALREADY_EXIST: return "ECONA_FOLDER_ALREADY_EXIST";
case ECONA_FOLDER_NOT_FOUND: return "ECONA_FOLDER_NOT_FOUND";
case ECONA_FOLDER_NO_PERMISSION: return "ECONA_FOLDER_NO_PERMISSION";
case ECONA_FOLDER_NOT_EMPTY: return "ECONA_FOLDER_NOT_EMPTY";
case ECONA_FOLDER_NO_PERMISSION_ON_PC: return "ECONA_FOLDER_NO_PERMISSION_ON_PC";


case ECONA_DEVICE_INSTALLER_BUSY: return "ECONA_DEVICE_INSTALLER_BUSY";


case ECONA_UI_NOT_IDLE_DEVICE: return "ECONA_UI_NOT_IDLE_DEVICE";
case ECONA_SYNC_CLIENT_BUSY_DEVICE: return "ECONA_SYNC_CLIENT_BUSY_DEVICE";
case ECONA_UNAUTHORIZED_DEVICE: return "ECONA_UNAUTHORIZED_DEVICE";
case ECONA_DATABASE_LOCKED_DEVICE: return "ECONA_DATABASE_LOCKED_DEVICE";
case ECONA_SETTINGS_NOT_OK_DEVICE: return "ECONA_SETTINGS_NOT_OK_DEVICE";
default:
    static char errbuf[20];
    sprintf(errbuf, "ERROR_%08lx", error);
    return errbuf;
}
}

char *attrstring(DWORD attr)
{
    static char attrbuf[10];
    memset(attrbuf, '-', 5);
    attrbuf[5]=0;
    if (attr&CONA_FPERM_READ)   attrbuf[0] = 'R';
    if (attr&CONA_FPERM_WRITE)  attrbuf[1] = 'W';
    if (attr&CONA_FPERM_DELETE) attrbuf[2] = 'D';
    if (attr&CONA_FPERM_FOLDER) attrbuf[3] = 'F';
    if (attr&CONA_FPERM_DRIVE)  attrbuf[4] = 'V';

    return attrbuf;
}
char *timestring(FILETIME *ft)
{
    SYSTEMTIME systime;
    FileTimeToSystemTime(ft, &systime);

    static char timebuf[40];
    sprintf(timebuf, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
            systime.wYear, systime.wMonth, systime.wDay,
            systime.wHour, systime.wMinute, systime.wSecond,systime.wMilliseconds);

    return timebuf;
}
char *mediastring(DWORD media)
{
    static char mediabuf[40];
    mediabuf[0]=0;

    if (media&CONAPI_MEDIA_ALL)       { if (mediabuf[0]) strcat(mediabuf,","); strcat(mediabuf, "ALL"); }
    if (media&CONAPI_MEDIA_IRDA)      { if (mediabuf[0]) strcat(mediabuf,","); strcat(mediabuf, "IRDA"); }
    if (media&CONAPI_MEDIA_SERIAL)    { if (mediabuf[0]) strcat(mediabuf,","); strcat(mediabuf, "SERIAL"); }
    if (media&CONAPI_MEDIA_BLUETOOTH) { if (mediabuf[0]) strcat(mediabuf,","); strcat(mediabuf, "BLUETOOTH"); }
    if (media&CONAPI_MEDIA_USB)       { if (mediabuf[0]) strcat(mediabuf,","); strcat(mediabuf, "USB"); }
    return mediabuf;
}
char *constatestring(DWORD state)
{
    static char statebuf[40];
    statebuf[0]=0;

    if (state&1)    { if (statebuf[0]) strcat(statebuf,","); strcat(statebuf, "UNPAIRED"); }
    if (state&2)    { if (statebuf[0]) strcat(statebuf,","); strcat(statebuf, "PAIRED"); }
    if (state&4)    { if (statebuf[0]) strcat(statebuf,","); strcat(statebuf, "TRUSTED"); }
    return statebuf;
}
void dump_coninfo(CONAPI_CONNECTION_INFO *con)
{
    printf("coninfo: devid=%08lx media=%s name='%ls' addr='%ls' state=%s\n",
            con->dwDeviceID,
            mediastring(con->dwMedia),
            con->pstrDeviceName,
            con->pstrAddress,
            constatestring(con->dwState));
}
void dump_devinfo(CONAPI_DEVICE *dev)
{
    printf("devinfo: sernum='%ls'  friendly='%ls'  model='%ls'  mfg='%ls'\n",
            dev->pstrSerialNumber,
            dev->pstrFriendlyName,
            dev->pstrModel,
            dev->pstrManufacturer);
    for (int i=0 ; i<dev->dwNumberOfItems ; i++)
        dump_coninfo(&(dev->pItems[i]));
}

void dump_folderinfo(std::Wstring &basepath, CONAPI_FOLDER_INFO *folder)
{
    // folder->pstrMemoryType, folder->pstrLabel,
    printf("%-5s %s    <DIR>  %ls%ls\n",
            attrstring(folder->dwAttributes),
            timestring(&(folder->tFolderTime)),
            basepath.c_str(),
            folder->pstrName);
}
void dump_fileinfo(std::Wstring &basepath, CONAPI_FILE_INFO *file)
{
    // file->pstrMIMEType,
    printf("%-5s %s %9d %ls%ls\n",
            attrstring(file->dwAttributes),
            timestring(&(file->tFileTime)),
            file->dwFileSize,
            basepath.c_str(),
            file->pstrName);
}
std::Wstring extraslash(const std::Wstring &path)
{
    if (path[path.size()-1]=='\\')
        return L"";
    else
        return L"\\";
}

void list_files(FSHANDLE fs, const std::Wstring &path)
{
    DWORD dwResult=0;
    FINDHANDLE find;
    dwResult=CONAFindBegin(fs, 0, &find, path.c_str());
    if (dwResult != CONA_OK) {
        printf("ERROR: CONAFindBegin: %s\n", errstring(dwResult));
        throw 1;
    }
    //printf("find=%08lx\n", find);
    try {
        while(1) {
            CONAPI_FILE_INFO fileinfo;
            dwResult= CONAFindNextFile(find, &fileinfo);
            if (dwResult == ECONA_ALL_LISTED)
                break;
            if (dwResult != CONA_OK) {
                printf("ERROR: CONAFindNextFile: %s\n", errstring(dwResult));
                throw 1;
            }
            dump_fileinfo(path+extraslash(path), &fileinfo);
            CONAFreeFileInfoStructure(&fileinfo);
        }
    }
    catch(int) {
    }
    dwResult=CONAFindEnd(find);
    if (dwResult != CONA_OK) {
        printf("ERROR: CONAFindEnd: %s\n", errstring(dwResult));
        throw 1;
    }
}
void list_subfolders(FSHANDLE fs, const std::Wstring &path)
{
    DWORD dwResult=0;
    FINDHANDLE find;
    dwResult=CONAFindBegin(fs, 0, &find, path.c_str());
    if (dwResult != CONA_OK) {
        printf("ERROR: CONAFindBegin: %s\n", errstring(dwResult));
        throw 1;
    }
    //printf("find=%08lx\n", find);
    try {
        while(1) {
            CONAPI_FOLDER_INFO folderinfo;
            dwResult= CONAFindNextFolder(find, &folderinfo);
            if (dwResult == ECONA_ALL_LISTED)
                break;
            if (dwResult != CONA_OK) {
                printf("ERROR: CONAFindNextFolder: %s\n", errstring(dwResult));
                throw 1;
            }
            dump_folderinfo(path+extraslash(path), &folderinfo);
            list_subfolders(fs, path+extraslash(path)+folderinfo.pstrName);
            list_files(fs, path+extraslash(path)+folderinfo.pstrName);
            CONAFreeFolderInfoStructure(&folderinfo);
        }
    }
    catch(int) {
    }
    dwResult=CONAFindEnd(find);
    if (dwResult != CONA_OK) {
        printf("ERROR: CONAFindEnd: %s\n", errstring(dwResult));
        throw 1;
    }
}

void dump_meminfo(FSHANDLE fs)
{
    DWORD dwResult=0;
    WCHAR *mtypes=NULL;
    dwResult=CONAGetMemoryTypes(fs, &mtypes);
    if (dwResult != CONA_OK) {
        printf("ERROR: CONAGetMemoryTypes: %s\n", errstring(dwResult));
        throw 1;
    }
    printf("mtypes='%ls'\n", mtypes);

    WCHAR memtypebuf[20];
    wcscpy(memtypebuf, mtypes);
    WCHAR *p = memtypebuf;

    while (1)
    {
        WCHAR *idx= wcschr(p, ',');
        if (idx)
            *idx=0;

        __int64 free=0;
        __int64 total=0;
        __int64 used=0;
        dwResult= CONAGetMemoryValues(fs, p, &free, &total, &used);
        if (dwResult != CONA_OK) {
            printf("ERROR: CONAGetMemoryValues: %s\n", errstring(dwResult));
            throw 1;
        }
        printf("%ls: free=%I64d total=%I64d used=%I64d\n", p, free, total, used);
        if (idx==0)
            break;
        p= idx+1;
    }
}

int main(int argc, char **argv)
{
    DWORD dwVersion = CONAGetConnAPIVersion();
    printf("version=%08lx\n", dwVersion);
    DWORD dwResult=0;
    dwResult = CONAInitialize(CONA_API_VERSION, L"Nokia", NULL);
    if (dwResult != CONA_OK) {
        printf("ERROR: CONAInitialize: %s\n", errstring(dwResult));
        return 1;
    }
    try {
        DMHANDLE dm;
        dwResult = CONAOpenDM(&dm);
        if (dwResult != CONA_OK) {
            printf("ERROR: CONAOpenDM: %s\n", errstring(dwResult));
            throw 1;
        }
        try {
            //printf("dm=%08lx\n", dm);
            DWORD count=0;
            dwResult= CONAGetDeviceCount(dm, &count);
            if (dwResult != CONA_OK) {
                printf("ERROR: CONAGetDeviceCount: %s\n", errstring(dwResult));
                throw 1;
            }
            printf("device count=%08lx\n", count);
            if (count==0)
                throw 1;
        // libs\CONADefinitions.h
            CONAPI_DEVICE *devlist= new CONAPI_DEVICE[count];
            DWORD devcount= count;
            try {
                dwResult = CONAGetDevices(dm, &devcount, devlist);
                if (dwResult != CONA_OK) {
                    printf("ERROR: CONAGetDevices: %s\n", errstring(dwResult));
                    throw 1;
                }
                try {
                    for (int i=0 ; i<devcount ; i++) {
                        dump_devinfo(&(devlist[i]));
                        // CONAGetDevice(dm, serial,&dev);
                        DWORD media=CONAPI_MEDIA_ALL;
                        FSHANDLE fs=0;
                        DWORD deviceid=0;
                        dwResult= CONAOpenFS(devlist[0].pstrSerialNumber, &media, &fs, &deviceid);
                        if (dwResult != CONA_OK) {
                            printf("ERROR: CONAOpenFS: %s\n", errstring(dwResult));
                            throw 1;
                        }
                        printf("media=%s devid=%08lx\n", mediastring(media), deviceid);
                        try {
                            dump_meminfo(fs);


                            list_subfolders(fs, L"\\\\");
                            list_files(fs, L"\\\\");
                        
                            CONAPI_FILE_INFO fileinfo;
                        }
                        catch(int) {
                        }
                        dwResult= CONACloseFS(fs);
                        if (dwResult != CONA_OK) {
                            printf("ERROR: CONACloseFS: %s\n", errstring(dwResult));
                            throw 1;
                        }
                    }
                }
                catch(int) {
                }
                dwResult = CONAFreeDeviceStructure(devcount, devlist);
                if (dwResult != CONA_OK) {
                    printf("ERROR: CONAFreeDeviceStructure: %s\n", errstring(dwResult));
                    throw 1;
                }
            }
            catch(int) {
            }
            delete[] devlist;

        }
        catch (int) {
        }
        
        dwResult = CONACloseDM(dm);
        if (dwResult != CONA_OK) {
            printf("ERROR: CONACloseDM: %s\n", errstring(dwResult));
            throw 1;
        }
    }
    catch(int) {
    }

    dwResult = CONAUninitialize(0);
    if (dwResult != CONA_OK) {
        printf("ERROR: CONAUninitialize: %s\n", errstring(dwResult));
        return 1;
    }
    return 0;
}

