/*
  This file is part of KOrganizer.

  Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org>
  Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.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 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.

  As a special exception, permission is given to link this program
  with any edition of Qt, and distribute the resulting executable,
  without including the source code for Qt in the source distribution.
*/

#include "koeventpopupmenu.h"
#include "actionmanager.h"
#include "kocorehelper.h"
#include "koglobals.h"
#include "dialog/noteeditdialog.h"

#include <CalendarSupport/KCalPrefs>
#include <CalendarSupport/Utils>
#include <CalendarSupport/CalPrinter>
#include <CalendarSupport/CalPrintDefaultPlugins>

#include <KCalendarCore/CalFormat>
#include <KCalendarCore/Incidence>
#include <Akonadi/Notes/NoteUtils>
#include <AkonadiCore/CollectionFetchJob>
#include <AkonadiCore/CollectionFetchScope>
#include <AkonadiCore/ItemCreateJob>

#include <KActionCollection>
#include <KMime/Message>
#include "korganizer_debug.h"

#include <IncidenceEditor/IncidenceDialogFactory>
#include <IncidenceEditor/IncidenceDialog>

KOEventPopupMenu::KOEventPopupMenu(Akonadi::ETMCalendar *calendar, QWidget *parent)
    : QMenu(parent)
    , mCalendar(calendar)
{
    addAction(QIcon::fromTheme(QStringLiteral("document-preview")), i18n("&Show"),
              this, &KOEventPopupMenu::popupShow);
    mEditOnlyItems.append(
        addAction(QIcon::fromTheme(QStringLiteral("document-edit")), i18n("&Edit..."),
                  this, &KOEventPopupMenu::popupEdit));
    mEditOnlyItems.append(addSeparator());
    addAction(QIcon::fromTheme(QStringLiteral("document-print")), i18n("&Print..."),
              this, &KOEventPopupMenu::slotPrint);
    addAction(QIcon::fromTheme(QStringLiteral("document-print-preview")),
              i18n("Print Previe&w..."),
              this, &KOEventPopupMenu::printPreview);

    //------------------------------------------------------------------------
    mEditOnlyItems.append(addSeparator());
    mEditOnlyItems.append(addAction(QIcon::fromTheme(QStringLiteral("edit-cut")),
                                    i18nc("cut this event", "C&ut"),
                                    this, &KOEventPopupMenu::popupCut));
    mEditOnlyItems.append(addAction(QIcon::fromTheme(QStringLiteral("edit-copy")),
                                    i18nc("copy this event", "&Copy"),
                                    this, &KOEventPopupMenu::popupCopy));
    // paste is always possible
    mEditOnlyItems.append(addAction(QIcon::fromTheme(QStringLiteral("edit-paste")),
                                    i18n("&Paste"),
                                    this, &KOEventPopupMenu::popupPaste));
    mEditOnlyItems.append(addAction(QIcon::fromTheme(QStringLiteral("edit-delete")),
                                    i18nc("delete this incidence", "&Delete"),
                                    this, &KOEventPopupMenu::popupDelete));
    //------------------------------------------------------------------------
    addSeparator();
    QAction *action = addAction(QIcon::fromTheme(QStringLiteral("task-new")),
                                i18n("Create To-do"),
                                this, &KOEventPopupMenu::createTodo);
    action->setObjectName(QStringLiteral("createtodo"));
    mEventOnlyItems.append(action);

    action = addAction(QIcon::fromTheme(QStringLiteral("appointment-new")),
                       i18n("Create Event"),
                       this, qOverload<>(&KOEventPopupMenu::createEvent));
    action->setObjectName(QStringLiteral("createevent"));
    mTodoOnlyItems.append(action);

    action = addAction(QIcon::fromTheme(QStringLiteral("view-pim-notes")),
                       i18n("Create Note"),
                       this, qOverload<>(&KOEventPopupMenu::createNote));
    action->setObjectName(QStringLiteral("createnote"));
    //------------------------------------------------------------------------
    addSeparator();
    mTodoOnlyItems.append(addAction(QIcon::fromTheme(QStringLiteral("task-complete")),
                                    i18n("Togg&le To-do Completed"),
                                    this, &KOEventPopupMenu::toggleTodoCompleted));
    mToggleReminder = addAction(QIcon::fromTheme(QStringLiteral("appointment-reminder")),
                                i18n("&Toggle Reminder"), this, &KOEventPopupMenu::toggleAlarm);
    mEditOnlyItems.append(mToggleReminder);
    //------------------------------------------------------------------------
    mRecurrenceItems.append(addSeparator());
    mDissociateOccurrences = addAction(i18n("&Dissociate From Recurrence..."),
                                       this, &KOEventPopupMenu::dissociateOccurrences);
    mRecurrenceItems.append(mDissociateOccurrences);

    addSeparator();
    addAction(QIcon::fromTheme(QStringLiteral("mail-forward")),
              i18n("Send as iCalendar..."),
              this, &KOEventPopupMenu::forward);
}

