#pragma once

#include "data/pattern.h"

#include <QUndoCommand>

namespace Xybrid::Editing {
    class CompositeCommand;

    class PatternCommand : public QUndoCommand {
        friend class CompositeCommand;
    protected:
        std::shared_ptr<Data::Pattern> pattern;
        bool composed = false;

    public:
        bool commit();
        bool cancel();
    };

    class PatternDeltaCommand : public PatternCommand {
        int ch, rw;
        Data::Pattern::Row oldRow;
    public:
        Data::Pattern::Row row;

        PatternDeltaCommand(const std::shared_ptr<Data::Pattern>& pattern, int channel, int row);
        ~PatternDeltaCommand() override = default;

        int id() const override { return 2000; }

        bool mergeWith(const QUndoCommand*) override;
        void redo() override;
        void undo() override;
    };

    class PatternRenameCommand : public PatternCommand {
        QString from, to;

    public:
        PatternRenameCommand(const std::shared_ptr<Data::Pattern>& pattern, const QString& to);
        ~PatternRenameCommand() override = default;

        int id() const override { return 2070; }

        bool mergeWith(const QUndoCommand*) override;
        void redo() override;
        void undo() override;
    };

    class PatternTimeSignatureCommand : public PatternCommand {
        Data::TimeSignature from, to;

    public:
        PatternTimeSignatureCommand(const std::shared_ptr<Data::Pattern>& pattern, const Data::TimeSignature& to);
        ~PatternTimeSignatureCommand() override = default;

        int id() const override { return 2071; }
        bool mergeWith(const QUndoCommand*) override;
        void redo() override;
        void undo() override;
    };

    class PatternLengthCommand : public PatternCommand {
        int from, to;

    public:
        PatternLengthCommand(const std::shared_ptr<Data::Pattern>& pattern, int to);
        ~PatternLengthCommand() override = default;

        int id() const override { return 2072; }
        bool mergeWith(const QUndoCommand*) override;
        void redo() override;
        void undo() override;
    };

    class PatternFoldCommand : public PatternCommand {
        int from, to;

    public:
        PatternFoldCommand(const std::shared_ptr<Data::Pattern>& pattern, int to);
        ~PatternFoldCommand() override = default;

        int id() const override { return 2073; }
        bool mergeWith(const QUndoCommand*) override;
        void redo() override;
        void undo() override;
    };

    class PatternChannelMoveCommand : public PatternCommand {
        int from, to;

    public:
        PatternChannelMoveCommand(const std::shared_ptr<Data::Pattern>& pattern, int from, int to);
        ~PatternChannelMoveCommand() override = default;

        int id() const override { return 2100; }

        bool mergeWith(const QUndoCommand*) override;
        void redo() override;
        void undo() override;
    };

    class PatternChannelRenameCommand : public PatternCommand {
        int idx;
        QString from, to;

    public:
        PatternChannelRenameCommand(const std::shared_ptr<Data::Pattern>& pattern, int channel, const QString& to);
        ~PatternChannelRenameCommand() override = default;

        int id() const override { return 2101; }

        bool mergeWith(const QUndoCommand*) override;
        void redo() override;
        void undo() override;
    };

    class PatternChannelAddCommand : public PatternCommand {
        int idx;

    public:
        PatternChannelAddCommand(const std::shared_ptr<Data::Pattern>& pattern, int channel);
        ~PatternChannelAddCommand() override = default;

        void redo() override;
        void undo() override;
    };

    class PatternChannelDeleteCommand : public PatternCommand {
        int idx;
        Data::Pattern::Channel ch;

    public:
        PatternChannelDeleteCommand(const std::shared_ptr<Data::Pattern>& pattern, int channel);
        ~PatternChannelDeleteCommand() override = default;

        void redo() override;
        void undo() override;
    };
}
