/***************************************************************************
* trackviewcommands.cpp: implementation of SetLengthCommand class
 *                                         InsertTabCommand
 *                                         MoveFingerCommand
 *                                         AddFXCommand
 *                                         SetFlagCommand
 *                                         DeleteNoteCommand
 *                                         AddColumnCommand
 *                                         SetTimeSigCommand
 *                                         InsertStrumCommand
 *                                         InsertRythm
 * 
 * This file is part of KTabDdit, a KDE tabulature editor
 *
 * copyright (C) 2003- the KTabEdit development team <mrgwen@gmail.com>
 ***************************************************************************/

/***************************************************************************
 * 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.
 *
 * See the file COPYING for more information.
 ***************************************************************************/

#include "trackviewcommands.h"
#include "util/assert.h"

#include <QtDebug>

#include "tablature/tabtrack.h"
#include "tablature/tabnote.h"
#include "tablature/tabtimes.h"
#include "tablature/tabbar.h"
#include "tablatureModel/tabtrackmodel.h"

TrackEditCommand::TrackEditCommand(TabTrackModel* track, TrackPosModel* position, const QString& text)
: QUndoCommand(text),
  mOldPosition(0x0),
  mPosition(0x0),
  mTrack(0x0)
{
	PRECONDITION( position != 0x0 );
	PRECONDITION( track != 0x0 );
	
	mPosition = position;
	mOldPosition = new TrackPosModel(*position);
	
	mTrack = track;
}

TrackEditCommand::~TrackEditCommand()
{
	delete mOldPosition;
}

void TrackEditCommand::undo()
{
	mPosition->clone(mOldPosition->position());
}

void TrackEditCommand::redo()
{
	mPosition->clone(mOldPosition->position());
}

TrackPosModel* TrackEditCommand::position() const
{
	return mPosition;
}

TabTrackModel* TrackEditCommand::track() const
{
	return mTrack;
}