void KOEventPopupMenu::showIncidencePopup(const Akonadi::Item &item, const QDate &qd)
{
    mCurrentIncidence = item;
    mCurrentDate = qd;

    if (!CalendarSupport::hasIncidence(mCurrentIncidence) /*&& qd.isValid()*/) {
        qCDebug(KORGANIZER_LOG) << "No event selected";
        return;
    }

    if (!mCalendar) {
        //TODO fix it
        qCDebug(KORGANIZER_LOG) << "Calendar is 0";
        return;
    }

    const bool hasChangeRights = mCalendar->hasRight(mCurrentIncidence,
                                                     Akonadi::Collection::CanChangeItem);

    KCalendarCore::Incidence::Ptr incidence = CalendarSupport::incidence(mCurrentIncidence);
    Q_ASSERT(incidence);
    if (incidence->recurs()) {
        const QDateTime thisDateTime(qd, {}, Qt::LocalTime);
        const bool isLastOccurrence
            = !incidence->recurrence()->getNextDateTime(thisDateTime).isValid();
        const bool isFirstOccurrence
            = !incidence->recurrence()->getPreviousDateTime(thisDateTime).isValid();
        mDissociateOccurrences->setEnabled(
            !(isFirstOccurrence && isLastOccurrence) && hasChangeRights);
    }

    // Enable/Disabled menu items only valid for editable events.
    QList<QAction *>::Iterator it;
    QList<QAction *>::Iterator end(mEditOnlyItems.end());
    for (it = mEditOnlyItems.begin(); it != end; ++it) {
        (*it)->setEnabled(hasChangeRights);
    }
    mToggleReminder->setVisible((incidence->type() != KCalendarCore::Incidence::TypeJournal));
    end = mRecurrenceItems.end();
    for (it = mRecurrenceItems.begin(); it != end; ++it) {
        (*it)->setVisible(incidence->recurs());
    }
    end = mTodoOnlyItems.end();
    for (it = mTodoOnlyItems.begin(); it != end; ++it) {
        (*it)->setVisible(incidence->type() == KCalendarCore::Incidence::TypeTodo);
        (*it)->setEnabled(hasChangeRights);
    }
    end = mEventOnlyItems.end();
    for (it = mEventOnlyItems.begin(); it != end; ++it) {
        (*it)->setVisible(incidence->type() == KCalendarCore::Incidence::TypeEvent);
        (*it)->setEnabled(hasChangeRights);
    }
    popup(QCursor::pos());
}

void KOEventPopupMenu::popupShow()
{
    if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
        Q_EMIT showIncidenceSignal(mCurrentIncidence);
    }
}

void KOEventPopupMenu::popupEdit()
{
    if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
        Q_EMIT editIncidenceSignal(mCurrentIncidence);
    }
}

void KOEventPopupMenu::slotPrint()
{
    print(false);
}

void KOEventPopupMenu::print(bool preview)
{
    KOCoreHelper helper;
    CalendarSupport::CalPrinter printer(this, mCalendar, true);
    connect(this, &KOEventPopupMenu::configChanged, &printer,
            &CalendarSupport::CalPrinter::updateConfig);

    //Item::List selectedIncidences;
    KCalendarCore::Incidence::List selectedIncidences;
    Q_ASSERT(mCurrentIncidence.hasPayload<KCalendarCore::Incidence::Ptr>());
    selectedIncidences.append(mCurrentIncidence.payload<KCalendarCore::Incidence::Ptr>());

    printer.print(CalendarSupport::CalPrinterBase::Incidence,
                  mCurrentDate, mCurrentDate, selectedIncidences, preview);
}

void KOEventPopupMenu::printPreview()
{
    print(true);
}

void KOEventPopupMenu::popupDelete()
{
    if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
        Q_EMIT deleteIncidenceSignal(mCurrentIncidence);
    }
}

void KOEventPopupMenu::popupCut()
{
    if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
        Q_EMIT cutIncidenceSignal(mCurrentIncidence);
    }
}

void KOEventPopupMenu::popupCopy()
{
    if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
        Q_EMIT copyIncidenceSignal(mCurrentIncidence);
    }
}

void KOEventPopupMenu::popupPaste()
{
    Q_EMIT pasteIncidenceSignal();
}

void KOEventPopupMenu::toggleAlarm()
{
    if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
        Q_EMIT toggleAlarmSignal(mCurrentIncidence);
    }
}

void KOEventPopupMenu::dissociateOccurrences()
{
    if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
        Q_EMIT dissociateOccurrencesSignal(mCurrentIncidence, mCurrentDate);
    }
}

void KOEventPopupMenu::forward()
{
    KOrg::MainWindow *w = ActionManager::findInstance(QUrl());
    if (!w || !CalendarSupport::hasIncidence(mCurrentIncidence)) {
        return;
    }

    KActionCollection *ac = w->getActionCollection();
    QAction *action = ac->action(QStringLiteral("schedule_forward"));
    if (action) {
        action->trigger();
    } else {
        qCCritical(KORGANIZER_LOG) << "What happened to the schedule_forward action?";
    }
}

void KOEventPopupMenu::createEvent(const Akonadi::Item &item)
{
    mCurrentIncidence = item;
    createEvent();
}

