/*
  Copyright (c) 2000,2001,2003 Cornelius Schumacher <schumacher@kde.org>
  Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
  Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
  Author: Kevin Krammer, krake@kdab.com
  Author: Sergio Martins, sergio.martins@kdab.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.
*/
#ifndef EVENTVIEWS_AGENDAVIEW_H
#define EVENTVIEWS_AGENDAVIEW_H

#include "eventviews_export.h"
#include "eventview.h"
#include "viewcalendar.h"

#include <KCalCore/Todo>

#include <QFrame>

class KConfig;

class QSplitter;

namespace EventViews {
#ifndef EVENTVIEWS_NODECOS
namespace CalendarDecoration {
class Decoration;
}
#endif

class TimeLabels;
class TimeLabelsZone;

class Agenda;
class AgendaItem;
class AgendaView;

class EventIndicator : public QFrame
{
    Q_OBJECT
public:
    enum Location {
        Top,
        Bottom
    };
    explicit EventIndicator(Location loc = Top, QWidget *parent = nullptr);
    virtual ~EventIndicator();

    void changeColumns(int columns);

    void enableColumn(int column, bool enable);

protected:
    void paintEvent(QPaintEvent *event) override;
    bool eventFilter(QObject *, QEvent *) override;

private:
    class Private;
    Private *const d;
};

/**
  AgendaView is the agenda-like view that displays events in a single
  or multi-day view.
*/
class EVENTVIEWS_EXPORT AgendaView : public EventView
{
    Q_OBJECT
public:
    explicit AgendaView(const PrefsPtr &preferences, const QDate &start, const QDate &end,
                        bool isInteractive, bool isSideBySide = false, QWidget *parent = nullptr);

    explicit AgendaView(const QDate &start, const QDate &end, bool isInteractive,
                        bool isSideBySide = false, QWidget *parent = nullptr);

    virtual ~AgendaView();

    enum {
        MAX_DAY_COUNT = 42 // (6 * 7)
    };

    /** Returns number of currently shown dates. */
    int currentDateCount() const override;

    /** returns the currently selected events */
    Akonadi::Item::List selectedIncidences() const override;

    /** returns the currently selected incidence's dates */
    KCalCore::DateList selectedIncidenceDates() const override;

    /** return the default start/end date/time for new events   */
    virtual bool eventDurationHint(QDateTime &startDt, QDateTime &endDt,
                                   bool &allDay) const override;

    /** start-datetime of selection */
    QDateTime selectionStart() const override;

    /** end-datetime of selection */
    QDateTime selectionEnd() const override;

    /** returns true if selection is for whole day */
    bool selectedIsAllDay() const;

    /** make selected start/end invalid */
    void deleteSelectedDateTime();

    /** returns if only a single cell is selected, or a range of cells */
    bool selectedIsSingleCell() const;

    /* reimp from EventView */
    void setCalendar(const Akonadi::ETMCalendar::Ptr &cal) override;
    virtual void addCalendar(const ViewCalendar::Ptr &cal);

    QSplitter *splitter() const;

    // FIXME: we already have startDateTime() and endDateTime() in the base class

    /** First shown day */
    QDate startDate() const;
    /** Last shown day */
    QDate endDate() const;

    /** Update event belonging to agenda item
        If the incidence is multi-day, item is the first one
    */
    void updateEventDates(AgendaItem *item, bool addIncidence,
                          Akonadi::Collection::Id collectionId);

    QVector<bool> busyDayMask() const;

    /**
     * Return calendar object for a concrete incidence.
     * this function is able to use multiple calenders
     * TODO: replace EventsView::calendar()
     */
    virtual KCalCore::Calendar::Ptr calendar2(const KCalCore::Incidence::Ptr &incidence) const;
    virtual KCalCore::Calendar::Ptr calendar2(const QString &incidenceIdentifier) const;

    virtual void showDates(const QDate &start, const QDate &end,
                           const QDate &preferredMonth = QDate()) override;

    void showIncidences(const Akonadi::Item::List &incidenceList, const QDate &date) override;

