/*
 *     Copyright (C) 2007 by Mathias Soeken <msoeken@tzi.de>
 *
 *   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 of the License, 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.
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this program; if not, write to the
 *      Free Software Foundation, Inc.
 *      51 Franklin Street, Fifth Floor
 *      Boston, MA  02110-1301  USA.
 *
 */

// TODO: what is the sense of tasksChanged()?

#include "timetrackerwidget.h"
#include "ktimetrackerconfigdialog.h"

#include <QDBusConnection>
#include <QFileInfo>
#include <QHBoxLayout>
#include <QKeyEvent>
#include <QKeySequence>
#include <QMap>
#include <QVBoxLayout>
#include <QVector>

#include <KApplication>
#include <KAction>
#include <KActionCollection>
#include <KConfig>
#include <KConfigDialog>
#include <KDebug>
#include <KFileDialog>
#include <KGlobal>
#include <KIcon>
#include <KLocale>
#include <KMessageBox>
#include <KRecentFilesAction>
#include <KStandardAction>
#include <KTabWidget>
#include <KTemporaryFile>
#include <KTreeWidgetSearchLine>
#include <KUrl>
#include <KIO/Job>

#include "historydialog.h"
#include "idletimedetector.h"
#include "ktimetrackerutility.h"
#include "ktimetracker.h"
#include "mainadaptor.h"
#include "reportcriteria.h"
#include "task.h"
#include "taskview.h"
#include "kdepim-version.h"


//@cond PRIVATE
class TimetrackerWidget::Private
{
  public:
    Private() :
      mTaskView( 0 ) {}

    QWidget *mSearchLine;
    KTreeWidgetSearchLine *mSearchWidget;
    TaskView *mTaskView;
    QMap<QString, KAction*> mActions;

    struct ActionData
    {
        QString iconName;
        const char* caption;
        const char* slot;
        QString name;
        const char* toolTip;
        const char* whatsThis;
    };
};
//@endcond

TimetrackerWidget::TimetrackerWidget( QWidget *parent ) : QWidget( parent ),
  d( new TimetrackerWidget::Private() )
{
    kDebug(5970) << "Entering function";
    new MainAdaptor( this );
    QDBusConnection::sessionBus().registerObject( "/KTimeTracker", this );

    QLayout *layout = new QVBoxLayout;
    layout->setMargin( 0 );
    layout->setSpacing( 0 );

    QLayout *innerLayout = new QHBoxLayout;
    d->mSearchLine = new QWidget( this );
    innerLayout->setMargin( KDialog::marginHint() );
    innerLayout->setSpacing( KDialog::spacingHint() );
    d->mSearchWidget = new KTreeWidgetSearchLine( d->mSearchLine );
    d->mSearchWidget->setClickMessage( i18n( "Search or add task" ) );
    d->mSearchWidget->setWhatsThis( i18n( "This is a combined field. As long as you do not type ENTER, it acts as a filter. Then, only tasks that match your input are shown. As soon as you type ENTER, your input is used as name to create a new task." ) );
    d->mSearchWidget->installEventFilter( this );
    innerLayout->addWidget( d->mSearchWidget );
    d->mSearchLine->setLayout( innerLayout );

    d->mTaskView = new TaskView( this );
    layout->addWidget( d->mSearchLine );
    layout->addWidget( d->mTaskView );
    setLayout( layout );

    showSearchBar( !KTimeTrackerSettings::configPDA() && KTimeTrackerSettings::showSearchBar() );
}

TimetrackerWidget::~TimetrackerWidget()
{
    delete d;
}

bool TimetrackerWidget::allEventsHaveEndTiMe()
{
    return currentTaskView()->allEventsHaveEndTiMe();
}

int TimetrackerWidget::focusSearchBar()
{
    kDebug(5970) << "Entering function";
    if ( d->mSearchWidget->isVisible() ) d->mSearchWidget->setFocus();
    return 0;
}

