/***************************************************************************
 * trackveditiew.h: implementation of TrackEditView class
 *
 * This file is part of KTabEdit, a KDE tabulature editor
 *
 * copyright (C) 2003-2007 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 "trackeditview.h"
#include "util/assert.h"

#include <QApplication>
#include <QClipboard>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QPaintEvent>

#include "clipboard/serialize.h"
#include "qcanvascm/qcanvasrectanglecm.h"
#include "tablature/tabtrack.h"
#include "tablatureModel/tabtrackmodel.h"
#include "settings.h"
#include "timesig.h"
#include "trackbarview.h"
#include "trackcursorview.h"
#include "tracksystemview.h"
#include "trackpageview.h"
#include "trackviewcommands.h"

#include "kprofilemethod.h"

int pr_refresh = 0;

// Dpi resolution
//
const uint TrackEditView::sDpi = 72;

// Margins in cm
//
const Cm TrackEditView::sMargins = Cm(1.00, sDpi);

// Size of a page
//
const QSizeCm TrackEditView::sPageSize = QSizeCm(Cm(21.0, sDpi), Cm(29.7, sDpi));

TrackEditView::TrackEditView(QUndoStack* commandHistory, QWidget* parent)
: QGraphicsView(parent),
  TabTrackObserver(),
  mCommandHistory(commandHistory),
  mCanvas(0x0),
  mSongProperties(0x0),
  mTabTrack(0x0),
  mCursorPosition(),
  mCursorView(0x0),
  mReadOnly(false),
  mModified(false),
  mViewScore(true),
  mViewTablature(true),
  size(632, 0),
  widthBar(582),
  mStartSelection(false),
  mSelection(),
  mGraphicsSelection(),
  mBeginSelection(),
  mEndSelection(),
  mIsSelection(false),
  mClefFont(),
  mTimeFont(),
  mTrackPageList(0x0),
  mBarHeight(0, sDpi)
{
	mTabTrack = new TabTrackModel();
	
	mTabTrack->attach(this);
	mCursorPosition.attach(this);
	
	initCanvasView();
	initPageViewer();
	initCursorView();
}

TrackEditView::~TrackEditView()
{
	delete mCursorView;
	delete mTrackPageList;
}

void TrackEditView::initCanvasView()
{
	mCanvas = new QGraphicsScene(this);
	setScene(mCanvas);
	setFrameStyle(QFrame::Panel | QFrame::Sunken);
	
	mCanvas->addItem(&mGraphicsSelection);
	mGraphicsSelection.show();
}

void TrackEditView::initPageViewer()
{
	mClefFont = QFont(Settings::getInstance()->timeSignatureFont());
	mTimeFont = QFont(Settings::getInstance()->timeSignatureFont());
	
	mTrackPageList = new TrackPageList();
	
	mTrackPageList->setGraphicsScene(mCanvas);
	mTrackPageList->setPageSize(sPageSize);
	mTrackPageList->setMargins(sMargins, sMargins);
	
	//setSceneRect(sPageSize.width().toPixel().value(), sPageSize.height().toPixel().value());
}

void TrackEditView::initCursorView()
{
	mCursorView = new TrackCursorView(sDpi);
	mCursorView->setGraphicsScene(mCanvas);
	mCursorView->show();
}

void TrackEditView::songChanged(TabProperties* songProperties, TabTrack* tabTrack)
{
	qDebug() << "[TrackEditView::songChanged] " << mSongProperties->title() << "\n";
	mSongProperties = songProperties;
	mTabTrack->setTabTrack(tabTrack);
	
	refreshView();
	//refreshCursor();
	
	connectTrack();
}

void TrackEditView::songPropertiesChanged(TabProperties* songProperties)
{
	PRECONDITION( songProperties != 0x0 );
	
	if (songProperties != mSongProperties) {
		mSongProperties = songProperties;
		mTrackPageList->setSongProperties(mSongProperties);
	}
}

void TrackEditView::songPropertyChanged()
{
	qDebug() << "[TrackEditView::songChanged] " << mSongProperties->title() << "\n";
	refreshView();
}

const TabProperties* TrackEditView::songProperties() const
{
	POSTCONDITION( mSongProperties != 0x0 );
	
	return mSongProperties;
}

void TrackEditView::setTabTrack(TabTrack* tabTrack)
{
	PRECONDITION( tabTrack != 0x0 );
	
	if (mTabTrack->hasTrack() == false || tabTrack != mTabTrack->tabTrack()) {
		mTabTrack->setTabTrack(tabTrack);
		mTrackPageList->setTabTrack(tabTrack);
		
		refreshView();
		refreshCursor();
		
		connectTrack();
	}
}

TabTrack* TrackEditView::tabTrack() const
{
	POSTCONDITION( mTabTrack != 0x0 );
	
	return mTabTrack->tabTrack();
}

TrackProperties* TrackEditView::tabTrackProperties() const
{
	return mTabTrack->properties();
}

void TrackEditView::setCursorPosition(const TrackPos& cursorPosition)
{
	mCursorPosition.clone(cursorPosition);
}

TrackPos TrackEditView::cursorPosition() const
{
	return mCursorPosition.position();
}

/**
@brief Sets the track editor in read-only or not (according to the parameter b)
@param b Boolean
*/
void TrackEditView::setReadOnly(bool b)
{
	mReadOnly = b;
}


