/**
 * UGENE - Integrated Bioinformatics Tools.
 * Copyright (C) 2008-2024 UniPro <ugene@unipro.ru>
 * http://ugene.net
 *
 * 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.
 */

#pragma once

#include <U2Core/Annotation.h>
#include <U2Core/AnnotationGroup.h>
#include <U2Core/AnnotationSelection.h>
#include <U2Core/Task.h>

#include "GSequenceLineView.h"

namespace U2 {

class AnnotationTableObject;
class AnnotationSettings;
class AnnotationModification;
class ClearAnnotationsTask;

class U2VIEW_EXPORT GSequenceLineViewAnnotated : public GSequenceLineView {
    Q_OBJECT
public:
    GSequenceLineViewAnnotated(QWidget* p, SequenceObjectContext* ctx);

    bool isAnnotationVisible(const Annotation* a) const;

    /**
     * Returns list of annotations that hold the on-screen point.
     * The method is delegated to the renderArea. Override the renderArea's variant of the method when needed.
     */
    QList<Annotation*> findAnnotationsByCoord(const QPoint& renderAreaPoint) const;

    static QString prepareAnnotationText(const SharedAnnotationData& a, const AnnotationSettings* as);

    QList<Annotation*> findAnnotationsInRange(const U2Region& range) const;

    bool isAnnotationSelectionInVisibleRange() const;

protected:
    void mousePressEvent(QMouseEvent* e) override;

    void mouseDoubleClickEvent(QMouseEvent* me) override;

    bool event(QEvent* e) override;

    /** Creates a tooltip for the given render area coordinate. */
    virtual QString createToolTip(const QPoint& renderAreaPoint);

    virtual void registerAnnotations(const QList<Annotation*>& l);

    virtual void unregisterAnnotations(const QList<Annotation*>& l);

    virtual void ensureVisible(Annotation* a, int locationIdx);

protected slots:

    virtual void sl_onAnnotationSettingsChanged(const QStringList& changedSettings);

    void sl_onAnnotationObjectAdded(AnnotationTableObject*);

    void sl_onAnnotationObjectRemoved(AnnotationTableObject*);

    void sl_onAnnotationsInGroupRemoved(const QList<Annotation*>&, AnnotationGroup*);

    void sl_onAnnotationsAdded(const QList<Annotation*>&);

    void sl_onAnnotationsRemoved(const QList<Annotation*>&);

    virtual void sl_onAnnotationsModified(const QList<AnnotationModification>& annotationModifications);

    /* Updates view so annotation becomes visible. */
    virtual void sl_onAnnotationActivated(Annotation* annotation, int regionIndex);

    virtual void sl_onAnnotationSelectionChanged(AnnotationSelection*, const QList<Annotation*>& added, const QList<Annotation*>& removed);

private:
    void connectAnnotationObject(const AnnotationTableObject* ao);
    /*
     * Calculate the index of the annotation region, which was as close as possible to the set base position.
     * This function is very valuable, then scale is very low and one pixel contains more than one sequence base.
     * \param ann The annotation.
     * \param baseIndex index of base user clicked on.
     * \return 0 The index of the closest annotation to the current position.
     **/
    static int getClosestAnnotationRegionToPointIndex(Annotation* ann, qint64 baseIndex);

protected:
    friend class ClearAnnotationsTask;
};

class U2VIEW_EXPORT GSequenceLineViewAnnotatedRenderArea : public GSequenceLineViewRenderArea {
    Q_OBJECT
public:
    GSequenceLineViewAnnotatedRenderArea(GSequenceLineViewAnnotated* sequenceLineViewAnnotated);
    ~GSequenceLineViewAnnotatedRenderArea();

    /** Returns all annotations by a coordinate inside render area. */
    virtual QList<Annotation*> findAnnotationsByCoord(const QPoint& coord) const = 0;

protected:
    void drawAll(QPaintDevice* pd) override = 0;

    GSequenceLineViewAnnotated* const sequenceLineViewAnnotated;

    //! VIEW_RENDERER_REFACTORING: this parameters are also doubled in SequenceViewAnnotaterRenderer
    // af* == annotation font
    QFont* afNormal;
    QFont* afSmall;

    QFontMetrics* afmNormal;
    QFontMetrics* afmSmall;

    int afNormalCharWidth;
    int afSmallCharWidth;

    QBrush gradientMaskBrush;
};

/**
 * Common base class for all grid-like annotation areas: PanView, DetView.
 * Note: the alternative to grid is a circular render area (see CircularView).
 */
class U2VIEW_EXPORT GSequenceLineViewGridAnnotationRenderArea : public GSequenceLineViewAnnotatedRenderArea {
    Q_OBJECT
public:
    GSequenceLineViewGridAnnotationRenderArea(GSequenceLineViewAnnotated* sequenceLineView);

    /** Returns all annotations by a coordinate inside render area. */
    QList<Annotation*> findAnnotationsByCoord(const QPoint& coord) const override;

    /**
     * Returns all x-regions covered by the annotation location region.
     */
    virtual QList<U2Region> getAnnotationXRegions(Annotation* annotation, int locationRegionIndex, const AnnotationSettings* annotationSettings) const = 0;

    /**
     * Returns all y-regions covered by the annotation location region.
     * TODO: rework to return list of QRects (may require a bigger refactoring).
     */
    virtual QList<U2Region> getAnnotationYRegions(Annotation* annotation, int locationRegionIndex, const AnnotationSettings* annotationSettings) const = 0;

private:
    /** Returns indexes of annotation region contains 'x' point. */
    QList<int> getAnnotationRegionIndexesByXCoord(int x, Annotation* annotation, int locationRegionIndex, const AnnotationSettings* annotationSettings) const;

    /** Returns the index of annotation region contains 'y' point. */
    int getAnnotationRegionIndexByYCoord(int y, Annotation* annotation, int locationRegionIndex, const AnnotationSettings* annotationSettings) const;

};

class ClearAnnotationsTask : public Task {
public:
    ClearAnnotationsTask(const QList<Annotation*>& list,
                         GSequenceLineViewAnnotated* view);

    void run() override;
    Task::ReportResult report() override;

private:
    QList<Annotation*> l;
    GSequenceLineViewAnnotated* view;
};

}  // namespace U2