void TimetrackerWidget::addTaskView( const QString &fileName )
{
    kDebug(5970) << "Entering function (fileName=" << fileName << ")";
    bool isNew = fileName.isEmpty();
    QString lFileName = fileName;

    if ( isNew )
    {
        KTemporaryFile tempFile;
        tempFile.setAutoRemove( false );
        if ( tempFile.open() )
        {
            lFileName = tempFile.fileName();
            tempFile.close();
        }
        else
        {
            KMessageBox::error( this, i18n( "Cannot create new file." ) );
            return;
        }
    }

    TaskView *taskView = d->mTaskView;

    connect( taskView, SIGNAL(contextMenuRequested(QPoint)),
           this, SIGNAL(contextMenuRequested(QPoint)) );

    emit setCaption( fileName );
    taskView->load( lFileName );
    d->mSearchWidget->addTreeWidget( taskView );

    // When adding the first tab currentChanged is not emitted, so...
    if ( !d->mTaskView )
    {
        emit currentTaskViewChanged();
        slotCurrentChanged();
    }
}

TaskView* TimetrackerWidget::currentTaskView() const
{
    return qobject_cast< TaskView* >( d->mTaskView );
}

Task* TimetrackerWidget::currentTask()
{
    TaskView *taskView = 0;
    if ( ( taskView = currentTaskView() ) )
    {
        return taskView->currentItem();
    }
    else
    {
        return 0;
    }
}