bool TrackEditView::isReadOnly() const
{
	return mReadOnly;
}


bool TrackEditView::isModified() const
{
	return mModified;
}


void TrackEditView::setSelection(const TrackPos& begin, const TrackPos& end)
{
	mBeginSelection = begin;
	mEndSelection   = end;
	
	mIsSelection = false;
}


void TrackEditView::selectAll(bool select)
{
}

void TrackEditView::getSelection(TrackPos* begin, TrackPos* end) const
{
	*begin = mBeginSelection;
	*end   = mEndSelection;
}


bool TrackEditView::hasSelection() const
{
	return mIsSelection;
}


/**
@brief View the score
@param on Boolean
*/
void TrackEditView::viewScore(bool on)
{
	mViewScore = on;
}


bool TrackEditView::isViewScore() const
{
	return mViewScore;
}


/**
@brief View the tablature
@param on Boolean
*/
void TrackEditView::viewTablature(bool on)
{
	mViewTablature = on;
}


bool TrackEditView::isViewTablature() const
{
	return mViewTablature;
}


/**
@brief Zooms in on the text
Zooms in on the text by by making the base font size range points larger and recalculating all font sizes to be the new size. This does not change the size of any images.
@param range not used?
*/
void TrackEditView::zoomIn(int range)
{
	qDebug() << "zoomIn\n";
	QMatrix matrix = this->matrix();
	matrix.scale(1.25, 1.25);
	setMatrix(matrix);

	mCanvas->update();
}


/**
@brief Zooms out the text
Zooms out the text by making the base font size size points and recalculating all font sizes to be the new size.
This does not change the size of any images.
@param range not used?
*/
void TrackEditView::zoomOut(int range)
{
	qDebug() << "zoomOut\n";
	QMatrix matrix = this->matrix();
	matrix.scale(0.80, 0.80);
	setMatrix(matrix);
	
	mCanvas->update();
}


void TrackEditView::copy()
{
	mBeginSelection = TrackPos(0,0,0);
	mEndSelection = TrackPos(0,0,3);
	QClipboard* clipboard = QApplication::clipboard();
	QMimeData* mimeData = new QMimeData;
	Serialize serialize(mTabTrack->tabTrack(), mBeginSelection, mEndSelection);
	
	mimeData->setData("music/ktabedit", serialize.exec());
	clipboard->setMimeData(mimeData);
}


void TrackEditView::cut()
{
}


void TrackEditView::del()
{
}


void TrackEditView::paste()
{
	QClipboard* clipboard = QApplication::clipboard();
	const QMimeData* mimeData = clipboard->mimeData();
	
	if (mimeData->hasFormat("music/ktabedit") == true) {
		qDebug() << "yes\n";
	}
}


void TrackEditView::emitSignalsOfCurrentTime()
{
	emit isAccentuated(mTabTrack->isAccentuated(mCursorPosition.position()));
	emit isGhostNote(mTabTrack->isGhostNote(mCursorPosition.position()));
	emit isTieNote(mTabTrack->isTieNote(mCursorPosition.position()));
	emit isHammerOnPullOff(mTabTrack->isHammerOnPullOff(mCursorPosition.position()));
	//emit isSlide(mTabTrack->isSlide(mCursorPosition));
	emit isLetRing(mTabTrack->isLetRing(mCursorPosition.position()));
	emit isDynamic(mTabTrack->isDynamic(mCursorPosition.position()));
	emit isFingeringLeft(mTabTrack->isFingeringLeft(mCursorPosition.position()));
	emit isFingeringRight(mTabTrack->isFingeringRight(mCursorPosition.position()));
//	emit isLeftHandVibrato(mTabTrack->isLeftHandVibrato(mCursorPosition));
	emit isPalmMute(mTabTrack->isPalmMute(mCursorPosition.position()));
	emit isStaccato(mTabTrack->isStaccato(mCursorPosition.position()));
	emit isTremoloPicking(mTabTrack->isTremoloPicking(mCursorPosition.position()));
	emit isHarmonic(mTabTrack->isHarmonic(mCursorPosition.position()));
	emit isBend(mTabTrack->isBend(mCursorPosition.position()));
	emit isGraceNote(mTabTrack->isGraceNote(mCursorPosition.position()));
	emit isTrill(mTabTrack->isTrill(mCursorPosition.position()));
	emit isRasgueado(mTabTrack->isRasgueado(mCursorPosition.position()));
	emit isPickStroke(mTabTrack->hasPickStroke(mCursorPosition.position()));
	emit isTremolo(mTabTrack->hasTremolo(mCursorPosition.position()));
	emit isTapping(mTabTrack->isTapping(mCursorPosition.position()));
	emit isPopping(mTabTrack->isPopping(mCursorPosition.position()));
	emit isSlapping(mTabTrack->isSlapping(mCursorPosition.position()));
	emit isStroke(mTabTrack->isStroke(mCursorPosition.position()));
	emit isVibrato(mTabTrack->isVibrato(mCursorPosition.position()));
	emit isFadeIn(mTabTrack->isFadeIn(mCursorPosition.position()));
}

void TrackEditView::insertTime()
{
	mCommandHistory->push(new InsertNoteCommand(mTabTrack, &mCursorPosition));
}


