/* ============================================================
 *
 * This file is a part of digiKam project
 * http://www.digikam.org
 *
 * Date        : 2004-09-17
 * Description : digital camera controller
 *
 * Copyright (C) 2004-2005 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
 * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com> 
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation;
 * either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * ============================================================ */

extern "C"
{
#include <unistd.h>
}

// C++ includes.

#include <typeinfo>
#include <cstdio>

// TQt includes.

#include <tqthread.h>
#include <tqmutex.h>
#include <tqwaitcondition.h>
#include <tqevent.h>
#include <tqapplication.h>
#include <tqdeepcopy.h>
#include <tqvariant.h>
#include <tqimage.h>
#include <tqdatastream.h>
#include <tqfile.h>
#include <tqtimer.h>
#include <tqregexp.h>

// KDE includes.

#include <klocale.h>
#include <kurl.h>
#include <kmessagebox.h>
#include <kio/renamedlg.h>
#include <kstandarddirs.h>

// Local includes.

#include "ddebug.h"
#include "thumbnailsize.h"
#include "imagewindow.h"
#include "gpcamera.h"
#include "umscamera.h"
#include "dmetadata.h"
#include "jpegutils.h"
#include "mtqueue.h"
#include "cameracontroller.h"
#include "cameracontroller.moc"

namespace Digikam
{

class CameraThread;

class CameraCommand
{
public:

    enum Action
    {
        gp_none = 0,
        gp_connect,
        gp_cancel,
        gp_cameraInformations,
        gp_listfolders,
        gp_listfiles,
        gp_download,
        gp_upload,
        gp_delete,
        gp_lock,
        gp_thumbnail,
        gp_exif,
        gp_open
    };

    Action                 action;
    TQMap<TQString,TQVariant> map; 
};

class CameraEvent : public TQCustomEvent
{
public:

    enum State
    {
        gp_connected = 0,
        gp_busy,
        gp_listedfolders,
        gp_listedfiles,
        gp_downloadstarted,
        gp_downloaded,
        gp_downloadFailed,
        gp_opened,
        gp_uploaded,
        gp_uploadFailed,
        gp_deleted,
        gp_deleteFailed,
        gp_locked,
        gp_lockFailed,
        gp_thumbnailed,
        gp_exif,
        gp_cameraInformations,
        gp_infomsg,
        gp_errormsg
    };

    CameraEvent(State state) :
        TQCustomEvent(TQEvent::User+state)
        {}

    bool                   result;
    TQString                msg;
    TQMap<TQString,TQVariant> map; 
};

class CameraControllerPriv
{
public:

    CameraControllerPriv()
    {
        close         = false;
        overwriteAll  = false;
        skipAll       = false;
        canceled      = false;
        downloadTotal = 0;
        parent        = 0; 
        timer         = 0; 
        camera        = 0;
        thread        = 0; 
    }

    bool                    close;
    bool                    overwriteAll;
    bool                    skipAll;
    bool                    canceled;

    int                     downloadTotal;

    TQWidget                *parent;

    TQTimer                 *timer;

    CameraThread           *thread;

    DKCamera               *camera;

    MTQueue<CameraCommand>  cmdQueue;
};

class CameraThread : public TQThread
{
public:

    CameraThread(CameraController* controller);
    ~CameraThread();

    void sendBusy(bool busy);
    void sendError(const TQString& msg);
    void sendInfo(const TQString& msg);

protected:

    void run();

private:

    CameraControllerPriv *d;