void TimetrackerWidget::setupActions( KActionCollection *actionCollection )
{
    d->mActions.insert ("file_open",
        KStandardAction::open( this, SLOT(openFile()), actionCollection ) );
    d->mActions.insert ("file_save",
        KStandardAction::save( this, SLOT(saveFile()), actionCollection ) );
    d->mActions.insert ("file_quit",
        KStandardAction::quit( this, SLOT(quit()), actionCollection ) );
    d->mActions.insert ("configure_ktimetracker",
        KStandardAction::preferences( this, SLOT(showSettingsDialog()),
                                  actionCollection ) );

    Private::ActionData actions[] =
    {
        { QString(), I18N_NOOP("Start &New Session"), SLOT(startNewSession()),
            "start_new_session", I18N_NOOP("Starts a new session"), I18N_NOOP("This will reset the "
            "session time to 0 for all tasks, to start a new session, without "
            "affecting the totals.")
        },
        { "view-history", I18N_NOOP("Edit History..."), SLOT(editHistory()), "edit_history",
            I18N_NOOP("Edits history of all tasks of the current document"), I18N_NOOP("A window will "
            "be opened where you can change start and stop times of tasks or add a "
            "comment to them.")
        },
        { QString(), I18N_NOOP("&Reset All Times"), SLOT(resetAllTimes()),
            "reset_all_times", I18N_NOOP("Resets all times"), I18N_NOOP("This will reset the session "
            "and total time to 0 for all tasks, to restart from scratch.")
        },
        { "media-playback-start", I18N_NOOP("&Start"), SLOT(startCurrentTimer()), "start",
            I18N_NOOP("Starts timing for selected task"), I18N_NOOP("This will start timing for the "
            "selected task.\nIt is even possible to time several tasks "
            "simultanously.\n\nYou may also start timing of tasks by double clicking "
            "the left mouse button on a given task. This will, however, stop timing "
            "of other tasks.")
        },
        { "media-playback-stop", I18N_NOOP("S&top"), SLOT(stopCurrentTimer()), "stop",
            I18N_NOOP("Stops timing of the selected task"), I18N_NOOP("Stops timing of the selected task")
        },
        { QString(), I18N_NOOP("Focus on Searchbar"), SLOT(focusSearchBar()), "focusSearchBar",
            I18N_NOOP("Sets the focus on the searchbar"), I18N_NOOP("Sets the focus on the searchbar")
        },
        { QString(), I18N_NOOP("Stop &All Timers"), SLOT(stopAllTimers()), "stopAll",
            I18N_NOOP("Stops all of the active timers"), I18N_NOOP("Stops all of the active timers")
        },
        { QString(), I18N_NOOP("Track Active Applications"), SLOT(focusTracking()),
            "focustracking", I18N_NOOP("Auto-creates and updates tasks when the focus of the "
            "current window has changed"), I18N_NOOP("If the focus of a window changes for the "
            "first time when this action is enabled, a new task will be created "
            "with the title of the window as its name and will be started. If there "
            "already exists such an task it will be started.")
        },
        // in the following element,
        // document-new is the icon name, e.g. for the toolbar, new_task is how the action is
        // called in the ktimetrackerui.rc file, rest should be obvious
        { "document-new", I18N_NOOP("&New Task..."), SLOT(newTask()), "new_task", I18N_NOOP("Creates "
            "new top level task"), I18N_NOOP("This will create a new top level task.")
        },
        { "subtask-new-ktimetracker", I18N_NOOP("New &Subtask..."), SLOT(newSubTask()),
            "new_sub_task", I18N_NOOP("Creates a new subtask to the current selected task"),
            I18N_NOOP("This will create a new subtask to the current selected task.")
        },
        { "edit-delete", I18N_NOOP("&Delete"), SLOT(deleteTask()), "delete_task", I18N_NOOP("Deletes "
            "selected task"), I18N_NOOP("This will delete the selected task(s) and all "
            "subtasks.")
        },
        { "document-properties", I18N_NOOP("&Edit..."), SLOT(editTask()), "edit_task",
            I18N_NOOP("Edits name or times for selected task"), I18N_NOOP("This will bring up a dialog "
            "box where you may edit the parameters for the selected task.")
        },
        { QString(), I18N_NOOP("&Mark as Complete"), SLOT(markTaskAsComplete()),
            "mark_as_complete", "", ""
        },
        { QString(), I18N_NOOP("&Mark as Incomplete"), SLOT(markTaskAsIncomplete()),
            "mark_as_incomplete", "", ""
        },
        { QString(), I18N_NOOP("&Export Times..."), SLOT(exportcsvFile()), "export_times",
            "", ""
        },
        { QString(), I18N_NOOP("Export &History..."), SLOT(exportcsvHistory()),
            "export_history", "", ""
        },
        { QString(), I18N_NOOP("Import Tasks From &Planner..."), SLOT(importPlanner()),
            "import_planner", "", ""
        },
        { QString(), I18N_NOOP("Show Searchbar"), SLOT(slotSearchBar()), "searchbar",
            "", ""
        }
    };

    for ( unsigned int i = 0; i < ( sizeof( actions ) / sizeof( Private::ActionData ) ); ++i )
    {
        Private::ActionData actionData = actions[i];
        KAction *action;
        if ( actionData.iconName.isEmpty() )
        {
            action = new KAction( i18n( actionData.caption ), this );
        }
        else
        {
            action = new KAction( KIcon( actionData.iconName ),
                            i18n( actionData.caption ), this );
        }

        actionCollection->addAction( actionData.name, action );
        connect( action, SIGNAL(triggered(bool)), actionData.slot );
        action->setToolTip( i18n( actionData.toolTip ) );
        action->setWhatsThis( i18n( actionData.whatsThis ) );

        d->mActions.insert( actionData.name, action );
    }

    // custom shortcuts
    d->mActions[ "start" ]->setShortcut( QKeySequence( Qt::Key_G) );
    d->mActions[ "stopAll" ]->setShortcut( QKeySequence( Qt::Key_Escape ) );
    d->mActions[ "new_task" ]->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_T ) );
    d->mActions[ "focusSearchBar" ]->setShortcut( QKeySequence( Qt::Key_S ) );
    d->mActions[ "new_sub_task" ]->setShortcut( QKeySequence( Qt::CTRL + Qt::ALT + Qt::Key_N ) );
    d->mActions[ "delete_task" ]->setShortcut( QKeySequence( Qt::Key_Delete ) );
    d->mActions[ "edit_task" ]->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_E ) );
    d->mActions[ "mark_as_complete" ]->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_M ) );
    d->mActions[ "mark_as_incomplete" ]->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_M ) );

    d->mActions[ "mark_as_complete" ]->setIcon( UserIcon( "task-complete.xpm" ) );
    d->mActions[ "mark_as_incomplete" ]->setIcon( UserIcon( "task-incomplete.xpm" ) );

    d->mActions[ "focustracking" ]->setCheckable( true );
    d->mActions[ "searchbar" ]->setCheckable( true );

    d->mActions[ "searchbar" ]->setChecked( KTimeTrackerSettings::self()->showSearchBar() );

    connect( this, SIGNAL(currentTaskChanged()),
             this, SLOT(slotUpdateButtons()) );
    connect( this, SIGNAL(currentTaskViewChanged()),
             this, SLOT(slotUpdateButtons()) );
    connect( this, SIGNAL(updateButtons()),
             this, SLOT(slotUpdateButtons()) );
}