void TrackEditView::insertBar()
{
	mCommandHistory->push(new InsertBarCommand(mTabTrack, &mCursorPosition));
}


void TrackEditView::deleteNote()
{
	mCommandHistory->push(new DeleteNoteCommand(mTabTrack, &mCursorPosition));
}

void TrackEditView::deleteTime()
{
	mCommandHistory->push(new DeleteTimeCommand(mTabTrack, &mCursorPosition));
}


void TrackEditView::deleteBar()
{
	mCommandHistory->push(new DeleteBarCommand(mTabTrack, &mCursorPosition));
}


void TrackEditView::setArmor(uchar armor)
{
/*// 	short oldsig = mTabTrack->getVectTabBar(0).getKeySignature();

	if ((oldsig <= -8) || (8 <= oldsig)) {
		oldsig = 0;
	}

	SetKeySig *sks = new SetKeySig();
	sks->sig->setCurrentItem(7 - oldsig);

	if (sks->exec()) {
		int newsig = sks->sig->currentItem();
// 		TabBar tab = mTabTrack->getVectTabBar(0);
		tab.setKeySignature((short) (7 - newsig));
// 		mTabTrack->setVectTabBar(0, tab);
	}

	//lastnumber = -1;*/
}


void TrackEditView::setTimeSignature(const TimeSignature& timeSignature)
{
	SetTimeSig *sts = new SetTimeSig(this);

	sts->getTime1().setValue(mTabTrack->numeratorOfBar(mCursorPosition.bar()));

	switch (mTabTrack->denominatorOfBar(mCursorPosition.bar()))
	{
		case 1:	 sts->getTime2().setCurrentIndex(0); break;
		case 2:	 sts->getTime2().setCurrentIndex(1); break;
		case 4:	 sts->getTime2().setCurrentIndex(2); break;
		case 8:	 sts->getTime2().setCurrentIndex(3); break;
		case 16: sts->getTime2().setCurrentIndex(4); break;
		case 32: sts->getTime2().setCurrentIndex(5); break;
	}

	if (sts->exec()) {
		int time1 = sts->getTime1().value();
		int time2 = ((QString) sts->getTime2().currentText()).toUInt();

		//mCommandHistory->push(new SetTimeSigCommand(*this, *mTabTrack, mCursorPosition.bar(), sts->getToEnd().isChecked(), time1, time2));
	}
}


void TrackEditView::switchDotted()
{
	mCommandHistory->push(new DottedNoteCommand(mTabTrack, &mCursorPosition));
}


void TrackEditView::setNTuplet(uint i)
{
	//mCommandHistory->push(new NTupletCommand(*this, *mTabTrack, mCursorPosition, i));
}


void TrackEditView::setRest()
{
	mCommandHistory->push(new RestNoteCommand(mTabTrack, &mCursorPosition));
}

void TrackEditView::setDeadNote()
{
	mCommandHistory->push(new DeadNoteCommand(mTabTrack, &mCursorPosition));
}

void TrackEditView::setChord(const TabChord& tabChord)
{
	//QMemArray<int> a(mTabTrack->nbStrings());
// 	ChordSelector cs(/*mMidiScheduler, */*mTabTrack);
	int i;
	
/*	// required to detect chord from tabulature
	//
	cs.detectChord();
	
	// set fingering right if frets > 5
	//
	for (i = 0; i < mTabTrack->nbStrings(); i++)
		a[i] = cs.app(i);
	
	cs.getFingering().setFingering(a);
	
	if (cs.exec()) {
		for (i = 0; i < mTabTrack->nbStrings(); i++)
			a[i] = cs.app(i);
		
		mCommandHistory->push(new InsertStrumCommand(*this, *mTabTrack, mCursorPosition, a));
		change = true;
	}*/
}

/***************************************************************************
 *                                                                         *
 * Effects actions                                                         *
 *                                                                         *
 ***************************************************************************/

void TrackEditView::setAccentuated(bool b)
{
}

void TrackEditView::setGhostNote(bool b)
{
}

void TrackEditView::setTieNote(bool b)
{
}

void TrackEditView::setHammerOnPullOff(bool b)
{
}

void TrackEditView::setSlide(Slide slide)
{
}

void TrackEditView::setLetRing(bool b)
{
}

void TrackEditView::setDynamic(Dynamic dynamic)
{
}

void TrackEditView::setFingeringLeft(TabFingering fingering)
{
}

void TrackEditView::setFingeringRight(TabFingering fingering)
{
}

void TrackEditView::setLeftHandVibrato(bool b)
{
}

void TrackEditView::setPalmMute(bool b)
{
}
void TrackEditView::setStaccato(bool b)
{
}

void TrackEditView::setTremoloPicking(uchar c)
{
}

void TrackEditView::setHarmonic(Harmonic harmonic)
{
}

void TrackEditView::setBend(const TabBend& bend)
{
	if (mTabTrack->isEmptyNote(mCursorPosition.position()) == false && mTabTrack->isDeadNote(mCursorPosition.position()) == false)
		mCommandHistory->push(new BendNoteCommand(mTabTrack, &mCursorPosition, bend));
}

void TrackEditView::setGraceNote(const TabGraceNote& graceNote)
{
}