void KOEventPopupMenu::createEvent()
{
    // Must be a Incidence
    if (!CalendarSupport::hasIncidence(mCurrentIncidence)) {
        return;
    }
    // Event ->event doesn't make sense
    if (CalendarSupport::hasEvent(mCurrentIncidence)) {
        return;
    }

    if (CalendarSupport::hasTodo(mCurrentIncidence)) {
        KCalendarCore::Todo::Ptr todo(CalendarSupport::todo(mCurrentIncidence));
        KCalendarCore::Event::Ptr event(new KCalendarCore::Event(*todo));
        event->setUid(KCalendarCore::CalFormat::createUniqueId());
        event->setDtStart(todo->dtStart());
        event->setAllDay(todo->allDay());
        event->setDtEnd(todo->dtDue());
        Akonadi::Item newEventItem;
        newEventItem.setMimeType(KCalendarCore::Event::eventMimeType());
        newEventItem.setPayload<KCalendarCore::Event::Ptr>(event);

        IncidenceEditorNG::IncidenceDialog *dlg = IncidenceEditorNG::IncidenceDialogFactory::create(
            true, KCalendarCore::IncidenceBase::TypeEvent, nullptr, this);
        dlg->setObjectName(QStringLiteral("incidencedialog"));
        dlg->load(newEventItem);
        dlg->open();
    }
}

void KOEventPopupMenu::createNote(const Akonadi::Item &item)
{
    mCurrentIncidence = item;
    createNote();
}

void KOEventPopupMenu::createNote()
{
    // Must be a Incidence
    if (CalendarSupport::hasIncidence(mCurrentIncidence)) {
        KCalendarCore::Incidence::Ptr incidence(CalendarSupport::incidence(mCurrentIncidence));
        Akonadi::NoteUtils::NoteMessageWrapper note;
        note.setTitle(incidence->summary());
        note.setText(incidence->description(),
                     incidence->descriptionIsRich() ? Qt::RichText : Qt::PlainText);
        note.setFrom(QCoreApplication::applicationName() + QCoreApplication::applicationVersion());
        note.setLastModifiedDate(QDateTime::currentDateTimeUtc());
        Akonadi::NoteUtils::Attachment attachment(
            mCurrentIncidence.url(), mCurrentIncidence.mimeType());
        note.attachments().append(attachment);
        Akonadi::Item newNoteItem;
        newNoteItem.setMimeType(Akonadi::NoteUtils::noteMimeType());
        newNoteItem.setPayload(note.message());

        NoteEditDialog *noteedit = new NoteEditDialog(this);
        connect(noteedit, &NoteEditDialog::createNote, this, &KOEventPopupMenu::slotCreateNote);
        noteedit->load(newNoteItem);
        noteedit->show();
    }
}

void KOEventPopupMenu::slotCreateNote(const Akonadi::Item &noteItem, const Akonadi::Collection &collection)
{
    Akonadi::ItemCreateJob *createJob = new Akonadi::ItemCreateJob(noteItem, collection, this);
    connect(createJob, &Akonadi::ItemCreateJob::result, this,
            &KOEventPopupMenu::slotCreateNewNoteJobFinished);
    createJob->start();
}

void KOEventPopupMenu::slotCreateNewNoteJobFinished(KJob *job)
{
    if (job->error()) {
        qCDebug(KORGANIZER_LOG) << "Error during create new Note " << job->errorString();
    }
}

void KOEventPopupMenu::createTodo()
{
    // Must be a Incidence
    if (!CalendarSupport::hasIncidence(mCurrentIncidence)) {
        return;
    }
    // Todo->Todo doesn't make sense
    if (CalendarSupport::hasTodo(mCurrentIncidence)) {
        return;
    }

    if (CalendarSupport::hasEvent(mCurrentIncidence)) {
        KCalendarCore::Event::Ptr event(CalendarSupport::event(mCurrentIncidence));
        KCalendarCore::Todo::Ptr todo(new KCalendarCore::Todo(*event));
        todo->setUid(KCalendarCore::CalFormat::createUniqueId());
        todo->setDtStart(event->dtStart());
        todo->setAllDay(event->allDay());
        todo->setDtDue(event->dtEnd());
        Akonadi::Item newTodoItem;
        newTodoItem.setMimeType(KCalendarCore::Todo::todoMimeType());
        newTodoItem.setPayload<KCalendarCore::Todo::Ptr>(todo);

        IncidenceEditorNG::IncidenceDialog *dlg = IncidenceEditorNG::IncidenceDialogFactory::create(
            true, KCalendarCore::IncidenceBase::TypeTodo, nullptr, this);
        dlg->setObjectName(QStringLiteral("incidencedialog"));
        dlg->load(newTodoItem);
        dlg->open();
    }
}

void KOEventPopupMenu::toggleTodoCompleted()
{
    if (CalendarSupport::hasTodo(mCurrentIncidence)) {
        Q_EMIT toggleTodoCompletedSignal(mCurrentIncidence);
    }
}

void KOEventPopupMenu::setCalendar(const Akonadi::ETMCalendar::Ptr &calendar)
{
    mCalendar = calendar;
}