KAction* TimetrackerWidget::action( const QString &name ) const
{
    return d->mActions.value( name );
}

void TimetrackerWidget::openFile( const QString &fileName )
{
    kDebug(5970) << "Entering function, fileName is " << fileName;
    QString newFileName = fileName;
    if ( newFileName.isEmpty() )
    {
        newFileName = KFileDialog::getOpenFileName( QString(), QString(), this );
        if ( newFileName.isEmpty() )
        {
            return;
        }
    }
    addTaskView( newFileName );
}

void TimetrackerWidget::openFile( const KUrl &fileName )
{
    openFile( fileName.toLocalFile() );
}

bool TimetrackerWidget::closeFile()
{
    kDebug(5970) << "Entering TimetrackerWidget::closeFile";
    TaskView *taskView = currentTaskView();

    if ( taskView )
    {
        taskView->save();
        taskView->closeStorage();
    }

    d->mSearchWidget->removeTreeWidget( taskView );

    emit currentTaskViewChanged();
    emit setCaption( QString() );
    slotCurrentChanged();

    delete taskView; // removeTab does not delete its widget.
    d->mTaskView=0;
    return true;
}

void TimetrackerWidget::saveFile()
{
    currentTaskView()->save();
}

void TimetrackerWidget::showSearchBar( bool visible )
{
    d->mSearchLine->setVisible( visible );
}

bool TimetrackerWidget::closeAllFiles()
{
    kDebug(5970) << "Entering TimetrackerWidget::closeAllFiles";
    bool err = true;
    if (d->mTaskView)
    {
        d->mTaskView->stopAllTimers();
        err=closeFile();
    }
    return err;
}

void TimetrackerWidget::slotCurrentChanged()
{
    kDebug() << "entering KTimetrackerWidget::slotCurrentChanged";

    if ( d->mTaskView )
    {
        disconnect( d->mTaskView, SIGNAL(totalTimesChanged(long,long)) );
        disconnect( d->mTaskView, SIGNAL(reSetTimes()) );
        disconnect( d->mTaskView, SIGNAL(itemSelectionChanged()) );
        disconnect( d->mTaskView, SIGNAL(updateButtons()) );
        disconnect( d->mTaskView, SIGNAL(setStatusBarText(QString)) );
        disconnect( d->mTaskView, SIGNAL(timersActive()) );
        disconnect( d->mTaskView, SIGNAL(timersInactive()) );
        disconnect( d->mTaskView, SIGNAL(tasksChanged(QList<Task*>)),
                this, SIGNAL(tasksChanged(QList<Task*>)) );
        
        connect( d->mTaskView, SIGNAL(totalTimesChanged(long,long)),
            this, SIGNAL(totalTimesChanged(long,long)) );
        connect( d->mTaskView, SIGNAL(reSetTimes()),
            this, SIGNAL(reSetTimes()) );
        connect( d->mTaskView, SIGNAL(itemSelectionChanged()),
            this, SIGNAL(currentTaskChanged()) );
        connect( d->mTaskView, SIGNAL(updateButtons()),
            this, SIGNAL(updateButtons()) );
        connect( d->mTaskView, SIGNAL(setStatusBarText(QString)), // FIXME signature
            this, SIGNAL(statusBarTextChangeRequested(QString)) );
        connect( d->mTaskView, SIGNAL(timersActive()),
            this, SIGNAL(timersActive()) );
        connect( d->mTaskView, SIGNAL(timersInactive()),
            this, SIGNAL(timersInactive()) );
        connect( d->mTaskView, SIGNAL(tasksChanged(QList<Task*>)), // FIXME signature
            this, SIGNAL(tasksChanged(QList<Task*>)) );
        emit setCaption( d->mTaskView->storage()->icalfile() );
    }
    d->mSearchWidget->setEnabled( d->mTaskView );
}