void TrackEditView::setTrill(const TabTrill& trill)
{
}

void TrackEditView::setRasgueado(bool b)
{
}

void TrackEditView::setPickStroke(bool b)
{
}

void TrackEditView::setTremolo(uint i)
{
}

void TrackEditView::setTapping(bool b)
{
}

void TrackEditView::setPopping(bool b)
{
}

void TrackEditView::setSlapping(bool b)
{
}

void TrackEditView::setStroke(bool b)
{
}

void TrackEditView::setVibrato(TabVibrato vibrato)
{
}

void TrackEditView::setFadeIn(bool b)
{
}


/***************************************************************************
 *                                                                         *
 * Fret actions                                                            *
 *                                                                         *
 ***************************************************************************/

/**
@brief Smart delete
Check if we delete the bar (because there are no times) we can delete the current time or a chord fretted in the time
*/
void TrackEditView::smartDelete()
{
	if (mReadOnly == false) {
		if (mTabTrack->barSize(mCursorPosition.bar()) == 1 && mTabTrack->isEmptyTime(mCursorPosition.position()) == true)
			deleteBar();
		else if (mTabTrack->isEmptyNote(mCursorPosition.position()) == false)
				deleteNote();
			else if (mTabTrack->isEmptyTime(mCursorPosition.position()) == true)
					deleteTime();
	}
}


/***************************************************************************
 *                                                                         *
 * Key actions                                                             *
 *                                                                         *
 ***************************************************************************/

void TrackEditView::keyPressEvent(QKeyEvent* e)
{
	if (e->state() == Qt::NoButton) {
		switch(e->key()) {
		case Qt::Key_Delete:
			smartDelete();
			break;
		case Qt::Key_Left:
			keyLeft();
			break ;
		case Qt::Key_Right:
			keyRight();
			break ;
		case Qt::Key_Up:
			keyUp();
			break;
		case Qt::Key_Down:
			keyDown();
			break;
		case Qt::Key_Home:
			keyHome();
			break;
		case Qt::Key_End:
			keyEnd();
			break;
		case Qt::Key_Equal:
			keyPlus();
			break;
		case Qt::Key_Minus:
			keyMinus();
			break;
		case Qt::Key_Semicolon:
			keySemiColon();
			break;
		case Qt::Key_0:
			keyNumber(0);
			break;
		case Qt::Key_1:
			keyNumber(1);
			break;
		case Qt::Key_2:
			keyNumber(2);
			break;
		case Qt::Key_3:
			keyNumber(3);
			break;
		case Qt::Key_4:
			keyNumber(4);
			break;
		case Qt::Key_5:
			keyNumber(5);
			break;
		case Qt::Key_6:
			keyNumber(6);
			break;
		case Qt::Key_7:
			keyNumber(7);
			break;
		case Qt::Key_8:
			keyNumber(8);
			break;
		case Qt::Key_9:
			keyNumber(9);
			break;
		case Qt::Key_X:
			setDeadNote();
			break;
		default:
			e->ignore();
			return ;
		}
	} else if (e->state() == Qt::ControlButton) {
		switch(e->key()) {
		case Qt::Key_Home:
			keyCtrlHome();
			break;
		case Qt::Key_End:
			keyCtrlEnd();
			break;
		default:
			e->ignore();
			return ;
		}
	} else if (e->state() == Qt::ShiftButton) {
		switch(e->key()) {
		case Qt::Key_Left:
			keyShiftLeft();
			break;
		case Qt::Key_Right:
			keyShiftRight();
			break;
		case Qt::Key_Semicolon:
			keySemiColon();
			break;
		case Qt::Key_0:
			keyNumber(0);
			break;
		case Qt::Key_1:
			keyNumber(1);
			break;
		case Qt::Key_2:
			keyNumber(2);
			break;
		case Qt::Key_3:
			keyNumber(3);
			break;
		case Qt::Key_4:
			keyNumber(4);
			break;
		case Qt::Key_5:
			keyNumber(5);
			break;
		case Qt::Key_6:
			keyNumber(6);
			break;
		case Qt::Key_7:
			keyNumber(7);
			break;
		case Qt::Key_8:
			keyNumber(8);
			break;
		case Qt::Key_9:
			keyNumber(9);
			break;
		default:
			e->ignore();
			return ;
		}
	}
	
	e->accept();
}


/**
@Brief Move the cursor to the left by one column
*/
void TrackEditView::keyLeft()
{
	TabBar* currentBar;
	
	if (mCursorPosition.times() > 0) {
		mCursorPosition.decTimes();
		currentBar = mTabTrack->bar(mCursorPosition.bar());
	} else if (mCursorPosition.bar() > 0) {
		mCursorPosition.decBar();
		currentBar = mTabTrack->bar(mCursorPosition.bar());
		
		mCursorPosition.setTimes(currentBar->size() - 1);
	}
	//if (currentBar->isOverTaken()) {
// 		emit statusBar("time overtake !");
	//}
	
	//refreshCursor();
}


