/*
 * Fooyin
 * Copyright © 2023, Luke Taylor <luket@pm.me>
 *
 * Fooyin 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 3 of the License, or
 * (at your option) any later version.
 *
 * Fooyin 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 Fooyin.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#pragma once

#include "playlist/playlistmodel.h"

#include <core/player/playbackqueue.h>
#include <core/playlist/playlist.h>
#include <core/playlist/playlistchangeset.h>
#include <gui/playlist/currentplaylistcontroller.h>

#include <QObject>

#include <memory>
#include <set>

class QUndoCommand;

namespace Fooyin {
class Application;
class PlayerController;
class Playlist;
class PlaylistColumnRegistry;
class PlaylistHandler;
class PlaylistUiController;
class PlaylistWorkspace;
enum class PlaylistTrackChangeSource;
struct PlaylistTrack;
class PresetRegistry;
enum class TrackAction;
class SettingsManager;
class TrackSelectionController;

struct PlaylistViewState
{
    int topIndex{-1};
    int scrollPos{0};
};

class PlaylistController : public CurrentPlaylistController
{
    Q_OBJECT

public:
    PlaylistController(Application* app, QObject* parent = nullptr);
    ~PlaylistController() override;

    [[nodiscard]] PlayerController* playerController() const;
    [[nodiscard]] PlaylistHandler* playlistHandler() const;
    [[nodiscard]] PresetRegistry* presetRegistry() const;
    [[nodiscard]] PlaylistColumnRegistry* columnRegistry() const;
    [[nodiscard]] PlaylistUiController* uiController() const;

    [[nodiscard]] bool playlistsHaveLoaded() const;
    [[nodiscard]] PlaylistList playlists() const;
    [[nodiscard]] PlaylistTrack currentTrack() const;
    [[nodiscard]] Player::PlayState playState() const;

    void startPlayback() const;

    [[nodiscard]] bool currentIsActive() const;
    [[nodiscard]] bool currentIsAuto() const;
    [[nodiscard]] Playlist* currentPlaylist() const override;
    [[nodiscard]] UId currentPlaylistId() const override;

    void changeCurrentPlaylist(Playlist* playlist);
    void changeCurrentPlaylist(const UId& id) override;
    void changePlaylistIndex(const UId& playlistId, int index);
    [[nodiscard]] bool canClearCurrentPlaylist() const;
    void clearCurrentPlaylist();

    [[nodiscard]] QString currentSearch(Playlist* playlist) const;
    void setSearch(Playlist* playlist, const QString& search);

    [[nodiscard]] std::optional<PlaylistViewState> playlistState(Playlist* playlist) const;
    void savePlaylistState(Playlist* playlist, const PlaylistViewState& state);

    void addToHistory(QUndoCommand* command);
    [[nodiscard]] bool canUndo() const;
    [[nodiscard]] bool canRedo() const;
    void undoPlaylistChanges();
    void redoPlaylistChanges();
    void clearHistory();

    [[nodiscard]] bool clipboardEmpty() const;
    [[nodiscard]] TrackList clipboard() const;
    void setClipboard(const TrackList& tracks);

Q_SIGNALS:
    void playlistsLoaded();
    void currentPlaylistUpdated(Fooyin::Playlist* playlist);
    void currentPlaylistTracksPatched(const Fooyin::PlaylistChangeset& changeSet);
    void currentPlaylistTracksChanged(const std::vector<int>& indexes, bool allNew);
    void currentPlaylistTracksUpdated(const std::vector<int>& indexes);
    void currentPlaylistTracksRemoved(const std::vector<int>& indexes);
    void currentPlaylistQueueChanged(const std::vector<int>& tracks);

    void playStateChanged(Fooyin::Player::PlayState state);
    void clipboardChanged();
    void playlistHistoryChanged();
    void playingTrackChanged(const Fooyin::PlaylistTrack& track);

public Q_SLOTS:
    void handleTrackSelectionAction(Fooyin::TrackAction action);

private:
    void restoreLastPlaylist();

    void handlePlaylistAdded(Playlist* playlist);
    void handlePlaylistMetadataUpdated(Playlist* playlist);
    void handlePlaylistTracksPatched(Playlist* playlist, const PlaylistChangeset& changeSet,
                                     PlaylistTrackChangeSource source);
    void handlePlaylistTracksRemoved(Playlist* playlist, const std::vector<int>& indexes);

    void handleTracksQueued(const QueueTracks& tracks);
    void handleTracksDequeued(const QueueTracks& tracks);
    void handleTracksDequeued(const PlaylistIndexes& indexes);
    void handleQueueChanged(const QueueTracks& removed, const QueueTracks& added);
    void flushPendingQueueChanges();

    void handlePlaylistUpdated(Playlist* playlist, const std::vector<int>& indexes, PlaylistTrackChangeSource source);
    void handleTracksUpdated(Playlist* playlist, const std::vector<int>& indexes);
    void handlePlaylistRemoved(Playlist* playlist);

    PlaylistHandler* m_handler;
    PlayerController* m_playerController;
    SettingsManager* m_settings;
    PresetRegistry* m_presetRegistry;
    PlaylistColumnRegistry* m_columnRegistry;
    PlaylistUiController* m_uiController;

    std::unique_ptr<PlaylistWorkspace> m_workspace;

    std::set<int> m_pendingQueueIndexes;
    UId m_pendingQueuePlaylistId;
    uint64_t m_playlistPatchRevision{0};
    uint64_t m_pendingQueuePatchRevision{0};
    bool m_queueRefreshPending{false};
};
} // namespace Fooyin