bool TimetrackerWidget::eventFilter( QObject *obj, QEvent *event )
{
    if ( obj == d->mSearchWidget )
    {
        if ( event->type() == QEvent::KeyPress )
        {
            QKeyEvent *keyEvent = static_cast< QKeyEvent* >( event );
            if ( keyEvent->key() == Qt::Key_Enter ||
                keyEvent->key() == Qt::Key_Return )
            {
                if ( !d->mSearchWidget->displayText().isEmpty() ) slotAddTask( d->mSearchWidget->displayText() );
                    return true;
            }
        }
    }
  return QObject::eventFilter( obj, event );
}

void TimetrackerWidget::slotAddTask( const QString &taskName )
{
    TaskView *taskView = currentTaskView();
    taskView->addTask( taskName, QString(), 0, 0, DesktopList(), 0 );

    d->mSearchWidget->clear();
}

void TimetrackerWidget::slotUpdateButtons()
{
    kDebug(5970) << "Entering function";
    Task *item = currentTask();

    d->mActions[ "start" ]->setEnabled( item && !item->isRunning() &&
      !item->isComplete() );
    d->mActions[ "stop" ]->setEnabled( item && item->isRunning() );
    d->mActions[ "delete_task" ]->setEnabled( item );
    d->mActions[ "edit_task" ]->setEnabled( item );
    d->mActions[ "mark_as_complete" ]->setEnabled( item && !item->isComplete() );
    d->mActions[ "mark_as_incomplete" ]->setEnabled( item && item->isComplete() );

    d->mActions[ "new_task" ]->setEnabled( currentTaskView() );
    d->mActions[ "new_sub_task" ]->setEnabled( currentTaskView() && currentTaskView()->count() );
    d->mActions[ "focustracking" ]->setEnabled( currentTaskView() );
    d->mActions[ "focustracking" ]->setChecked( currentTaskView() &&
        currentTaskView()->isFocusTrackingActive() );
    d->mActions[ "start_new_session" ]->setEnabled( currentTaskView() );
    d->mActions[ "edit_history" ]->setEnabled( currentTaskView() );
    d->mActions[ "reset_all_times" ]->setEnabled( currentTaskView() );
    d->mActions[ "export_times" ]->setEnabled( currentTaskView() );
    d->mActions[ "export_history" ]->setEnabled( currentTaskView() );
    d->mActions[ "import_planner" ]->setEnabled( currentTaskView() );
    d->mActions[ "file_save" ]->setEnabled( currentTaskView() );
    kDebug(5970) << "Leaving function";
}

void TimetrackerWidget::showSettingsDialog()
{
    kDebug(5970) << "Entering function";
    /* show main window b/c if this method was started from tray icon and the window
        is not visible the application quits after accepting the settings dialog.
    */
    window()->show();
    KTimeTrackerConfigDialog *dialog=new KTimeTrackerConfigDialog(i18n( "Settings" ), this);
    dialog->exec();
    delete dialog;
    KTimeTrackerSettings::self()->readConfig();

    showSearchBar(!KTimeTrackerSettings::configPDA() && KTimeTrackerSettings::showSearchBar());
    currentTaskView()->reconfigure();
}

//BEGIN wrapper slots
void TimetrackerWidget::startCurrentTimer()
{
    currentTaskView()->startCurrentTimer();
}

void TimetrackerWidget::stopCurrentTimer()
{
    currentTaskView()->stopCurrentTimer();
}

void TimetrackerWidget::stopAllTimers( const QDateTime &when )
{
    currentTaskView()->stopAllTimers(when);
}

void TimetrackerWidget::newTask()
{
    currentTaskView()->newTask();
}

void TimetrackerWidget::newSubTask()
{
    currentTaskView()->newSubTask();
}

void TimetrackerWidget::editTask()
{
    currentTaskView()->editTask();
}

void TimetrackerWidget::deleteTask()
{
    currentTaskView()->deleteTask();
}

void TimetrackerWidget::markTaskAsComplete()
{
    currentTaskView()->markTaskAsComplete();
}