/**
@Brief Move the cursor to the right by one column
*/
void TrackEditView::keyRight()
{
	TabBar* currentBar = mTabTrack->bar(mCursorPosition.bar());
	
	// Is it the last bar ?
	if (mTabTrack->size() == (mCursorPosition.bar() + 1)) {
		// Is it the last note ?
		//
		if (currentBar->size() == (mCursorPosition.times() + 1) && mReadOnly == false) {
			// Is it full ?
			//
			if (currentBar->isFull())
				mCommandHistory->push(new InsertBarCommand(mTabTrack, &mCursorPosition));
			else
				mCommandHistory->push(new InsertNoteCommand(mTabTrack, &mCursorPosition));
			
		} else {
			// Go to the next note
			//
			mCursorPosition.incTimes();
		}
	} else {
		// Is it the last note ?
		//
		if (currentBar->size() == (mCursorPosition.times() + 1)) {
			// Is it full ?
			//
			if (currentBar->isFull()) {
				// Go to the next bar
				//
				mCursorPosition.setTimes(0);
				mCursorPosition.incBar();
			} else
				if (mReadOnly == false)
					mCommandHistory->push(new InsertNoteCommand(mTabTrack, &mCursorPosition));
		} else {
			mCursorPosition.incTimes();
		}
	}
	
	if (currentBar->isOverTaken()) {
// 		emit statusBar("time overtake !");
	}
	//refreshCursor();
}


/**
@brief Move the cursor to a high string
*/
void TrackEditView::keyUp()
{
	if(mCursorPosition.chord() > 0) {
		mCursorPosition.decChord();
	}
	//refreshCursor();
}


/**
@brief Move the cursor to a low string
*/
void TrackEditView::keyDown()
{
	if(mCursorPosition.chord() + 1 < mTabTrack->nbStrings()) {
		mCursorPosition.incChord();
	}
	//refreshCursor();
}


/**
@brief Move cursor to the beginning of the bar, breaking selection
*/
void TrackEditView::keyHome()
{
	mCursorPosition.setBar(0);
	mCursorPosition.setTimes(0);
	//refreshCursor();
}

/**
@brief Move cursor to the end of the bar, breaking selection
*/
void TrackEditView::keyEnd()
{
	if (mTabTrack->size() > 0)
		mCursorPosition.setBar(mTabTrack->size() - 1);
	else
		mCursorPosition.setBar(0);
	
	mCursorPosition.setTimes(0);
	//refreshCursor();
}


/**
@brief Move cursor to the very beginning of the song, breaking selection
*/
void TrackEditView::keyCtrlHome()
{
	mCursorPosition.setTimes(0);
}


/**
@brief Move cursor to the very end of the song
*/
void TrackEditView::keyCtrlEnd()
{
	//TabBar tempBar = mTabTrack->bar(mCursorPosition.bar());
	
	mCursorPosition.setTimes(mTabTrack->barSize(mCursorPosition.bar()) - 1);
}


/**
@brief Move the cursor to a high string
*/
void TrackEditView::keyShiftLeft()
{
}

/**
@brief Move the cursor to an high string.
*/
void TrackEditView::keyShiftRight()
{
}


/**
@brief Increase the duration of the note under cursor
*/
void TrackEditView::keyPlus()
{
	if (mReadOnly == true)
		return ;
	
	const Duration duration = mTabTrack->timeDuration(mCursorPosition.position());
	
	if (duration < Whole && duration != NoneDuration) {
		Duration newDuration = NoneDuration;
		
		switch (duration) {
		case Half:
			newDuration = Whole;
			break;
		case Quarter:
			newDuration = Half;
			break;
		case Eighth:
			newDuration = Quarter;
			break;
		case Sixteenth:
			newDuration = Eighth;
			break;
		case ThirthSecond:
			newDuration = Sixteenth;
			break;
		case SixtyFourth:
			newDuration = ThirthSecond;
			break;
		case HundredTwentyEighth:
			newDuration = SixtyFourth;
		case Whole:
		case NoneDuration:
			break;
		}
		
		setDuration(newDuration);
	}
}


/**
@brief Derease the duration of the note under cursor
*/
void TrackEditView::keyMinus()
{
	if (mReadOnly == true)
		return ;
	
	const Duration duration = mTabTrack->timeDuration(mCursorPosition.position());
	
	if (duration > HundredTwentyEighth && duration != NoneDuration) {
		Duration newDuration = NoneDuration;
		
		switch (duration) {
		case Whole:
			newDuration = Half;
			break;
		case Half:
			newDuration = Quarter;
			break;
		case Quarter:
			newDuration = Eighth;
			break;
		case Eighth:
			newDuration = Sixteenth;
			break;
		case Sixteenth:
			newDuration = ThirthSecond;
			break;
		case ThirthSecond:
			newDuration = SixtyFourth;
			break;
		case SixtyFourth:
			newDuration = HundredTwentyEighth;
		case HundredTwentyEighth:
		case NoneDuration:
			break;
		}
		
		setDuration(newDuration);
	}
}

void TrackEditView::setDuration(Duration duration)
{
	if (mReadOnly == false)
		mCommandHistory->push(new SetLengthCommand(mTabTrack, duration, &mCursorPosition));
}


/**
@brief Set the time to dotted
*/
void TrackEditView::keySemiColon()
{
	if (mReadOnly == false)
		mCommandHistory->push(new DottedNoteCommand(mTabTrack, &mCursorPosition));
}


