#include "patterncommands.h"

#include "uisocket.h"
#include "data/project.h"

#include <QDebug>
#include <QUndoStack>

using Xybrid::Data::Pattern;
using namespace Xybrid::Editing;

bool PatternCommand::commit() {
    if (!pattern->valid()) return false;
    if (!pattern->project->socket || !pattern->project->socket->undoStack) return cancel();
    pattern->project->socket->undoStack->push(this);
    return true;
}
bool PatternCommand::cancel() {
    delete this;
    return false;
}


PatternDeltaCommand::PatternDeltaCommand(const std::shared_ptr<Pattern>& pattern, int c, int r) {
    this->pattern = pattern;
    ch = c;
    rw = r;
    oldRow = pattern->rowAt(c, r);
    row = oldRow;
    setText("edit row");
}

bool PatternDeltaCommand::mergeWith(const QUndoCommand* o_) {
    if (o_->id() != id()) return false;
    auto* o = static_cast<const PatternDeltaCommand*>(o_);
    if (o->pattern != pattern) return false;
    if (o->ch != ch || o->rw != rw) return false;
    row = o->row;
    return true;
}

void PatternDeltaCommand::redo() {
    pattern->rowAt(ch, rw) = row;
    if (!composed) emit pattern->project->socket->rowUpdated(pattern.get(), ch, rw);
}

void PatternDeltaCommand::undo() {
    pattern->rowAt(ch, rw) = oldRow;
    if (!composed) emit pattern->project->socket->rowUpdated(pattern.get(), ch, rw);
}

PatternRenameCommand::PatternRenameCommand(const std::shared_ptr<Pattern>& pattern, const QString& to) {
    this->pattern = pattern;
    from = pattern->name;
    this->to = to;
    setText("rename pattern");
}

bool PatternRenameCommand::mergeWith(const QUndoCommand* o_) {
    if (o_->id() != id()) return false;
    auto* o = static_cast<const PatternRenameCommand*>(o_);
    if (o->pattern != pattern) return false;
    to = o->to;
    return true;
}

void PatternRenameCommand::redo() {
    pattern->name = to;
    emit pattern->project->socket->updatePatternLists();
}

void PatternRenameCommand::undo() {
    pattern->name = from;
    emit pattern->project->socket->updatePatternLists();
}

PatternTimeSignatureCommand::PatternTimeSignatureCommand(const std::shared_ptr<Xybrid::Data::Pattern>& pattern, const Xybrid::Data::TimeSignature& to) {
    this->pattern = pattern;
    from = pattern->time;
    this->to = to;
    setText("edit pattern time signature");
}

bool PatternTimeSignatureCommand::mergeWith(const QUndoCommand* o_) {
    if (o_->id() != id()) return false;
    auto* o = static_cast<const PatternTimeSignatureCommand*>(o_);
    if (o->pattern != pattern) return false;
    to = o->to;
    return true;
}

void PatternTimeSignatureCommand::redo() {
    pattern->time = to;
    emit pattern->project->socket->patternUpdated(pattern.get());
}

void PatternTimeSignatureCommand::undo() {
    pattern->time = from;
    emit pattern->project->socket->patternUpdated(pattern.get());
}

PatternLengthCommand::PatternLengthCommand(const std::shared_ptr<Xybrid::Data::Pattern>& pattern, int to) {
    this->pattern = pattern;
    from = pattern->rows;
    this->to = to;
    setText("resize pattern");
}

bool PatternLengthCommand::mergeWith(const QUndoCommand* o_) {
    if (o_->id() != id()) return false;
    auto* o = static_cast<const PatternLengthCommand*>(o_);
    if (o->pattern != pattern) return false;
    to = o->to;
    return true;
}

void PatternLengthCommand::redo() {
    pattern->setLength(to);
    emit pattern->project->socket->patternUpdated(pattern.get());
}

void PatternLengthCommand::undo() {
    pattern->setLength(from);
    emit pattern->project->socket->patternUpdated(pattern.get());
}