    TQObject              *parent;
};

CameraThread::CameraThread(CameraController* controller)
            : d(controller->d), parent(controller)
{
}

CameraThread::~CameraThread()
{
}

void CameraThread::run()
{
    if (d->close)
        return;

    sendBusy(true);

    CameraCommand* cmd = d->cmdQueue.dequeue();
    if (cmd)
    {
        switch (cmd->action)
        {
            case(CameraCommand::gp_connect):
            {
                sendInfo(i18n("Connecting to camera..."));

                bool result = d->camera->doConnect();
    
                CameraEvent* event = new CameraEvent(CameraEvent::gp_connected);
                event->result = result;
                TQApplication::postEvent(parent, event);
    
                if (result)
                    sendInfo(i18n("Connection established"));
                else
                    sendInfo(i18n("Connection failed"));
    
                break;
            }
            case(CameraCommand::gp_cameraInformations):
            {
                sendInfo(i18n("Getting camera information..."));
            
                TQString summary, manual, about;

                d->camera->cameraSummary(summary);
                d->camera->cameraManual(manual);
                d->camera->cameraAbout(about);
            
                CameraEvent* event = new CameraEvent(CameraEvent::gp_cameraInformations);
                event->map.insert("summary", TQVariant(summary));
                event->map.insert("manual", TQVariant(manual));
                event->map.insert("about", TQVariant(about));
                TQApplication::postEvent(parent, event);
                break;
            }            
            case(CameraCommand::gp_listfolders):
            {
                sendInfo(i18n("Listing folders..."));
    
                TQStringList folderList;
                folderList.append(d->camera->path());
                d->camera->getAllFolders(d->camera->path(), folderList);
    
                /* TODO: ugly hack since qt <= 3.1.2 does not define
                TQStringList with TQDeepCopy as a friend. */
                TQValueList<TQString> flist(folderList);
                
                CameraEvent* event = new CameraEvent(CameraEvent::gp_listedfolders);
                event->map.insert("folders", TQVariant(flist));
                TQApplication::postEvent(parent, event);
            
                sendInfo(i18n("The folders have been listed."));
    
                break;
            }
            case(CameraCommand::gp_listfiles):
            {
                TQString folder = cmd->map["folder"].asString();
                
                sendInfo(i18n("The files in %1 have been listed.").tqarg(folder));
    
                GPItemInfoList itemsList;
                // setting getImageDimensions to false is a huge speedup for UMSCamera
                if (!d->camera->getItemsInfoList(folder, itemsList, false))
                {
                    sendError(i18n("Failed to list files in %1").tqarg(folder));
                }
    
                if (!itemsList.isEmpty())
                {
                    CameraEvent* event = new CameraEvent(CameraEvent::gp_listedfiles);
                    event->map.insert("folder", TQVariant(folder));
                    
                    TQByteArray  ba;
                    TQDataStream ds(ba, IO_WriteOnly);
                    ds << itemsList;
                    
                    event->map.insert("files", TQVariant(ba));
                    TQApplication::postEvent(parent, event);
                }
    
                sendInfo(i18n("Listing files in %1 is complete").tqarg(folder));
                
                break;
            }
            case(CameraCommand::gp_thumbnail):
            {
                TQString folder = cmd->map["folder"].asString();
                TQString file   = cmd->map["file"].asString();
    
                sendInfo(i18n("Getting thumbnails..."));
    
                TQImage thumbnail;
                d->camera->getThumbnail(folder, file, thumbnail);
    
                if (!thumbnail.isNull())
                {
                    thumbnail = thumbnail.smoothScale(ThumbnailSize::Huge, ThumbnailSize::Huge, TQ_ScaleMin);
            
                    CameraEvent* event = new CameraEvent(CameraEvent::gp_thumbnailed);
                    event->map.insert("folder", TQVariant(folder));
                    event->map.insert("file", TQVariant(file));
                    event->map.insert("thumbnail", TQVariant(thumbnail));
                    TQApplication::postEvent(parent, event);
                }
    
                break;
            }
            case(CameraCommand::gp_exif):
            {
                TQString folder = cmd->map["folder"].asString();
                TQString file   = cmd->map["file"].asString();

                sendInfo(i18n("Getting EXIF information for %1/%2...").tqarg(folder).tqarg(file));

                char* edata = 0;
                int   esize = 0;
                d->camera->getExif(folder, file, &edata, esize);

                if (edata || esize)
                {
                    TQByteArray  ba;
                    TQDataStream ds(ba, IO_WriteOnly);
                    ds.writeRawBytes(edata, esize);
                    delete [] edata;

                    CameraEvent* event = new CameraEvent(CameraEvent::gp_exif);
                    event->map.insert("folder", TQVariant(folder));
                    event->map.insert("file", TQVariant(file));
                    event->map.insert("exifSize", TQVariant(esize));
                    event->map.insert("exifData", TQVariant(ba));
                    TQApplication::postEvent(parent, event);
                }
                break;
            }
            case(CameraCommand::gp_download):
            {
                TQString   folder            = cmd->map["folder"].asString();
                TQString   file              = cmd->map["file"].asString();
                TQString   dest              = cmd->map["dest"].asString();
                bool      autoRotate        = cmd->map["autoRotate"].asBool();
                bool      fixDateTime       = cmd->map["fixDateTime"].asBool();
                TQDateTime newDateTime       = cmd->map["newDateTime"].asDateTime();
                bool      setPhotographerId = cmd->map["setPhotographerId"].asBool();
                TQString   author            = cmd->map["author"].asString();
                TQString   authorTitle       = cmd->map["authorTitle"].asString();
                bool      setCredits        = cmd->map["setCredits"].asBool();
                TQString   credit            = cmd->map["credit"].asString();
                TQString   source            = cmd->map["source"].asString();
                TQString   copyright         = cmd->map["copyright"].asString();
                bool      convertJpeg       = cmd->map["convertJpeg"].asBool();
                TQString   losslessFormat    = cmd->map["losslessFormat"].asString();
                sendInfo(i18n("Downloading file %1...").tqarg(file));

                // download to a temp file

                CameraEvent* event = new CameraEvent(CameraEvent::gp_downloadstarted);
                event->map.insert("folder", TQVariant(folder));
                event->map.insert("file", TQVariant(file));
                event->map.insert("dest", TQVariant(dest));
                TQApplication::postEvent(parent, event);

                KURL tempURL(dest);
                tempURL      = tempURL.upURL();
                tempURL.addPath( TQString(".digikam-camera-tmp1-%1").tqarg(getpid()).append(file));
                DDebug() << "Downloading: " << file << " using (" << tempURL.path() << ")" << endl;
                TQString temp = tempURL.path();

                bool result  = d->camera->downloadItem(folder, file, tempURL.path());

                if (result && isJpegImage(tempURL.path()))
                {
                    if (autoRotate)
                    {
                        DDebug() << "Exif autorotate: " << file << " using (" << tempURL.path() << ")" << endl;
                        sendInfo(i18n("EXIF rotating file %1...").tqarg(file));
                        exifRotate(tempURL.path(), file);
                    }

                    if (fixDateTime || setPhotographerId || setCredits)
                    {
                        DDebug() << "Set Metadata from: " << file << " using (" << tempURL.path() << ")" << endl;
                        sendInfo(i18n("Setting Metadata tags to file %1...").tqarg(file));
                        DMetadata metadata(tempURL.path());

                        if (fixDateTime)
                            metadata.setImageDateTime(newDateTime, true);

                        if (setPhotographerId)
                            metadata.setImagePhotographerId(author, authorTitle);

                        if (setCredits)
                            metadata.setImageCredits(credit, source, copyright);

                        metadata.applyChanges();
                    }

                    // Convert Jpeg file to lossless format if necessary, 
                    // and move converted image to destination.

                    if (convertJpeg)
                    {
                        DDebug() << "Convert to LossLess: " << file << " using (" << tempURL.path() << ")" << endl;
                        sendInfo(i18n("Converting %1 to lossless file format...").tqarg(file));

                        KURL tempURL2(dest);
                        tempURL2 = tempURL2.upURL();
                        tempURL2.addPath( TQString(".digikam-camera-tmp2-%1").tqarg(getpid()).append(file));
                        temp     = tempURL2.path();

                        if (!jpegConvert(tempURL.path(), tempURL2.path(), file, losslessFormat))
                        {
                            // convert failed. delete the temp file
                            unlink(TQFile::encodeName(tempURL.path()));
                            unlink(TQFile::encodeName(tempURL2.path()));
                            result = false;
                        }
                        else
                        {
                            // Else remove only the first temp file.
                            unlink(TQFile::encodeName(tempURL.path()));
                        }
                    }
                }

                if (result)
                {
                    CameraEvent* event = new CameraEvent(CameraEvent::gp_downloaded);
                    event->map.insert("folder", TQVariant(folder));
                    event->map.insert("file", TQVariant(file));
                    event->map.insert("dest", TQVariant(dest));
                    event->map.insert("temp", TQVariant(temp));
                    TQApplication::postEvent(parent, event);
                }
                else
                {
                    CameraEvent* event = new CameraEvent(CameraEvent::gp_downloadFailed);
                    event->map.insert("folder", TQVariant(folder));
                    event->map.insert("file", TQVariant(file));
                    event->map.insert("dest", TQVariant(dest));
                    TQApplication::postEvent(parent, event);
                }
                break;
            }
            case(CameraCommand::gp_open):
            {
                TQString folder = cmd->map["folder"].asString();
                TQString file   = cmd->map["file"].asString();
                TQString dest   = cmd->map["dest"].asString();
    
                sendInfo(i18n("Retrieving file %1 from camera...").tqarg(file));
    
                bool result = d->camera->downloadItem(folder, file, dest);
    
                if (result)
                {
                    CameraEvent* event = new CameraEvent(CameraEvent::gp_opened);
                    event->map.insert("folder", TQVariant(folder));
                    event->map.insert("file", TQVariant(file));
                    event->map.insert("dest", TQVariant(dest));
                    TQApplication::postEvent(parent, event);
                }
                else
                {
                    sendError(i18n("Failed to retrieve file %1 from camera").tqarg(file));
                }                
                break;
            }
            case(CameraCommand::gp_upload):
            {                
                TQString folder = cmd->map["destFolder"].asString();
    
                // We will using the same source file name to create the dest file 
                // name in camera.
                TQString file   = cmd->map["destFile"].asString();
                
                // The source file path to download in camera.
                TQString src    = cmd->map["srcFilePath"].asString();
    
                sendInfo(i18n("Uploading file %1 to camera...").tqarg(file));
    
                GPItemInfo itemsInfo;

                bool result = d->camera->uploadItem(folder, file, src, itemsInfo);
    
                if (result)
                {
                    CameraEvent* event = new CameraEvent(CameraEvent::gp_uploaded);
                    TQByteArray  ba;
                    TQDataStream ds(ba, IO_WriteOnly);
                    ds << itemsInfo;                    
                    event->map.insert("info", TQVariant(ba));

                    TQApplication::postEvent(parent, event);
                }
                else
                {
                    CameraEvent* event = new CameraEvent(CameraEvent::gp_uploadFailed);
                    event->map.insert("folder", TQVariant(folder));
                    event->map.insert("file", TQVariant(file));
                    event->map.insert("src", TQVariant(src));
                    TQApplication::postEvent(parent, event);
                }                
                break;
            }
            case(CameraCommand::gp_delete):
            {
                TQString folder = cmd->map["folder"].asString();
                TQString file   = cmd->map["file"].asString();
    
                sendInfo(i18n("Deleting file %1...").tqarg(file));
    
                bool result = d->camera->deleteItem(folder, file);
    
                if (result)
                {
                    CameraEvent* event = new CameraEvent(CameraEvent::gp_deleted);
                    event->map.insert("folder", TQVariant(folder));
                    event->map.insert("file", TQVariant(file));
                    TQApplication::postEvent(parent, event);
                }
                else
                {
                    CameraEvent* event = new CameraEvent(CameraEvent::gp_deleteFailed);
                    event->map.insert("folder", TQVariant(folder));
                    event->map.insert("file", TQVariant(file));
                    TQApplication::postEvent(parent, event);
                }                
                break;
            }
            case(CameraCommand::gp_lock):
            {
                TQString folder = cmd->map["folder"].asString();
                TQString file   = cmd->map["file"].asString();
                bool    lock   = cmd->map["lock"].asBool();
    
                sendInfo(i18n("Toggle lock file %1...").tqarg(file));
    
                bool result = d->camera->setLockItem(folder, file, lock);
    
                if (result)
                {
                    CameraEvent* event = new CameraEvent(CameraEvent::gp_locked);
                    event->map.insert("folder", TQVariant(folder));
                    event->map.insert("file", TQVariant(file));
                    TQApplication::postEvent(parent, event);
                }
                else
                {
                    CameraEvent* event = new CameraEvent(CameraEvent::gp_lockFailed);
                    event->map.insert("folder", TQVariant(folder));
                    event->map.insert("file", TQVariant(file));
                    TQApplication::postEvent(parent, event);
                }                
                break;
            }
            default:
                DWarning() << k_funcinfo << " unknown action specified" << endl;
        }    

        delete cmd;
    }

    sendBusy(false);
}

void CameraThread::sendBusy(bool val)
{
    CameraEvent* event = new CameraEvent(CameraEvent::gp_busy);
    event->result = val;
    TQApplication::postEvent(parent, event);
}

void CameraThread::sendError(const TQString& msg)
{
    CameraEvent* event = new CameraEvent(CameraEvent::gp_errormsg);
    event->msg = msg;
    TQApplication::postEvent(parent, event);
}

void CameraThread::sendInfo(const TQString& msg)
{
    CameraEvent* event = new CameraEvent(CameraEvent::gp_infomsg);
    event->msg = msg;
    TQApplication::postEvent(parent, event);
}


//-- Camera Controller ------------------------------------------------------


CameraController::CameraController(TQWidget* parent, const TQString& title, const TQString& model,
                                   const TQString& port, const TQString& path)
                : TQObject(parent)
{
    d = new CameraControllerPriv;   
    d->parent        = parent;
    d->canceled      = false;
    d->close         = false;
    d->overwriteAll  = false;
    d->skipAll       = false;
    d->downloadTotal = 0;
    d->camera        = 0;

    // URL parsing (c) Stephan Kulow
    if (path.startsWith("camera:/"))
    {
        KURL url(path);
        DDebug() << "path " << path << " " << url <<  " " << url.host() << endl;
        TQString xport = url.host();
        if (xport.startsWith("usb:"))
        {
            DDebug() << "xport " << xport << endl;
            TQRegExp x = TQRegExp("(usb:[0-9,]*)");

            if (x.search(xport) != -1) 
            {
                TQString usbport = x.cap(1);
                DDebug() << "USB " << xport << " " << usbport << endl;
                // if ((xport == usbport) || ((count == 1) && (xport == "usb:"))) {
                //   model = xmodel;
                d->camera = new GPCamera(title, url.user(), "usb:", "/");
                // }
            }
        }
    }

    if (!d->camera)
    {
        if (model.lower() == "directory browse")
            d->camera = new UMSCamera(title, model, port, path);
        else
            d->camera = new GPCamera(title, model, port, path);
    }

    d->thread = new CameraThread(this);
    d->timer  = new TQTimer(this);

    connect(d->timer, TQT_SIGNAL(timeout()),
            this, TQT_SLOT(slotProcessNext()));

    d->timer->start(50, false);
}

CameraController::~CameraController()
{
    if (d->timer->isActive()) 
    {
        d->timer->stop();
        delete d->timer;
    }
    
    d->camera->cancel();
    d->canceled = true;
    d->close    = true;

    while (d->thread->running())
        d->thread->wait();
        
    delete d->thread;
    delete d->camera;
    delete d;
}

TQString CameraController::getCameraPath()
{
    if (!d->camera) return TQString();
    return d->camera->path();
}

TQString CameraController::getCameraTitle()
{
    if (!d->camera) return TQString();
    return d->camera->title();
}

void CameraController::slotConnect()
{
    d->canceled = false;
    CameraCommand *cmd = new CameraCommand;
    cmd->action = CameraCommand::gp_connect;
    d->cmdQueue.enqueue(cmd);
}

void CameraController::listFolders()
{
    d->canceled = false;
    CameraCommand *cmd = new CameraCommand;
    cmd->action = CameraCommand::gp_listfolders;
    d->cmdQueue.enqueue(cmd);
}

void CameraController::listFiles(const TQString& folder)
{
    d->canceled = false;
    CameraCommand *cmd = new CameraCommand;
    cmd->action = CameraCommand::gp_listfiles;
    cmd->map.insert("folder", TQVariant(folder));
    d->cmdQueue.enqueue(cmd);
}

void CameraController::getThumbnail(const TQString& folder, const TQString& file)
{
    d->canceled = false;
    CameraCommand *cmd = new CameraCommand;
    cmd->action = CameraCommand::gp_thumbnail;
    cmd->map.insert("folder", TQVariant(folder));
    cmd->map.insert("file", TQVariant(file));
    d->cmdQueue.enqueue(cmd);
}

void CameraController::getExif(const TQString& folder, const TQString& file)
{
    d->canceled = false;
    CameraCommand *cmd = new CameraCommand;
    cmd->action = CameraCommand::gp_exif;
    cmd->map.insert("folder", TQVariant(folder));
    cmd->map.insert("file", TQVariant(file));
    d->cmdQueue.enqueue(cmd);
}

void CameraController::getCameraInformations()
{
    d->canceled = false;
    CameraCommand *cmd = new CameraCommand;
    cmd->action = CameraCommand::gp_cameraInformations;
    d->cmdQueue.enqueue(cmd);
}

void CameraController::upload(const TQFileInfo& srcFileInfo, const TQString& destFile, const TQString& destFolder)
{
    d->canceled = false;
    CameraCommand *cmd = new CameraCommand;
    cmd->action = CameraCommand::gp_upload;
    cmd->map.insert("srcFilePath", TQVariant(srcFileInfo.filePath()));
    cmd->map.insert("destFile", TQVariant(destFile));
    cmd->map.insert("destFolder", TQVariant(destFolder));
    d->cmdQueue.enqueue(cmd);
    DDebug() << "Uploading '" << srcFileInfo.filePath() << "' into camera : '" << destFolder << 
                 "' (" << destFile << ")" << endl;
}

void CameraController::downloadPrep()
{
    d->overwriteAll  = false;
    d->skipAll       = false;
    d->downloadTotal = 0;
}

void CameraController::download(DownloadSettingsContainer downloadSettings)
{
    d->canceled = false;
    CameraCommand *cmd = new CameraCommand;
    cmd->action = CameraCommand::gp_download;
    cmd->map.insert("folder", TQVariant(downloadSettings.folder));
    cmd->map.insert("file", TQVariant(downloadSettings.file));
    cmd->map.insert("dest", TQVariant(downloadSettings.dest));
    cmd->map.insert("autoRotate", TQVariant(downloadSettings.autoRotate, 0));
    cmd->map.insert("fixDateTime", TQVariant(downloadSettings.fixDateTime, 0));
    cmd->map.insert("newDateTime", TQVariant(downloadSettings.newDateTime));
    cmd->map.insert("setPhotographerId", TQVariant(downloadSettings.setPhotographerId, 0));
    cmd->map.insert("author", TQVariant(downloadSettings.author));
    cmd->map.insert("authorTitle", TQVariant(downloadSettings.authorTitle));
    cmd->map.insert("setCredits", TQVariant(downloadSettings.setCredits, 0));
    cmd->map.insert("credit", TQVariant(downloadSettings.credit));
    cmd->map.insert("source", TQVariant(downloadSettings.source));
    cmd->map.insert("copyright", TQVariant(downloadSettings.copyright));
    cmd->map.insert("convertJpeg", TQVariant(downloadSettings.convertJpeg, 0));
    cmd->map.insert("losslessFormat", TQVariant(downloadSettings.losslessFormat));
    d->cmdQueue.enqueue(cmd);
}

void CameraController::deleteFile(const TQString& folder, const TQString& file)
{
    d->canceled = false;
    CameraCommand *cmd = new CameraCommand;
    cmd->action = CameraCommand::gp_delete;
    cmd->map.insert("folder", TQVariant(folder));
    cmd->map.insert("file", TQVariant(file));
    d->cmdQueue.enqueue(cmd);
}

void CameraController::lockFile(const TQString& folder, const TQString& file, bool lock)
{
    d->canceled = false;
    CameraCommand *cmd = new CameraCommand;
    cmd->action = CameraCommand::gp_lock;
    cmd->map.insert("folder", TQVariant(folder));
    cmd->map.insert("file", TQVariant(file));
    cmd->map.insert("lock", TQVariant(lock, 0));
    d->cmdQueue.enqueue(cmd);
}

void CameraController::openFile(const TQString& folder, const TQString& file)
{
    d->canceled = false;
    CameraCommand *cmd = new CameraCommand;
    cmd->action = CameraCommand::gp_open;
    cmd->map.insert("folder", TQVariant(folder));
    cmd->map.insert("file", TQVariant(file));
    cmd->map.insert("dest", TQVariant(locateLocal("tmp", file)));
    d->cmdQueue.enqueue(cmd);
}

void CameraController::slotCancel()
{
    d->canceled = true;
    d->cmdQueue.flush();   
    d->camera->cancel();
}

void CameraController::customEvent(TQCustomEvent* e)
{
    CameraEvent* event = dynamic_cast<CameraEvent*>(e);
    if (!event)
    {
        return;
    }
    
    switch(event->type()-TQEvent::User)
    {
        case (CameraEvent::gp_connected) :
        {
            emit signalConnected(event->result);
            break;
        }
        case (CameraEvent::gp_cameraInformations) :
        {
            TQString summary = TQDeepCopy<TQString>(event->map["summary"].asString());
            TQString manual  = TQDeepCopy<TQString>(event->map["manual"].asString());
            TQString about   = TQDeepCopy<TQString>(event->map["about"].asString());
            emit signalCameraInformations(summary, manual, about);
            break;
        }
        case (CameraEvent::gp_errormsg) :
        {
            emit signalErrorMsg(TQDeepCopy<TQString>(event->msg));
            break;
        }
        case (CameraEvent::gp_busy) :
        {
            if (event->result)
                emit signalBusy(true);
            break;
        }
        case (CameraEvent::gp_infomsg) :
        {
            if (!d->canceled)
                emit signalInfoMsg(TQDeepCopy<TQString>(event->msg));
            break;
        }
        case (CameraEvent::gp_listedfolders) :
        {
            /* TODO: ugly hack since qt <= 3.1.2 does not define
            TQStringList with TQDeepCopy as a friend. */
            TQValueList<TQVariant> flist = TQDeepCopy< TQValueList<TQVariant> >(event->map["folders"].toList());
    
            TQStringList folderList;
            TQValueList<TQVariant>::Iterator it;
            for (it = flist.begin(); it != flist.end(); ++it )
            {
                folderList.append(TQDeepCopy<TQString>((*it).asString()));
            }
            
            emit signalFolderList(folderList);
            break;
        }
        case (CameraEvent::gp_listedfiles) :
        {
            TQString    folder = TQDeepCopy<TQString>(event->map["folder"].asString());
            TQByteArray ba     = TQDeepCopy<TQByteArray>(event->map["files"].asByteArray());
            TQDataStream ds(ba, IO_ReadOnly);
            GPItemInfoList items;
            ds >> items;
            emit signalFileList(items);
            break;
        }
        case (CameraEvent::gp_thumbnailed) :
        {
            TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString());
            TQString file   = TQDeepCopy<TQString>(event->map["file"].asString());
            TQImage  thumb  = TQDeepCopy<TQImage>(event->map["thumbnail"].asImage());
            emit signalThumbnail(folder, file, thumb);
            break;
        }
        case (CameraEvent::gp_exif) :
        {
            TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString());
            TQString file   = TQDeepCopy<TQString>(event->map["file"].asString());
            TQByteArray ba  = TQDeepCopy<TQByteArray>(event->map["exifData"].asByteArray());
            emit signalExifData(ba);
            break;
        }
        case (CameraEvent::gp_downloadstarted) :
        {
            TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString());
            TQString file   = TQDeepCopy<TQString>(event->map["file"].asString());
            emit signalDownloaded(folder, file, GPItemInfo::DownloadStarted);
            break;
        }
        case (CameraEvent::gp_downloaded) :
        {
            TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString());
            TQString file   = TQDeepCopy<TQString>(event->map["file"].asString());
            TQString dest   = TQDeepCopy<TQString>(event->map["dest"].asString());
            TQString temp   = TQDeepCopy<TQString>(event->map["temp"].asString());

            d->timer->stop();

            bool skip      = false;
            bool cancel    = false;
            bool overwrite = false;

            // Check if dest file already exist.

            if (!d->overwriteAll)
            {
                TQFileInfo info(dest);

                while (info.exists())
                {
                    if (d->skipAll)
                    {
                        skip = true;
                        break;
                    }

                    KIO::RenameDlg dlg(d->parent, i18n("Rename File"),
                                       folder + TQString("/") + file, dest,
                                       KIO::RenameDlg_Mode(KIO::M_MULTI | KIO::M_OVERWRITE | KIO::M_SKIP));

                    int result = dlg.exec();
                    dest       = dlg.newDestURL().path();
                    info       = TQFileInfo(dest);

                    switch (result)
                    {
                        case KIO::R_CANCEL:
                        {
                            cancel = true;
                            break;
                        }
                        case KIO::R_SKIP:
                        {
                            skip = true;
                            break;
                        }
                        case KIO::R_AUTO_SKIP:
                        {
                            d->skipAll = true;
                            skip       = true;
                            break;
                        }
                        case KIO::R_OVERWRITE:
                        {
                            overwrite = true;
                            break;
                        }
                        case KIO::R_OVERWRITE_ALL:
                        {
                            d->overwriteAll = true;
                            overwrite       = true;
                            break;
                        }
                        default:
                            break;
                    }

                    if (cancel || skip || overwrite)
                        break;
                }
            }

            if (cancel)
            {
                unlink(TQFile::encodeName(temp));
                slotCancel();
                d->timer->start(50);
                emit signalSkipped(folder, file);
                return;
            }
            else if (skip)
            {
                unlink(TQFile::encodeName(temp));
                d->timer->start(50);
                emit signalInfoMsg(i18n("Skipped file %1").tqarg(file));
                emit signalSkipped(folder, file);
                return;
            }

            // move the file to the destination file
            if (rename(TQFile::encodeName(temp), TQFile::encodeName(dest)) != 0)
            {
                // rename failed. delete the temp file
                unlink(TQFile::encodeName(temp));
                d->timer->start(50);
                emit signalDownloaded(folder, file, GPItemInfo::DownloadFailed);
            }
            else
            {
                d->timer->start(50);
                emit signalDownloaded(folder, file, GPItemInfo::DownloadedYes);
            }
            break;
        }
        case (CameraEvent::gp_downloadFailed) :
        {
            TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString());
            TQString file   = TQDeepCopy<TQString>(event->map["file"].asString());

            d->timer->stop();

            TQString msg = i18n("Failed to download file \"%1\".").tqarg(file);

            if (!d->canceled)
            {
                if (d->cmdQueue.isEmpty())
                {
                    KMessageBox::error(d->parent, msg);
                }
                else
                {
                    msg += i18n(" Do you want to continue?");
                    int result = KMessageBox::warningContinueCancel(d->parent, msg);
                    if (result != KMessageBox::Continue)
                        slotCancel();  
                }
            }

            d->timer->start(50);
            emit signalDownloaded(folder, file, GPItemInfo::DownloadFailed);
            break;
        }
        case (CameraEvent::gp_uploaded) :
        {
            TQByteArray ba  = TQDeepCopy<TQByteArray>(event->map["info"].asByteArray());
            TQDataStream ds(ba, IO_ReadOnly);
            GPItemInfo itemInfo;
            ds >> itemInfo;

            emit signalUploaded(itemInfo);
            break;
        }
        case (CameraEvent::gp_uploadFailed) :
        {
            TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString());
            TQString file   = TQDeepCopy<TQString>(event->map["file"].asString());
            TQString src    = TQDeepCopy<TQString>(event->map["src"].asString());

            d->timer->stop();

            TQString msg = i18n("Failed to upload file \"%1\".").tqarg(file);

            if (!d->canceled)
            {
                if (d->cmdQueue.isEmpty())
                {
                    KMessageBox::error(d->parent, msg);
                }
                else
                {
                    msg += i18n(" Do you want to continue?");
                    int result = KMessageBox::warningContinueCancel(d->parent, msg);
                    if (result != KMessageBox::Continue)
                        slotCancel();
                }
            }

            d->timer->start(50);
            break;
        }
        case (CameraEvent::gp_deleted) :
        {
            TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString());
            TQString file   = TQDeepCopy<TQString>(event->map["file"].asString());
            emit signalDeleted(folder, file, true);
            break;
        }
        case (CameraEvent::gp_deleteFailed) :
        {
            TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString());
            TQString file   = TQDeepCopy<TQString>(event->map["file"].asString());

            d->timer->stop();
            emit signalDeleted(folder, file, false);

            TQString msg = i18n("Failed to delete file \"%1\".").tqarg(file);

            if (!d->canceled)
            {
                if (d->cmdQueue.isEmpty())
                {
                    KMessageBox::error(d->parent, msg);
                }
                else
                {
                    msg += i18n(" Do you want to continue?");
                    int result = KMessageBox::warningContinueCancel(d->parent, msg);
                    if (result != KMessageBox::Continue)
                        slotCancel();
                }
            }

            d->timer->start(50);
            break;
        }
        case (CameraEvent::gp_locked) :
        {
            TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString());
            TQString file   = TQDeepCopy<TQString>(event->map["file"].asString());
            emit signalLocked(folder, file, true);
            break;
        }
        case (CameraEvent::gp_lockFailed) :
        {
            TQString folder = TQDeepCopy<TQString>(event->map["folder"].asString());
            TQString file   = TQDeepCopy<TQString>(event->map["file"].asString());

            d->timer->stop();
            emit signalLocked(folder, file, false);

            TQString msg = i18n("Failed to toggle lock file \"%1\".").tqarg(file);

            if (!d->canceled)
            {
                if (d->cmdQueue.isEmpty())
                {
                    KMessageBox::error(d->parent, msg);
                }
                else
                {
                    msg += i18n(" Do you want to continue?");
                    int result = KMessageBox::warningContinueCancel(d->parent, msg);
                    if (result != KMessageBox::Continue)
                        slotCancel();
                }
            }

            d->timer->start(50);
            break;
        }
        case (CameraEvent::gp_opened) :
        {
            TQString file = TQDeepCopy<TQString>(event->map["file"].asString());
            TQString dest = TQDeepCopy<TQString>(event->map["dest"].asString());

            KURL url(dest);
            KURL::List urlList;
            urlList << url;

            ImageWindow *im = ImageWindow::imagewindow();
            im->loadURL(urlList, url, i18n("Camera \"%1\"").tqarg(d->camera->model()), false);

            if (im->isHidden())
                im->show();
            else
                im->raise();

            im->setFocus();
            break;
        }
        default:
        {
            DWarning() << k_funcinfo << "Unknown event" << endl;
        }
    }
}

void CameraController::slotProcessNext()
{
    if (d->thread->running())
        return;

    if (d->cmdQueue.isEmpty())
    {
        emit signalBusy(false);
        return;
    }

    d->timer->stop();
    emit signalBusy(true);

    CameraCommand* cmd = d->cmdQueue.head();

    TQString folder;
    TQString file;
    TQString dest;

    if ((cmd->action == CameraCommand::gp_exif) &&
        (typeid(*(d->camera)) == typeid(UMSCamera)))
    {
        folder = TQDeepCopy<TQString>(cmd->map["folder"].asString());
        file   = TQDeepCopy<TQString>(cmd->map["file"].asString());

        emit signalExifFromFile(folder, file);

        d->cmdQueue.dequeue();
        d->timer->start(50, false);
        return;
    }

    if (cmd->action == CameraCommand::gp_download)
    {
        folder = TQDeepCopy<TQString>(cmd->map["folder"].asString());
        file   = TQDeepCopy<TQString>(cmd->map["file"].asString());
        dest   = TQDeepCopy<TQString>(cmd->map["dest"].asString());
        cmd->map["dest"] = TQVariant(TQDeepCopy<TQString>(dest));
    }

    d->thread->start();
    d->timer->start(50, false);
}

}  // namespace Digikam