/**
@brief Set the fret ???
@param num
*/
void TrackEditView::keyNumber(uchar num)
{
	PRECONDITION( num <= 9 );
	
	uint oldFret = 0;
	
	if (mReadOnly == false) {
		if (mTabTrack->isEmptyNote(mCursorPosition.position()) == false)
			oldFret = mTabTrack->fret(mCursorPosition.position());
		
		if (num + 10 * oldFret <= mTabTrack->nbFrets())
			mCommandHistory->push(new InsertTabCommand(mTabTrack, num, &mCursorPosition));
	}
}



QPointCm TrackEditView::transformsXY(const QPoint& point) const
{
	QPointF result = mapToScene(point);
	
	return QPointCm(Pixel(result.x(), sDpi).toCm(), Pixel(result.y(), sDpi).toCm());
}


void TrackEditView::mousePressEvent(QMouseEvent* e)
{
	//qDebug() << "ici\n";
	//TrackPos mousePos;
	mStartSelection = true;
	
	mSelection.setLeft(mapToScene(e->pos()).x());
	mSelection.setBottom(mapToScene(e->pos()).y());
	
	mGraphicsSelection.show();
	
	//qDebug() << "Mouse Click Position : " << mCursorPosition.bar() << " " << mCursorPosition.times() << " " << mCursorPosition.chord() << "\n";
	mCursorPosition.clone(mTrackPageList->cursorPositionAt(transformsXY(e->pos())));
	//qDebug() << "Mouse Click Position : " << mousePos.bar() << " " << mousePos.times() << " " << mousePos.chord() << "\n";
	//mousePos.clone(mTrackPageList->cursorPositionAt(transformsXY(e->pos())));
	//qDebug() << "Mouse Click Position Result : " << mousePos.bar() << " " << mousePos.times() << " " << mousePos.chord() << "\n";
	
	mBeginSelection = mCursorPosition.position();
	//mBeginSelection = mousePos;
}

void TrackEditView::mouseMoveEvent(QMouseEvent* e)
{
	if (mStartSelection == true) {
		mSelection.setRight(mapToScene(e->pos()).x());
		mSelection.setTop(mapToScene(e->pos()).y());
		
		mGraphicsSelection.setRect(mSelection);
	}
}

void TrackEditView::mouseReleaseEvent(QMouseEvent* e)
{
	mStartSelection = false;
	
	mSelection.setRight(mapToScene(e->pos()).x());
	mSelection.setTop(mapToScene(e->pos()).y());
	
	mGraphicsSelection.hide();
	
	mEndSelection = mTrackPageList->cursorPositionAt(transformsXY(e->pos()));
	
	if (mBeginSelection > mEndSelection) {
		TrackPos temp;
		
		temp = mBeginSelection;
		mBeginSelection = mEndSelection;
		mEndSelection = temp;
	}
}

void TrackEditView::paintEvent(QPaintEvent* event)
{
//TODO: following commented-out code buggy! it causes a BIG increase in CPU(here: about 50%)!
// 	QPointCm point = transformsXY(event->rect().topLeft());
// 	int firstPage = static_cast<int>((point.y() / sPageSize.height()).value());
// 	
// 	if (mTrackPageList) {
// 		if (firstPage == 1)
// 			mTrackPageList->draw(firstPage);
// 		else
// 			mTrackPageList->drawRange(firstPage - 1, firstPage);
// 		
// 		mCanvas->update();
// 	}
// 	
	QGraphicsView::paintEvent(event);
	//QPointCm point = transformsXY(cx, cy);
	//int firstPage = static_cast<int>((point.y() / sPageSize.height()).value());
// 	int firstPage = static_cast<int>((Pixel(cy, sDpi).toCm() / sPageSize.height()).value());
	
	/*if (mTrackPageList)
		if (firstPage == 1)
			mTrackPageList->draw(firstPage);
		else
			mTrackPageList->drawRange(firstPage - 1, firstPage);
	
	mCanvas->update();*/
	
//	QGraphicsView::drawContents(p, cx, cy, cw, ch);
}


/**
@brief Get the bar height
*/
Cm TrackEditView::barHeight() const
{
	TrackBarView barView(1, mTabTrack->bar(0), mTabTrack->properties(), mTabTrack->nbStrings(), sDpi);
	barView.displayPartition(mViewScore);
	barView.displayTablature(mViewTablature);
	return barView.height();
}

void TrackEditView::clearView()
{
	PRECONDITION( mTrackPageList != 0x0 );
	delete mTrackPageList;
	initPageViewer();
	if (mSongProperties != 0x0)
		mTrackPageList->setSongProperties(mSongProperties);
	
	if (mTabTrack != 0x0)
		mTrackPageList->setTabTrack(mTabTrack->tabTrack());
}

TrackPageView* TrackEditView::allocAndInitPage(uint pageNumber)
{
	TrackPageView* trackPage = new TrackPageView(sDpi);

	trackPage->setGraphicsScene(mCanvas);
	trackPage->setSongProperties(mSongProperties);
	trackPage->setTabTrack(mTabTrack->tabTrack());
	trackPage->setPageSize(sPageSize);
	trackPage->setLeftMargins(sMargins);
	trackPage->setTopMargins(sMargins);
	trackPage->setPageNumber(pageNumber);
	trackPage->setPosition(QPointCm(Cm(0.0, sDpi), sPageSize.height() * (pageNumber - 1)));
	return trackPage;
}