    void clearSelection() override;

    void startDrag(const Akonadi::Item &);

    void readSettings();
    void readSettings(const KConfig *);
    void writeSettings(KConfig *);

    void enableAgendaUpdate(bool enable);
    void setIncidenceChanger(Akonadi::IncidenceChanger *changer) override;

    void zoomInHorizontally(const QDate &date = QDate());
    void zoomOutHorizontally(const QDate &date = QDate());

    void zoomInVertically();
    void zoomOutVertically();

    void zoomView(const int delta, const QPoint &pos,
                  const Qt::Orientation orient = Qt::Horizontal);

    void clearTimeSpanSelection();

    // Used by the timelabelszone
    void updateTimeBarWidth();
    /** Create labels for the selected dates. */
    void createDayLabels(bool force);

    void createTimeBarHeaders();

    void setChanges(EventView::Changes) override;

Q_SIGNALS:
    void showNewEventPopupSignal();
    void showIncidencePopupSignal(const Akonadi::Item &, const QDate &);
    void zoomViewHorizontally(const QDate &, int count);

    void timeSpanSelectionChanged();

protected:
    /** Fill agenda using the current set value for the start date */
    void fillAgenda();

    void connectAgenda(Agenda *agenda, Agenda *otherAgenda);

    /**
      Set the masks on the agenda widgets indicating, which days are holidays.
    */
    void setHolidayMasks();

    void removeIncidence(const KCalCore::Incidence::Ptr &inc);

    void resizeEvent(QResizeEvent *resizeEvent) override;

public Q_SLOTS:
    void updateView() override;
    void updateConfig() override;
    /** reschedule the todo  to the given x- and y- coordinates.
        Third parameter determines all-day (no time specified) */
    void slotIncidencesDropped(const KCalCore::Incidence::List &incidences, const QPoint &, bool);
    void slotIncidencesDropped(const QList<QUrl> &incidences, const QPoint &, bool);
    void startDrag(const KCalCore::Incidence::Ptr &);

protected Q_SLOTS:
    void updateEventIndicatorTop(int newY);
    void updateEventIndicatorBottom(int newY);

    /** Updates data for selected timespan */
    void newTimeSpanSelected(const QPoint &start, const QPoint &end);
    /** Updates data for selected timespan for all day event*/
    void newTimeSpanSelectedAllDay(const QPoint &start, const QPoint &end);
    /**
      Updates the event indicators after a certain incidence was modified or
      removed.
    */
    void updateEventIndicators();
    void scheduleUpdateEventIndicators();
    void updateDayLabelSizes();

    void alignAgendas();

private Q_SLOTS:
    void slotIncidenceSelected(const KCalCore::Incidence::Ptr &incidence, const QDate &date);
    void slotShowIncidencePopup(const KCalCore::Incidence::Ptr &incidence, const QDate &date);
    void slotEditIncidence(const KCalCore::Incidence::Ptr &incidence);
    void slotShowIncidence(const KCalCore::Incidence::Ptr &incidence);
    void slotDeleteIncidence(const KCalCore::Incidence::Ptr &incidence);

private:
    void init(const QDate &start, const QDate &end);
    bool filterByCollectionSelection(const KCalCore::Incidence::Ptr &incidence);
    void setupTimeLabel(TimeLabels *timeLabel);
    bool displayIncidence(const KCalCore::Incidence::Ptr &incidence, bool createSelected);

#ifndef EVENTVIEWS_NODECOS
    typedef QList<EventViews::CalendarDecoration::Decoration *> DecorationList;
    bool loadDecorations(const QStringList &decorations, DecorationList &decoList);
    void placeDecorationsFrame(QFrame *frame, bool decorationsFound, bool isTop);
    void placeDecorations(EventViews::AgendaView::DecorationList &decoList, const QDate &date,
                          QWidget *labelBox, bool forWeek);
#endif

    friend class TimeLabelsZone;
    friend class MultiAgendaView;
    Agenda *agenda() const;
    Agenda *allDayAgenda() const;
private:
    class Private;
    Private *const d;
};
}

#endif
