#include <windows.h>
#include <stdio.h>
#include <string>
#include "ConnAPI.h"

// this program copies photos and videos from our family's phones, and copies them to the current directory.
// 
// this is not a general purpose tool!!
//
//#include "PCCAPIUtils.h"
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 *timestampstring(FILETIME *ft)
{
    SYSTEMTIME systime;
    FileTimeToSystemTime(ft, &systime);

    static char timebuf[40];
    sprintf(timebuf, "%04d%02d%02d%02d%02d%02d",
            systime.wYear, systime.wMonth, systime.wDay,
            systime.wHour, systime.wMinute, systime.wSecond);

    return timebuf;
}
char *datestring(FILETIME *ft)
{
    SYSTEMTIME systime;
    FileTimeToSystemTime(ft, &systime);

    static char datebuf[40];
    sprintf(datebuf, "%04d%02d", systime.wYear, systime.wMonth);

    return datebuf;
}
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 (DWORD 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"\\";
}
// 20061031(003).jpg
// 20061013(003_2).jpg
// 20061029.jpg
// 20061101298.jpg
bool parsename(WCHAR *filename, char *ext, int *num)
{
    WCHAR *bracket= wcsrchr(filename, '(');
    WCHAR *dot= wcsrchr(filename, '.');
    WCHAR *uscore= wcsrchr(filename, '_');
    if (dot==NULL)
        return false;
    if (wcslen(dot)!=4)
        return false;
    if (bracket) {
        *num= wcstol(bracket+1,0,10);
        if (bracket-filename != 8)
            return false;
        if (uscore)
            *num += 1000 * (uscore[1]-'0');
    }
    else {
        if (dot-filename == 8)
            *num = 0;
        else if (dot-filename == 11)
            *num= wcstol(dot-3,0,10);
        else
            return false;
    }
    *ext++=*++dot;
    *ext++=*++dot;
    *ext++=*++dot;
    *ext++=0;
    return true;
}
bool copy_file(FSHANDLE fs, std::Wstring &basepath, CONAPI_FILE_INFO *file,char typechar)
{
    DWORD dwResult;
    char dstname[260];
    char fileext[4];
    int filenum;
    if (!parsename(file->pstrName, fileext, &filenum)) {
        printf("%ls  not in recognized image format\n", file->pstrName);
        return false;
    }
    sprintf(dstname, "%s\\%s-%c-%04d.%s", datestring(&(file->tFileTime)), timestampstring(&(file->tFileTime)), typechar, filenum, fileext);

    BYTE *filedata= NULL;
    dwResult = CONAReadFile(fs, file, &filedata, 0, basepath.c_str());
    if (dwResult != CONA_OK) {
        printf("ERROR: CONAReadFile: %s\n", errstring(dwResult));
        return false;
    }
    CreateDirectory(datestring(&(file->tFileTime)), NULL);

    HANDLE h= CreateFile(dstname, GENERIC_WRITE, FILE_SHARE_READ,
                NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
    if (h==INVALID_HANDLE_VALUE || h==NULL)
    {
        printf("ERROR %08lx: CreateFile(%hs, WRITE)\n", GetLastError(), dstname);
        return false;
    }
    DWORD wrote;
    if (!WriteFile(h, filedata, file->dwFileSize, &wrote, NULL))
    {
        CloseHandle(h);
        return false;
    }
    if (!CloseHandle(h))
    {
        printf("ERROR %08lx: WriteFileData: CloseHandle\n", GetLastError());
        return false;
    }

    return true;
}
bool delete_file(FSHANDLE fs, std::Wstring &basepath, CONAPI_FILE_INFO *file)
{
    DWORD dwResult;

    dwResult = CONADeleteFile(fs, file->pstrName, basepath.c_str());
    if (dwResult != CONA_OK) {
        printf("ERROR: CONADeleteFile: %s\n", errstring(dwResult));
        return false;
    }

    return true;
}

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 copy_files(FSHANDLE fs, const std::Wstring &path, char typechar)
{
    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;
            }
            if (copy_file(fs, path+extraslash(path), &fileinfo, typechar))
                delete_file(fs, path+extraslash(path), &fileinfo);
            else {
                printf("FAIL: ");
                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 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);

                        char typechar= (wcscmp(devlist[i].pstrModel, L"RM-36")==0) ? 'w' 
                            : (wcscmp(devlist[i].pstrModel, L"RM-84")==0) ? 'b' : 'x';
                        try {
                            dump_meminfo(fs);


                            copy_files(fs, L"\\\\E:\\Images", typechar);
                            copy_files(fs, L"\\\\E:\\Videos", typechar);
                            copy_files(fs, L"\\\\C:\\Nokia\\Images", typechar);
                            copy_files(fs, L"\\\\C:\\Nokia\\Videos", typechar);
                        
                        }
                        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;
}