TrackPageView* TrackEditView::createFirstPage(QRectCm* position)
{
	TrackPageView* trackPage = allocAndInitPage(1);
	
	trackPage->displayTitle(true);
	trackPage->displayWordsAndMusic(true);
	trackPage->displayTuning(true);
	position->setX(sMargins);
	position->setY(trackPage->headersHeight());
	mTrackPageList->push_back(trackPage);
	return trackPage;
}

TrackPageView* TrackEditView::createPage(uint pageNumber, QRectCm* position)
{
	TrackPageView* trackPage = allocAndInitPage(pageNumber);
	
	mTrackPageList->push_back(trackPage);
	position->setY((sPageSize.height() * (pageNumber - 1)) + sMargins + Cm(0.5, sDpi) + trackPage->headersHeight());
	return trackPage;
}

TrackSystemView* TrackEditView::createSystem(const QPointCm& position, QList<TrackBarView*>& system)
{
	TrackSystemView* trackSystem = new TrackSystemView(sDpi);

	trackSystem->setGraphicsScene(mCanvas);
	trackSystem->setPosition(position);
	trackSystem->setSystem(system);
	return trackSystem;
}

TrackBarView* TrackEditView::barViewCreate(uint barNumber, uint firstBar)
{
	TrackBarView* barView = new TrackBarView(barNumber + 1, mTabTrack->bar(barNumber), mTabTrack->properties(), mTabTrack->nbStrings(), sDpi);
	
	barView->setGraphicsScene(mCanvas);
	barView->displayPartition(mViewScore);
	
	if (firstBar == barNumber)
		barView->displayPartitionClef(true);

	barView->displayTablature(true);
	barView->displayBarNumber(true);
	barView->setDrawEffects(true);
	
	if (barNumber == 0)
		barView->displayTablatureClef(true, mClefFont);
	else
		barView->displayTablatureClef(false, mClefFont);
	
	if (firstBar == barNumber || barNumber == 0)
		barView->displayClef(true);
	
	if (barNumber == 0 || mTabTrack->bar(barNumber - 1)->key() != mTabTrack->bar(barNumber)->key())
		barView->displayArmor(true);
	
	if (barNumber == 0)
		barView->displayTimeSignature(true, mTimeFont);
	else {
		if (mTabTrack->areTimesSignaturesEquals(barNumber - 1, barNumber) == true)
			barView->displayTimeSignature(true, mTimeFont);
		else
			barView->displayTimeSignature(false, mTimeFont);
	} 
	
	if (barNumber == 0 || mTabTrack->bar(barNumber - 1)->tempo() != mTabTrack->bar(barNumber)->tempo())
		barView->displayTempo(true);
	
	return barView;
}

bool TrackEditView::appendIfBarIsGreater(TrackPageView* trackPage, QList<TrackBarView*>* barList, TrackBarView* barView)
{
	bool greaterBar = false;
	
	if (barView->minimalWidth() > trackPage->pageWidth() && barList->size() == 0) {
		barView->setWidth(trackPage->pageWidth());
		
		barList->push_back(barView);
		
		greaterBar = true;
	}
	
	return greaterBar;
}

bool TrackEditView::appendSystemOnPage(QRectCm* position, TrackPageView* trackPage, QList<TrackBarView*>* barList, TrackBarView* barView, uint barNumber, Cm* width, bool greaterBar)
{
	const Cm systemWidth = sPageSize.width() - sMargins * 2;
	bool isLastBar = mTabTrack->isLastBar(barNumber);
	bool addLastBar = false;
	Cm newWidth(0, sDpi);
	
	if (isLastBar == true && ((barList->size() == 0) || (barView->minimalWidth() + (*width) < systemWidth))) {
		barList->push_back(barView);
		(*width)-=barView->minimalWidth();
		
		addLastBar = true;
	}
	
	if (isLastBar == false || (isLastBar == true && addLastBar == false))
		newWidth = (systemWidth - (*width)) / barList->size();
	
	if (greaterBar)
		newWidth = Cm(0, sDpi);
	
	TrackSystemView* system = createSystem(QPointCm(sMargins, position->y()), *barList);
	system->setHeight(barView->height());
	system->setWidth(systemWidth);
	system->lastSystem(addLastBar);
	trackPage->addSystem(system);
	
	position->setX(sMargins);
	position->setY(position->y() + mBarHeight);
	
	barList->clear();
	
	if (addLastBar == false && ((isLastBar == false && greaterBar == false) || (isLastBar == true && greaterBar == false)))
		delete barView;
	
	return addLastBar;
}

