// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#pragma once

#include "tracing_global.h"
#include "trackpainterbase.h" // TrackBackend

#include <QList>
#include <QString>
#include <QWidget>

#include <chrono>

QT_BEGIN_NAMESPACE
class QLabel;
class QScrollArea;
class QVBoxLayout;
class QWidget;
QT_END_NAMESPACE

namespace Timeline {

class TimelineModel;
class TimelineModelAggregator;
class TimelineNotesModel;
class TimelineZoomControl;
class TimelineScrollSync;
class RangeDetailsWidget;
class SelectionRangeDetailsWidget;
class SelectionRangeOverlay;
class TimeRuler;
class TrackLabels;
class TrackPainter;
class TrackPainterRaster;

class TRACING_EXPORT TimelineContentWidget : public QWidget
{
    Q_OBJECT
public:
    explicit TimelineContentWidget(TimelineModelAggregator *aggregator,
                                   TimelineZoomControl *zoom,
                                   RangeDetailsWidget *details,
                                   QWidget *parent = nullptr);

    QSize sizeHint() const override;

    void setSelectionRangeMode(bool active);
    bool selectionRangeMode() const;

    QString currentFile() const { return m_currentFile; }
    int currentLine() const { return m_currentLine; }
    int currentColumn() const { return m_currentColumn; }
    int currentTypeId() const { return m_currentTypeId; }

    int selectedModelIndex() const { return m_selectedModelIndex; }
    int selectedItemIndex() const { return m_selectedItemIndex; }

    int hoveredModelIndex() const { return m_hoveredModelIndex; }
    int hoveredItemIndex() const { return m_hoveredItemIndex; }

    void setLeftHeaderWidget(QWidget *widget);
    void addLeftPanelWidget(QWidget *widget);

    bool selectionRangeReady() const;
    void selectItem(int modelIndex, int itemIndex);
    void selectByAggregatorIndex(int aggModelIndex, int itemIndex);
    void selectByTypeId(int typeId);
    void jumpToNext();
    void jumpToPrev();
    void clear();

    bool selectionLocked() const;
    void setSelectionLocked(bool locked);

    // Which backend currently renders the tracks, and switching it at runtime
    // (recreates the track widget, since the two backends are different types).
    TrackBackend trackBackend() const;
    void setTrackBackend(TrackBackend backend);

signals:
    void itemHovered(int modelIndex, int itemIndex);
    void selectionLockedChanged(bool locked);
    void selectionRangeModeChanged(bool active);
    void trackBackendChanged(TrackBackend backend);

protected:
    bool eventFilter(QObject *watched, QEvent *event) override;
    void resizeEvent(QResizeEvent *) override;

private:
    // Track rendering backends are created lazily and kept alive; switching only
    // toggles which one is visible (recreating the QRhiWidget-based backend is
    // unsafe). activateTrackView() shows the requested backend, wireTrackView()
    // connects a newly created view's signals.
    void activateTrackView(TrackBackend backend);
    template<class T> void wireTrackView(T *view);

    void rebuildTracks();
    void updateNotes();
    void applyHorizontalPan(int dx);
    void applyZoom(double cursorX, int dy);
    void recenterOnItem(int modelIndex, int itemIndex);
    void onItemHovered(int modelIndex, int itemIndex);
    void showItemDetails(int modelIndex, int itemIndex);
    void onFramePainted(std::chrono::nanoseconds renderTime);
    void updateFrameTime();
    void positionFrameTimeLabel();

    // Translate between painter index (into m_painters) and aggregator model index.
    int painterToAggregator(int painterIdx) const;
    int aggregatorToPainter(int aggIdx) const;

    TimelineModelAggregator *m_aggregator;
    TimelineZoomControl *m_zoom;
    TimelineScrollSync *m_sync;
    TimeRuler *m_ruler;
    TrackLabels *m_labels;
    QScrollArea *m_scrollArea;
    QWidget *m_trackContainer; // sizing spacer that drives the vertical scroll range
    QWidget *m_leftPanel = nullptr;
    QVBoxLayout *m_leftLayout = nullptr;
    QWidget *m_leftHeader = nullptr;
    SelectionRangeOverlay *m_overlay = nullptr;
    SelectionRangeDetailsWidget *m_selectionDetails = nullptr;
    RangeDetailsWidget *m_details; // Not owned; lives in a dockable view.
    TimelineNotesModel *m_notes = nullptr;
    // Backend-independent API of the single widget that renders all tracks, and
    // that same object as a QWidget (for sizing/parenting). The concrete type is
    // TrackPainter (QCanvasPainter) or TrackPainterRaster (QPainter).
    TrackPainterBase *m_tracksView = nullptr;
    QWidget *m_tracksWidget = nullptr;
    // Both backends are kept alive once created; only visibility is toggled.
    TrackPainter *m_gpuView = nullptr;
    TrackPainterRaster *m_rasterView = nullptr;
    QList<qint64> m_markers; // last ruler markers, re-applied when the view changes
    QList<TimelineModel *> m_trackModels; // visible models, parallel to track index
    QList<int> m_painterAggregatorMap; // m_painterAggregatorMap[trackIdx] = aggregator model index

    void updateContainerSize();

    int m_selectedModelIndex = -1;
    int m_selectedItemIndex = -1;
    QString m_currentFile;
    int m_currentLine = -1;
    int m_currentColumn = 0;
    int m_currentTypeId = -1;

    int m_hoveredModelIndex = -1;
    int m_hoveredItemIndex = -1;

    bool m_selectionLocked = false;

    // Frame-time overlay: shows the worst full-frame render time (the render
    // widget's paint duration; a single widget renders all tracks here).
    QLabel *m_frameTimeLabel = nullptr;
    std::chrono::nanoseconds m_frameAccum{};  // paint time accumulated for the current cycle
    std::chrono::nanoseconds m_maxRender{};   // worst full-frame render time this sample window
    bool m_frameFlushScheduled = false;
};

} // namespace Timeline