void TimetrackerWidget::markTaskAsIncomplete()
{
    currentTaskView()->markTaskAsIncomplete();
}

void TimetrackerWidget::exportcsvFile()
{
    currentTaskView()->exportcsvFile();
}

void TimetrackerWidget::exportcsvHistory()
{
    currentTaskView()->exportcsvHistory();
}

void TimetrackerWidget::importPlanner( const QString &fileName )
{
    currentTaskView()->importPlanner(fileName);
}

void TimetrackerWidget::startNewSession()
{
    currentTaskView()->startNewSession();
}

void TimetrackerWidget::editHistory()
{
    // historydialog is the new historydialog, but the EditHiStoryDiaLog exists as well.
    // historydialog can be edited with qtcreator and qtdesigner, EditHiStoryDiaLog cannot.
    if ( currentTaskView() )
    {
        historydialog *dlg = new historydialog( currentTaskView() );
        if (currentTaskView()->storage()->rawevents().count()!=0) dlg->exec();
        else KMessageBox::information(0, i18nc("@info in message box", "There is no history yet. Start and stop a task and you will have an entry in your history."));
    }
}

void TimetrackerWidget::resetAllTimes()
{
    if ( currentTaskView() )
    {
        if ( KMessageBox::warningContinueCancel( this,
            i18n( "Do you really want to reset the time to zero for all tasks? This will delete the entire history." ),
            i18n( "Confirmation Required" ), KGuiItem( i18n( "Reset All Times" ) ) ) == KMessageBox::Continue )
        currentTaskView()->resetTimeForAllTasks();
    }
}

void TimetrackerWidget::focusTracking()
{
    currentTaskView()->toggleFocusTracking();
    d->mActions["focustracking"]->setChecked(currentTaskView()->isFocusTrackingActive());
}

void TimetrackerWidget::slotSearchBar()
{
    bool currentVisible = KTimeTrackerSettings::self()->showSearchBar();
    KTimeTrackerSettings::self()->setShowSearchBar( !currentVisible );
    d->mActions[ "searchbar" ]->setChecked( !currentVisible );
    showSearchBar( !currentVisible );
}
//END

/** \defgroup dbus slots ‘‘dbus slots’’ */
/* @{ */
QString TimetrackerWidget::version() const
{
    return KDEPIM_VERSION;
}

QStringList TimetrackerWidget::taskIdsFromName( const QString &taskName ) const
{
    QStringList result;

    TaskView *taskView = currentTaskView();
    if ( !taskView ) return result;
    QTreeWidgetItemIterator it( taskView );
    while ( *it )
    {
        Task *task = static_cast< Task* >( *it );
        if ( task && task->name() == taskName )
        {
            result << task->uid();
        }
        ++it;
    }

    return result;
}

void TimetrackerWidget::addTask( const QString &taskName )
{
    TaskView *taskView = currentTaskView();

    if ( taskView )
    {
        taskView->addTask( taskName, QString(), 0, 0, DesktopList(), 0 );
    }
}

void TimetrackerWidget::addSubTask( const QString& taskName, const QString &taskId )
{
    TaskView *taskView = currentTaskView();

    if ( taskView )
    {
        taskView->addTask( taskName, QString(), 0, 0, DesktopList(), taskView->task( taskId) );
        taskView->refresh();
    }
}

void TimetrackerWidget::deleteTask( const QString &taskId )
{
    TaskView *taskView = currentTaskView();

    if ( !taskView ) return;

    QTreeWidgetItemIterator it( taskView );
    while ( *it )
    {
        Task *task = static_cast< Task* >( *it );
        if ( task && task->uid() == taskId )
        {
            taskView->deleteTaskBatch( task );
        }
        ++it;
    }
}

void TimetrackerWidget::setPercentComplete( const QString &taskId, int percent )
{
    TaskView *taskView = currentTaskView();
    
    if ( !taskView ) return;

    QTreeWidgetItemIterator it( taskView );
    while ( *it )
    {
        Task *task = static_cast< Task* >( *it );
        if ( task && task->uid() == taskId )
        {
            task->setPercentComplete( percent, taskView->storage() );
        }
        ++it;
    }
}