void TrackEditView::refreshView()
{
	TrackPageView* trackPage = 0;
	uint pageNumber = 1;
	Cm width(0, sDpi);
	uint firstBar = 0;
	QList<TrackBarView*> barList;
	QList<TrackSystemView*> systemList;
	bool greaterBar = false;
	bool addLastBar = false;
	QRectCm position;
	mBarHeight = barHeight();
	
	PROFILE_METHOD_BEGIN( pr_refresh );
	clearView();
	
	trackPage = createFirstPage(&position);
	
	for (uint i = 0; i < mTabTrack->size(); i++) {
		TrackBarView* barView = barViewCreate(i, firstBar);
		
		width = width + barView->minimalWidth();
		
		greaterBar = appendIfBarIsGreater(trackPage, &barList, barView);
		
		if (width > trackPage->pageWidth() || mTabTrack->isLastBar(i + 1)) {
			if (position.y() + barView->height() > ((sPageSize.height() * pageNumber) - sMargins)) {
				pageNumber++;
				trackPage = createPage(pageNumber, &position);
			}
			
			addLastBar = appendSystemOnPage(&position, trackPage, &barList, barView, i + 1, &width, greaterBar);
			
			width = Cm(0, sDpi);
			firstBar = i;
			
			if (mTabTrack->isLastBar(i + 1) == false || (mTabTrack->isLastBar(i + 1) == true && !addLastBar))
				i--;
			
			if (greaterBar) {
				i++;
				
				greaterBar = false;
			}
			
		} else
			barList.push_back(barView);
	}
	
	PROFILE_METHOD_END( pr_refresh );
	PROFILE_METHOD_PRINT( pr_refresh, "theMethod" );
	
	//mTrackPageList->draw();
	mTrackPageList->draw(1);
	setSceneRect(sceneRect().x(), sceneRect().y(), sceneRect().width(), sPageSize.heightValueOfPixel() * mTrackPageList->size());
	
	qDebug() << "mTrackPageList : " << mTrackPageList->size() << "\n";
	
	//refreshCursor();
	
	mCanvas->update();

}

void TrackEditView::refreshPageAt(const TrackPos& position)
{
	mTrackPageList->refreshPageAt(position);
	mCanvas->update();
}

void TrackEditView::updateSystemAt(const TrackPos& position)
{
}

void TrackEditView::updateBarAt(const TrackPos& position)
{
	mTrackPageList->refreshBarAt(position);
}

void TrackEditView::refreshCursor()
{
	if (const TrackBarView* barView = mTrackPageList->barAt(mCursorPosition.position())) {
		mCursorView->setPosition(barView->positionTablature(mCursorPosition.position()));
		mCursorView->refresh();
		centerOn(barView->positionTablature(mCursorPosition.position()).toQPointF());
		mCanvas->update();
	}
}

void TrackEditView::connectTrack() const
{
/*	connect(mTabTrack, SIGNAL(noteEffectChanged(int, int, int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(noteChanged(int, int, int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(timeChordChanged(int, int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(timeEffectChanged(int, int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(timeChanged(int, int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(barPropertyChanged(int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(barMixChanged(int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(barChanged(int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(trackPropertyChanged()), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(trackChanged()), this, SLOT(refreshView()));*/
}

void TrackEditView::disconnectTrack() const
{
	/*connect(mTabTrack, SIGNAL(noteEffectChanged(int, int, int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(noteChanged(int, int, int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(timeChordChanged(int, int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(timeEffectChanged(int, int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(timeChanged(int, int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(barPropertyChanged(int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(barMixChanged(int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(barChanged(int)), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(trackPropertyChanged()), this, SLOT(refreshView()));
	connect(mTabTrack, SIGNAL(trackChanged()), this, SLOT(refreshView()));*/
}

void TrackEditView::noteEffectChanged(int bar, int time, int note)
{
	qDebug() << "[TrackEditView::noteEffectChanged] " << bar << " " << time << " " << note << "\n";
	TrackPos position(bar, time, note);
	mTrackPageList->refreshNoteAt(position);
	mCanvas->update();
}

void TrackEditView::noteChanged(int bar, int time, int note)
{
	qDebug() << "[TrackEditView::noteChanged] " << bar << " " << time << " " << note << "\n";
	TrackPos position(bar, time, note);
	mTrackPageList->refreshNoteAt(position);
	mCanvas->update();
}

void TrackEditView::timeChordChanged(int bar, int time)
{
	qDebug() << "[TrackEditView::timeChordChanged]\n";
	mTrackPageList->refreshTimeAt(bar, time);
	mCanvas->update();
}

void TrackEditView::timeEffectChanged(int bar, int time)
{
	qDebug() << "[TrackEditView::timeEffectChanged]\n";
	mTrackPageList->refreshTimeAt(bar, time);
	mCanvas->update();
}

void TrackEditView::timeChanged(int bar, int time)
{
	qDebug() << "[TrackEditView::timeChanged]\n";
	mTrackPageList->refreshTimeAt(bar, time);
	mCanvas->update();
}

void TrackEditView::barPropertyChanged(int bar)
{
	//qDebug() << "[TrackEditView::timeChanged]\n";
	refreshView();
}

void TrackEditView::barMixChanged(int bar)
{
	//qDebug() << "[TrackEditView::timeChanged]\n";
	refreshView();
}

void TrackEditView::barChanged(int bar)
{
	//qDebug() << "[TrackEditView::timeChanged]\n";
	refreshView();
}

void TrackEditView::trackPropertyChanged()
{
	//qDebug() << "[TrackEditView::timeChanged]\n";
	refreshView();
}

void TrackEditView::trackChanged()
{
	//qDebug() << "[TrackEditView::timeChanged]\n";
	refreshView();
}

void TrackEditView::positionUpdated()
{
	refreshCursor();
}

#include "trackeditview.moc"