PatternFoldCommand::PatternFoldCommand(const std::shared_ptr<Xybrid::Data::Pattern>& pattern, int to) {
    this->pattern = pattern;
    from = pattern->fold;
    this->to = to;
    setText("set pattern fold");
}

bool PatternFoldCommand::mergeWith(const QUndoCommand* o_) {
    if (o_->id() != id()) return false;
    auto* o = static_cast<const PatternFoldCommand*>(o_);
    if (o->pattern != pattern) return false;
    to = o->to;
    return true;
}

void PatternFoldCommand::redo() {
    pattern->fold = to;
    emit pattern->project->socket->patternUpdated(pattern.get());
}

void PatternFoldCommand::undo() {
    pattern->fold = from;
    emit pattern->project->socket->patternUpdated(pattern.get());
}

PatternChannelMoveCommand::PatternChannelMoveCommand(const std::shared_ptr<Pattern> &pattern, int from, int to) {
    this->pattern = pattern;
    this->from = from;
    this->to = to;
    setText("move channel");
}

bool PatternChannelMoveCommand::mergeWith(const QUndoCommand* o_) {
    if (o_->id() != id()) return false;
    auto* o = static_cast<const PatternChannelMoveCommand*>(o_);
    if (o->pattern != pattern) return false;
    if (o->from != to) return false;
    to = o->to;
    return true;
}

void PatternChannelMoveCommand::redo() {
    Pattern::Channel ch = std::move(*(pattern->channels.begin()+from));
    pattern->channels.erase(pattern->channels.begin()+from);
    pattern->channels.insert(pattern->channels.begin()+to, std::move(ch));
    emit pattern->project->socket->patternUpdated(pattern.get());
}

void PatternChannelMoveCommand::undo() {
    Pattern::Channel ch = std::move(*(pattern->channels.begin()+to));
    pattern->channels.erase(pattern->channels.begin()+to);
    pattern->channels.insert(pattern->channels.begin()+from, std::move(ch));
    emit pattern->project->socket->patternUpdated(pattern.get());
}

PatternChannelRenameCommand::PatternChannelRenameCommand(const std::shared_ptr<Pattern>& pattern, int channel, const QString& to) {
    this->pattern = pattern;
    idx = channel;
    from = pattern->channel(idx).name;
    this->to = to;
    setText("rename channel");
}

bool PatternChannelRenameCommand::mergeWith(const QUndoCommand* o_) {
    if (o_->id() != id()) return false;
    auto* o = static_cast<const PatternChannelRenameCommand*>(o_);
    if (o->pattern != pattern) return false;
    if (o->idx != idx) return false;
    to = o->to;
    return true;
}

void PatternChannelRenameCommand::redo() {
    pattern->channel(idx).name = to;
    emit pattern->project->socket->patternUpdated(pattern.get());
}

void PatternChannelRenameCommand::undo() {
    pattern->channel(idx).name = from;
    emit pattern->project->socket->patternUpdated(pattern.get());
}

PatternChannelAddCommand::PatternChannelAddCommand(const std::shared_ptr<Pattern>& pattern, int channel) {
    this->pattern = pattern;
    idx = channel;
    setText("add channel");
}

void PatternChannelAddCommand::redo() {
    pattern->addChannel(idx);
    emit pattern->project->socket->patternUpdated(pattern.get());
}

void PatternChannelAddCommand::undo() {
    pattern->deleteChannel(idx);
    emit pattern->project->socket->patternUpdated(pattern.get());
}

PatternChannelDeleteCommand::PatternChannelDeleteCommand(const std::shared_ptr<Pattern>& pattern, int channel) {
    this->pattern = pattern;
    idx = channel;
    setText("delete channel");
}

void PatternChannelDeleteCommand::redo() {
    ch = std::move(*(pattern->channels.begin()+idx));
    pattern->channels.erase(pattern->channels.begin()+idx);
    emit pattern->project->socket->patternUpdated(pattern.get());
}

void PatternChannelDeleteCommand::undo() {
    pattern->channels.insert(pattern->channels.begin()+idx, std::move(ch));
    emit pattern->project->socket->patternUpdated(pattern.get());
}