int TimetrackerWidget::changeTime( const QString &taskId, int minutes )
{
    int result=0;
    QDate startDate;
    QTime startTime;
    QDateTime startDateTime;
    Task *task = 0, *t = 0;

    TaskView *taskView;

    if ( minutes <= 0 ) return KTIMETRACKER_ERR_INVALID_DURATION;

    // Find task
    taskView = currentTaskView();
    if ( taskView ) return KTIMETRACKER_ERR_UID_NOT_FOUND; //FIXME: it mimics the behaviour with the for loop, but I am not sure semantics were right. Maybe a new error code must be defined?

    QTreeWidgetItemIterator it( taskView );
    while ( *it ) {
        t = static_cast< Task* >( *it );
        if ( t && t->uid() == taskId ) {
            task = t;
            break;
        }
        ++it;
    }

    if ( !task ) result=KTIMETRACKER_ERR_UID_NOT_FOUND;
    else task->changeTime(minutes, task->taskView()->storage());
    return result;
}

QString TimetrackerWidget::error( int errorCode ) const
{
    switch ( errorCode )
    {
        case KTIMETRACKER_ERR_GENERIC_SAVE_FAILED:
            return i18n( "Save failed, most likely because the file could not be locked." );
        case KTIMETRACKER_ERR_COULD_NOT_MODIFY_RESOURCE:
            return i18n( "Could not modify calendar resource." );
        case KTIMETRACKER_ERR_MEMORY_EXHAUSTED:
            return i18n( "Out of memory--could not create object." );
        case KTIMETRACKER_ERR_UID_NOT_FOUND:
            return i18n( "UID not found." );
        case KTIMETRACKER_ERR_INVALID_DATE:
            return i18n( "Invalidate date--format is YYYY-MM-DD." );
        case KTIMETRACKER_ERR_INVALID_TIME:
            return i18n( "Invalid time--format is YYYY-MM-DDTHH:MM:SS." );
        case KTIMETRACKER_ERR_INVALID_DURATION:
            return i18n( "Invalid task duration--must be greater than zero." );
        default:
            return i18n( "Invalid error number: %1", errorCode );
    }
}

bool TimetrackerWidget::isIdleDetectionPossible() const
{
    bool result;
    IdleTimeDetector *idletimedetector1=new IdleTimeDetector(50);
    result=idletimedetector1->isIdleDetectionPossible();
    delete idletimedetector1;
    return result;
}

int TimetrackerWidget::totalMinutesForTaskId( const QString &taskId ) const
{
    TaskView *taskView = currentTaskView();
    if ( !taskView ) return -1;
    QTreeWidgetItemIterator it( taskView );
    while ( *it )
    {
        Task *task = static_cast< Task* >( *it );
        if ( task && task->uid() == taskId )
        {
            return task->totalTime();
        }
        ++it;
    }
    return -1;
}

void TimetrackerWidget::startTimerFor( const QString &taskId )
{
    kDebug();
        
    TaskView *taskView = currentTaskView();
    if ( !taskView ) return;
    QTreeWidgetItemIterator it( taskView );
    while ( *it )
    {
        Task *task = static_cast< Task* >( *it );
        if ( task && task->uid() == taskId )
        {
            taskView->startTimerFor( task );
            return;
        }
        ++it;
    }
}

bool TimetrackerWidget::startTimerForTaskName( const QString &taskName )
{
    TaskView *taskView = currentTaskView();
    if ( !taskView ) return false;
    QTreeWidgetItemIterator it( taskView );
    while ( *it )
    {
        Task *task = static_cast< Task* >( *it );
        if ( task && task->name() == taskName )
        {
            taskView->startTimerFor( task );
            return true;
        }
        ++it;
    }
    return false;
}


bool TimetrackerWidget::stopTimerForTaskName( const QString &taskName )
{
    TaskView *taskView = currentTaskView();
    if ( !taskView ) return false;

    QTreeWidgetItemIterator it( taskView );
    while ( *it )
    {
        Task *task = static_cast< Task* >( *it );

        if ( task && task->name() == taskName )
        {
            taskView->stopTimerFor( task );
            return true;
        }
        ++it;
    }
    return false;
}