TrackEditView::InsertTabCommand::InsertTabCommand(TabTrackModel* track, uchar fret, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Insert tab %0").arg(fret)),
  mOldNote(0x0),
  mOldFret(0),
  mFret(fret)
{
	mOldNote = new TabNote(0);
	mOldNote->clone(track->note(position->position()));
	
	if (track->isEmptyNote(position->position()) == true)
		mOldFret = track->fret(position->position());
}

TrackEditView::InsertTabCommand::~InsertTabCommand()
{
	delete mOldNote;
}

void TrackEditView::InsertTabCommand::undo()
{
	TabNote* note;
	TrackEditCommand::undo();
	
	note = new TabNote(0);
	note->clone(mOldNote);
	
	track()->setNote(position()->position(), note);
}

void TrackEditView::InsertTabCommand::redo()
{
	TrackEditCommand::redo();
	track()->setFret(position()->position(), mFret + 10 * mOldFret);
}

TrackEditView::InsertNoteCommand::InsertNoteCommand(TabTrackModel* track, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Insert note"))
{
}

TrackEditView::InsertNoteCommand::~InsertNoteCommand()
{
}

void TrackEditView::InsertNoteCommand::undo()
{
	TrackEditCommand::undo();
	track()->deleteTime(position()->bar(), position()->times() + 1);
}

void TrackEditView::InsertNoteCommand::redo()
{
	TabTime* time;
	TrackEditCommand::redo();
	
	time = new TabTime(0);
	track()->insertTime(position()->position(), time);
	position()->incTimes();
}

TrackEditView::InsertBarCommand::InsertBarCommand(TabTrackModel* track, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Insert column"))
{
}

TrackEditView::InsertBarCommand::~InsertBarCommand()
{
}

void TrackEditView::InsertBarCommand::undo()
{
	TrackEditCommand::undo();
	track()->deleteBar(position()->bar() + 1);
}

void TrackEditView::InsertBarCommand::redo()
{
	TrackEditCommand::redo();
	
	track()->insertEmptyBar(position()->bar() + 1);
	
	position()->incBar();
	position()->setTimes(0);
}

TrackEditView::DeleteNoteCommand::DeleteNoteCommand(TabTrackModel* track, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Delete note")),
  mOldNote(0x0)
{
	mOldNote = new TabNote(0);
	mOldNote->clone(track->note(position->position()));
}

TrackEditView::DeleteNoteCommand::~DeleteNoteCommand()
{
	delete mOldNote;
}

void TrackEditView::DeleteNoteCommand::undo()
{
	TabNote* note;
	TrackEditCommand::undo();
	
	note = new TabNote(0);
	note->clone(mOldNote);
	
	track()->setNote(position()->position(), note);
}

void TrackEditView::DeleteNoteCommand::redo()
{
	TrackEditCommand::redo();
	track()->deleteNote(position()->position());
}

TrackEditView::DeleteTimeCommand::DeleteTimeCommand(TabTrackModel* track, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Delete time")),
  mOldTime(0x0)
{
	mOldTime = new TabTime(0);
	mOldTime->clone(track->time(position->position()));
}

TrackEditView::DeleteTimeCommand::~DeleteTimeCommand()
{
	delete mOldTime;
}

void TrackEditView::DeleteTimeCommand::undo()
{
	TabTime* copy;
	TrackEditCommand::undo();
	
	copy = new TabTime(0);
	copy->clone(mOldTime);
	track()->insertTime(position()->position(), copy);
}

void TrackEditView::DeleteTimeCommand::redo()
{
	TrackEditCommand::redo();
	track()->deleteTime(position()->position());
	
	if (position()->times() >= track()->barSize(position()->bar()))
		position()->decTimes();
}

TrackEditView::DeleteBarCommand::DeleteBarCommand(TabTrackModel* track, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Delete bar")),
  mOldBar(0x0)
{
	mOldBar = new TabBar(0);
	mOldBar->clone(track->bar(this->position()->bar()));
}

TrackEditView::DeleteBarCommand::~DeleteBarCommand()
{
	delete mOldBar;
}

void TrackEditView::DeleteBarCommand::undo()
{
	TabBar* copy;
	TrackEditCommand::undo();
	
	copy = new TabBar(0);
	copy->clone(mOldBar);
	track()->insertBar(position()->bar(), copy);
}

void TrackEditView::DeleteBarCommand::redo()
{
	TrackEditCommand::redo();
	track()->deleteBar(position()->bar());
	
	if (position()->bar() == 1 && track()->size() == 1)
		position()->decBar();
}

TrackEditView::SetLengthCommand::SetLengthCommand(TabTrackModel* track, Duration duration, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Set duration")),
  mOldDuration(track->timeDuration(position->position())),
  mDuration(duration)
{
}

TrackEditView::SetLengthCommand::~SetLengthCommand()
{
}

void TrackEditView::SetLengthCommand::undo()
{
	TrackEditCommand::undo();
	track()->setTimeDuration(position()->position(), mOldDuration);
}

void TrackEditView::SetLengthCommand::redo()
{
	TrackEditCommand::redo();
	track()->setTimeDuration(position()->position(), mDuration);
}

TrackEditView::DottedNoteCommand::DottedNoteCommand(TabTrackModel* track, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Dotted Note")),
  mWasDotted(track->isDotted(position->position()))
{
}

TrackEditView::DottedNoteCommand::~DottedNoteCommand()
{
}

void TrackEditView::DottedNoteCommand::undo()
{
	TrackEditCommand::undo();
	track()->setDotted(position()->position(), mWasDotted);
}

void TrackEditView::DottedNoteCommand::redo()
{
	TrackEditCommand::redo();
	track()->setDotted(position()->position(), ~mWasDotted);
}

TrackEditView::RestNoteCommand::RestNoteCommand(TabTrackModel* track, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Rest Note")),
  mRest(track->isRest(this->position()->position()))
{
}

TrackEditView::RestNoteCommand::~RestNoteCommand()
{
}

void TrackEditView::RestNoteCommand::undo()
{
	TrackEditCommand::undo();
	track()->setRest(position()->position(), mRest);
}

void TrackEditView::RestNoteCommand::redo()
{
	TrackEditCommand::redo();
	track()->setRest(position()->position(), ~mRest);
}

TrackEditView::DeadNoteCommand::DeadNoteCommand(TabTrackModel* track, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Dead Note")),
  mWasDeadNote(track->isDeadNote(position->position()))
{
}

TrackEditView::DeadNoteCommand::~DeadNoteCommand()
{
}

void TrackEditView::DeadNoteCommand::undo()
{
	TrackEditCommand::undo();
	track()->setDeadNote(position()->position(), mWasDeadNote);
}

void TrackEditView::DeadNoteCommand::redo()
{
	TrackEditCommand::undo();
	track()->setDeadNote(position()->position(), ~mWasDeadNote);
}

TrackEditView::BendNoteCommand::BendNoteCommand(TabTrackModel* track, TrackPosModel* position, const TabBend& bend)
: TrackEditCommand(track, position, tr("Bend Note")),
  mOldBend(0x0),
  mBend(0x0)
{
	mOldBend = new TabBend();
	mOldBend->clone(track->bend(position->position()));
	
	mBend = new TabBend();
	mBend->clone(&bend);
}

TrackEditView::BendNoteCommand::~BendNoteCommand()
{
	delete mOldBend;
	delete mBend;
}

void TrackEditView::BendNoteCommand::undo()
{
	TrackEditCommand::undo();
	TabBend* bend = new TabBend();
	
	bend->clone(mOldBend);
	track()->setBend(position()->position(), bend);
}

void TrackEditView::BendNoteCommand::redo()
{
	TrackEditCommand::redo();
	TabBend* bend = new TabBend();
	
	bend->clone(mBend);
	track()->setBend(position()->position(), bend);
}

TrackEditView::GhostNoteCommand::GhostNoteCommand(TabTrackModel* track, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Ghost Note")),
  mWasGhostNote(track->isGhostNote(position->position()))
{
}

TrackEditView::GhostNoteCommand::~GhostNoteCommand()
{
}

void TrackEditView::GhostNoteCommand::undo()
{
	TrackEditCommand::undo();
	track()->setGhostNote(position()->position(), mWasGhostNote);
}

void TrackEditView::GhostNoteCommand::redo()
{
	TrackEditCommand::undo();
	track()->setGhostNote(position()->position(), ~mWasGhostNote);
}

TrackEditView::TieNoteCommand::TieNoteCommand(TabTrackModel* track, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Tie Note")),
  mWasTieNote(track->isTieNote(position->position()))
{
}

TrackEditView::TieNoteCommand::~TieNoteCommand()
{
}

void TrackEditView::TieNoteCommand::undo()
{
	TrackEditCommand::undo();
	track()->setTieNote(position()->position(), mWasTieNote);
}

void TrackEditView::TieNoteCommand::redo()
{
	TrackEditCommand::undo();
	track()->setTieNote(position()->position(), ~mWasTieNote);
}

TrackEditView::HammerNoteCommand::HammerNoteCommand(TabTrackModel* track, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Hammer Note")),
  mWasHammerNote(track->isHammerOnPullOff(position->position()))
{
}

TrackEditView::HammerNoteCommand::~HammerNoteCommand()
{
}

void TrackEditView::HammerNoteCommand::undo()
{
	TrackEditCommand::undo();
	track()->setHammerOnPullOff(position()->position(), mWasHammerNote);
}

void TrackEditView::HammerNoteCommand::redo()
{
	TrackEditCommand::undo();
	track()->setHammerOnPullOff(position()->position(), ~mWasHammerNote);
}

TrackEditView::LetRingNoteCommand::LetRingNoteCommand(TabTrackModel* track, TrackPosModel* position)
: TrackEditCommand(track, position, tr("Let Ring Note")),
  mWasLetRingNote(track->isLetRing(position->position()))
{
}

TrackEditView::LetRingNoteCommand::~LetRingNoteCommand()
{
}

void TrackEditView::LetRingNoteCommand::undo()
{
	TrackEditCommand::undo();
	track()->setLetRing(position()->position(), mWasLetRingNote);
}

void TrackEditView::LetRingNoteCommand::redo()
{
	TrackEditCommand::undo();
	track()->setLetRing(position()->position(), ~mWasLetRingNote);
}

/*TrackEditView::MoveFingerCommand::MoveFingerCommand(TrackView *, TabTrack *, int _from, int _to, int _tune)
: QUndoCommand(tr("Transpose"))
{
	kDebug() << "TODO" << endl;
	
	if (_to < _from) {
		setName(tr("Transpose down"));
	} else {
		setName(tr("Transpose up"));
	}
}

void TrackEditView::MoveFingerCommand::execute()
{
}

void TrackEditView::MoveFingerCommand::unexecute()
{
}

TrackEditView::DeleteNoteCommand::DeleteNoteCommand(TrackEditView & tv, TabTrack& trk, TrackPos& pos)
: QUndoCommand(tr("Delete note")), currentPos(pos), oldPos(pos),
  trackView(tv), oldBar(trk.bar(pos.bar())), tabTrack(trk)
{
	setName(tr("Delete note %1").arg(pos.times()));
}

void TrackEditView::DeleteNoteCommand::execute()
{
	currentPos = oldPos;
	
	TabBar tempBar = tabTrack.bar(oldPos.bar());
	TabTimes tempTimes = tempBar.times(oldPos.times());
	bool checkDelete = false;
	
	for (uchar i = 0; i < tabTrack.nbStrings(); i++) {
		if (!tempTimes.notes(i).isEmpty()) {
			checkDelete = true;
			break;
		}
	}
	
	if (checkDelete) {
		TabNote note;
		tempTimes.setNotes(oldPos.chord(), note);
// 		tempTimes.setNotes(oldPos.chord(), 255);
		tempBar.setTimes(oldPos.times(), tempTimes);
	} else {
		tempBar.deleteTimes(oldPos.times());
		
		if (currentPos.times() >= 1) {
			currentPos.decTimes();
		}
	}
// 	TabBar tempBar = tabTrack.bar(oldPos.bar());
// 	tempBar.deleteTimes(oldPos.times());
	tabTrack.setBar(oldPos.bar(), tempBar);
	
//	if (currentPos.times() >= 1) {
//		currentPos.decTimes();
//	}
	
// 	emit trackView.songChanged(true);
	// trackView.repaintCell();
}

void TrackEditView::DeleteNoteCommand::unexecute()
{
	tabTrack.setBar(oldPos.bar(), oldBar);
	
	currentPos = oldPos;
	
// 	emit trackView.songChanged(false);
	// trackView.repaintCell();
}

TrackEditView::DeleteColumnCommand::DeleteColumnCommand(TrackEditView & tv, TabTrack& trk, TrackPos& pos)
: QUndoCommand(tr("Delete column")), currentPos(pos), oldPos(pos),
  countBar(trk.size()), oldBar(trk.bar(pos.bar())), trackView(tv), tabTrack(trk)
{
	setName(tr("Delete %1 columns").arg(QString::number(pos.bar())));
}

void TrackEditView::DeleteColumnCommand::execute()
{
	currentPos = oldPos;
	
	if (countBar == 1) {
		TabBar tempBar;
		
		tempBar.setTimeSignature(0, tabTrack.timeSignature(0));
		tempBar.setTimeSignature(1, tabTrack.timeSignature(1));
		
		// Insert an empty bar
		//
		tabTrack.setBar(oldPos.bar(), tempBar);
		
		currentPos.setTimes(0);
		currentPos.setBar(0);
	} else {
		// Delete the current bar
		//
		tabTrack.deleteBar(currentPos.bar());
		
		// There is a bar beside the current bar so
		// we can decrease the bar number and the
		// the correct time position
		//
		if (currentPos.bar() > 0) {
			currentPos.decBar();
			
			TabBar tempBar = tabTrack.bar(currentPos.bar());
			currentPos.setTimes(tempBar.count() - 1);
		} else {
			currentPos.setTimes(0);
			currentPos.setBar(0);
		}
	}
	
// 	emit trackView.songChanged(true);
// 	trackView.update();
	// trackView.repaintCell();
}

void TrackEditView::DeleteColumnCommand::unexecute()
{
	currentPos = oldPos;
	
	tabTrack.insertBar(oldPos.bar(), oldBar);
	
// 	emit trackView.songChanged(false);
// 	trackView.update();
	// trackView.repaintCell();
}

TrackEditView::SetTimeSigCommand::SetTimeSigCommand(TrackEditView & tv, TabTrack& trk, int _bar, bool _toend, int _time1, int _time2)
:QUndoCommand(tr("Set time sig.")), bar(_bar), time1(_time1), time2(_time2), oldTime1(trk.bar(_bar).timeSignature(0)),   
 oldTime2(trk.bar(_bar).timeSignature(1)), defaultOldTime1(trk.timeSignature(0)),
 defaultOldTime2(trk.timeSignature(0)), toend(_toend), tabTrack(trk), trackView(tv)
{
}

void TrackEditView::SetTimeSigCommand::execute()
{
	if (toend) {
		tabTrack.setTimeSignature(0, time1);
		tabTrack.setTimeSignature(1, time2);
	}
	
	TabBar tempBar = tabTrack.bar(bar);
	
	tempBar.setTimeSignature(0, time1);
	tempBar.setTimeSignature(1, time2);
	
	tabTrack.setBar(bar, tempBar);
	
// 	emit trackView.songChanged(true);
	// trackView.repaintCell();

}

void TrackEditView::SetTimeSigCommand::unexecute()
{
	if (toend) {
		tabTrack.setTimeSignature(0, defaultOldTime1);
		tabTrack.setTimeSignature(1, defaultOldTime2);
	}
	
	TabBar tempBar = tabTrack.bar(bar);
	
	tempBar.setTimeSignature(0, oldTime1);
	tempBar.setTimeSignature(1, oldTime2);
	
	tabTrack.setBar(bar, tempBar);
	
// 	emit trackView.songChanged(false);
// 	trackView.update();
	// trackView.repaintCell(); //for emit paneChanded
}

TrackEditView::InsertStrumCommand::InsertStrumCommand(TrackEditView & tv, TabTrack& trk, TrackPos& pos, QMemArray<int> &_chord)
: QUndoCommand(tr("Insert strum")), currentPos(pos), oldPos(pos),
  chord(_chord), oldTimes(trk.bar(pos.bar()).times(pos.times())), trackView(tv), tabTrack(trk)
{
	setName(tr("Insert Chord"));
}

void TrackEditView::InsertStrumCommand::execute()
{
	TabBar tempBar = tabTrack.bar(oldPos.bar());
	TabTimes tempTimes = tempBar.times(oldPos.times());
	
	for (unsigned int i = 0; i < tabTrack.nbStrings(); i++) {
		TabNote note;
		
		if (chord[i] == -1)
			note.setDeadNote(true);
		else
			note.setFret(chord[i]);
		
		tempTimes.setNotes(tabTrack.nbStrings() - 1 - i , note);
	}
	
	tempBar.setTimes(oldPos.times(), tempTimes);
	tabTrack.setBar(oldPos.bar(), tempBar);
	
	currentPos = oldPos;
	
// 	trackView.update();
// 	emit trackView.songChanged(true);
	// trackView.repaintCell();
}

void TrackEditView::InsertStrumCommand::unexecute()
{
	TabBar tempBar = tabTrack.bar(oldPos.bar());
	TabTimes tempTimes = tempBar.times(oldPos.times());
	
	tempBar.setTimes(oldPos.times(), oldTimes);
	tabTrack.setBar(oldPos.bar(), tempBar);
	
	currentPos = oldPos;
	
// 	trackView.update();
// 	emit trackView.songChanged(false);
	// trackView.repaintCell();
}

TrackEditView::InsertRhythm::InsertRhythm(TrackEditView & tv, TabTrack& trk, QListBox& quantized, TrackPos& pos)
: QUndoCommand(tr("Insert rhythm")), currentPos(pos), oldPos(pos),
  tabTrack(trk), trackView(tv)
{
	newdur.resize(quantized.count() - 1);
	
	for (uint i = 1; i < quantized.count(); i++)
		newdur[i - 1] = static_cast<Duration>(quantized.text(i).toInt());
}

void TrackEditView::InsertRhythm::execute()
{
	TabBar tempBar = tabTrack.bar(oldPos.bar());
	
	for (uint i = 0; i < newdur.count(); i++) {
		TabTimes tempTimes;
		
		tempTimes.setDuration(newdur[i]);
		tempBar.insertTimes(tempTimes);
	}
	
	tabTrack.setBar(oldPos.bar(), tempBar);
	
	currentPos = oldPos;
	
// 	emit trackView.songChanged(true);
// 	trackView.repaintContents();
}

void TrackEditView::InsertRhythm::unexecute()
{
	tabTrack.setBar(oldPos.bar(), oldBar);
	
	currentPos = oldPos;
	
// 	emit trackView.songChanged(false);
// 	trackView.repaintContents();
}

TrackEditView::NTupletCommand::NTupletCommand(TrackEditView& _tv, TabTrack &_trk, TrackPos& pos, uint n)
: QUndoCommand(tr("NTuplet")), currentPos(pos), oldPos(pos),
  tv(_tv), tabTrack(_trk), oldTime(_trk.bar(pos.bar()).times(pos.times())), ntuplet(n)
{
}

void TrackEditView::NTupletCommand::execute()
{
	TabBar tempBar = tabTrack.bar(oldPos.bar());
	TabTimes tempTimes = tempBar.times(oldPos.times());
	
	tempTimes.setNTuplet(ntuplet);
	
	tempBar.setTimes(oldPos.times(), tempTimes);
	tabTrack.setBar(oldPos.bar(), tempBar);
	
	currentPos = oldPos;
	
// 	emit tv.songChanged(true);
// 	tv.repaintCell();
}

void TrackEditView::NTupletCommand::unexecute()
{
	TabBar tempBar = tabTrack.bar(oldPos.bar());
	
	tempBar.setTimes(oldPos.times(), oldTime);
	tabTrack.setBar(oldPos.bar(), tempBar);
	
	currentPos = oldPos;
	
// 	emit tv.songChanged(true);
// 	tv.repaintCell();
}
*/