void TimetrackerWidget::stopTimerFor( const QString &taskId )
{
    TaskView *taskView = currentTaskView();
    if ( !taskView ) return;
    QTreeWidgetItemIterator it( taskView );
    while ( *it )
    {
        Task *task = static_cast< Task* >( *it );
        if ( task && task->uid() == taskId )
        {
            taskView->stopTimerFor( task );
            return;
        }
        ++it;
    }
}

void TimetrackerWidget::stopAllTimersDBUS()
{
    TaskView *taskView = currentTaskView();
    if (taskView) taskView->stopAllTimers();
}

QString TimetrackerWidget::exportCSVFile( const QString &filename,
                                          const QString &from,
                                          const QString &to, int type,
                                          bool decimalMinutes,
                                          bool allTasks,
                                          const QString &delimiter,
                                          const QString &quote )
{
    TaskView *taskView = currentTaskView();

    if ( !taskView ) return "";
    ReportCriteria rc;
    rc.url = filename;
    rc.from = QDate::fromString( from );
    if ( rc.from.isNull() )
        rc.from = QDate::fromString( from, Qt::ISODate );
    rc.to = QDate::fromString( to );
    if ( rc.to.isNull() )
        rc.to = QDate::fromString( to, Qt::ISODate );
    rc.reportType = ( ReportCriteria::REPORTTYPE )type;
    rc.decimalMinutes = decimalMinutes;
    rc.allTasks = allTasks;
    rc.delimiter = delimiter;
    rc.quote = quote;

    return taskView->report( rc );
}

void TimetrackerWidget::importPlannerFile( const QString &filename )
{
    TaskView *taskView = currentTaskView();
    if ( !taskView ) return;
    taskView->importPlanner( filename );
}

bool TimetrackerWidget::isActive( const QString &taskId ) const
{
    TaskView *taskView = currentTaskView();
    if ( !taskView ) return false;
    QTreeWidgetItemIterator it( taskView );
    while ( *it )
    {
        Task *task = static_cast< Task* >( *it );

        if ( task && task->uid() == taskId )
        {
            return task->isRunning();
        }
        ++it;
    }
    return false;
}

bool TimetrackerWidget::isTaskNameActive( const QString &taskName ) const
{
    TaskView *taskView = currentTaskView();
    if ( !taskView ) return false;
    QTreeWidgetItemIterator it( taskView );
    while ( *it )
    {
        Task *task = static_cast< Task* >( *it );
        if ( task && task->name() == taskName )
        {
            return task->isRunning();
        }
        ++it;
    }
    return false;
}

QStringList TimetrackerWidget::tasks() const
{
    QStringList result;

    TaskView *taskView = currentTaskView();
    if ( !taskView ) return result;
    QTreeWidgetItemIterator it( taskView );
    while ( *it )
    {
        result << static_cast< Task* >( *it )->name();
        ++it;
    }
    return result;
}

QStringList TimetrackerWidget::activeTasks() const
{
    QStringList result;
    TaskView *taskView = currentTaskView();
    if ( !taskView ) return result;
    for ( int j = 0; j < taskView->count(); ++j )
    {
        if ( taskView->itemAt( j )->isRunning() )
        {
            result << taskView->itemAt( j )->name();
        }
    }
    return result;
}

void TimetrackerWidget::saveAll()
{
    currentTaskView()->save();
}

bool TimetrackerWidget::event ( QEvent * event ) // inherited from QWidget
{
    if (event->type()==QEvent::QueryWhatsThis)
    {
        if ( d->mTaskView->count() == 0 )
            setWhatsThis( i18n("This is ktimetracker, KDE's program to help you track your time. Best, start with creating your first task - enter it into the field where you see \"search or add task\".") );
        else setWhatsThis( i18n("You have already created a task. You can now start and stop timing") );
    }
    return QWidget::event(event);
}

void TimetrackerWidget::quit()
{
    kDebug(5970) << "Entering TimetrackerWidget::quit";
    if ( closeAllFiles() )
    {
        kapp->quit();
    }
}
// END of dbus slots group
/* @} */
#include "timetrackerwidget.moc"
