/***************************************************************************
 * tracktimesclassicalview.cpp: implementation of TrackTimesClassicalView 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 "tracktimesclassicalview.h"

#include <QFontMetrics>

#include <limits>

#include "tablature/tabnote.h"
#include "tablature/tabtimes.h"
#include "trackclassicaldurationview.h"

#include "qcanvascm/qcanvasellipsecm.h"
#include "qcanvascm/qcanvaslinecm.h"
#include "qcanvascm/qcanvastextcm.h"

const QFont TrackTimesClassicalView::sClassicalFont("feta26", 26);

TrackTimesClassicalView::TrackTimesClassicalView(const TabTime* time, const TrackProperties* properties, const QFont& textFont, uint strings, const Cm& lSpace, const Cm& tSpace, uint dpi)
: DrawingItem(dpi),
  mTabTime(time),
  trackProperties(properties),
  font(textFont),
  nbStrings(strings),
  lineSpaces(lSpace),
  timeSpaces(tSpace),
  durationView(0),
  heightNote(std::numeric_limits<double>::max(), dpi),
  lowNote(std::numeric_limits<double>::min(), dpi),
  itemCollection(),
  mNextDuration(NoneDuration)
{
}

TrackTimesClassicalView::~TrackTimesClassicalView()
{
}

void TrackTimesClassicalView::setNextDuration(Duration nextDuration)
{
	mNextDuration = nextDuration;
}

Duration TrackTimesClassicalView::nextDuration() const
{
	return mNextDuration;
}

int TrackTimesClassicalView::timeHeight(TabClef clef, uint string)
{
	// At this time it's only working for the C Key
	//
	int octave = (trackProperties->tune(nbStrings - string - 1) + mTabTime->string(string)->fret()) / 12;
	uint realNote = (trackProperties->tune(nbStrings - string - 1) + mTabTime->string(string)->fret()) % 12;
	float note;
	int result;
	
	// For the c tonality
	//
	switch (realNote) {
		case 0:
		case 1:
			note = 0.0;
			break;
		case 2:
		case 3:
			note = 0.5;
			break;
		case 4:
			note = 1.0;
			break;
		case 5:
		case 6:
			note = 1.5;
			break;
		case 7:
		case 8:
			note = 2.0;
			break;
		case 9:
		case 10:
			note = 2.5;
			break;
		case 11:
			note = 3.0;
			break;
	}
	
//	if (clef == TrebleClef)
//		result = static_cast<int>(position().y() - (((octave - 4.0) * 3.5) * lineSpaces) 
//					- (note * lineSpaces) + lineSpaces);
//	else if (clef == BassClef)
//		result = static_cast<int>(position().y() - (((octave - 4.0) * 3.5) * lineSpaces) 
//					- (note * lineSpaces) + lineSpaces);
	
	return result;
}

void TrackTimesClassicalView::drawFret()
{
	QString string;
	QFontMetrics fm(font);
	QCanvasEllipseCm* ellipseView = 0;
	QCanvasLineCm* line = 0;
	Cm result(0, dpi());
	int octave;
	uint realNote;
	float note;
	int lineOctave;
	float lineNote;
	
	for (unsigned int i = 0; i < nbStrings; i++) {
		if ((!mTabTime->isNoteEmpty(i)) && (!mTabTime->isDeadNote(i) == true)) {
			// At this time it's only working for the C Key
			//
			octave = (trackProperties->tune(nbStrings - i - 1) + mTabTime->fret(i)) / 12;
			realNote = (trackProperties->tune(nbStrings - i - 1) + mTabTime->fret(i)) % 12;
			
			// For the c tonality
			//
			switch (realNote) {
				case 0:
				case 1:
					note = 0.0;
					break;
				case 2:
				case 3:
					note = 0.5;
					break;
				case 4:
					note = 1.0;
					break;
				case 5:
				case 6:
					note = 1.5;
					break;
				case 7:
				case 8:
					note = 2.0;
					break;
				case 9:
				case 10:
					note = 2.5;
					break;
				case 11:
					note = 3.0;
					break;
			}
			
			result = position().y() - (lineSpaces * ((octave - 4.0) * 3.5)) - (lineSpaces * note) + lineSpaces;
			
			ellipseView = new QCanvasEllipseCm(position().x(), result, Cm(0.14, dpi()), Cm(0.14, dpi()));
			itemCollection.push_front(ellipseView);
			
			ellipseView->setBrush(Qt::black);
			ellipseView->moveBy(position().x(), result);
			ellipseView->show();
			
			// Height note
			//
			if (result < heightNote)
				heightNote = result;
			
			// Lower note
			//
			if (result > lowNote)
				lowNote = result;
			
			// Drawn partition lines under E
			//
			if (octave <= 4) {
				lineOctave = 4;
				lineNote = 0.0;
				
				while (lineOctave > octave || (lineOctave == octave && lineNote >= note)) {
					line = new QCanvasLineCm();
					itemCollection.push_front(line);
					
					Cm linePosition = (lineSpaces * ((lineOctave - 4) * 3.5)) - (lineSpaces * lineNote) + lineSpaces;
					line->setPoints(position().x() - Cm(0.2, dpi()), position().y() - linePosition,
									position().x() + Cm(0.2, dpi()), position().y() - linePosition);
					line->show();
					
					lineNote--;
					
					if (lineNote < 0.0) {
						lineOctave--;
						lineNote = 3.5 + lineNote;
					}
				}
			} else if (octave >= 5){
				// Drawn partition lines above G
				//
				lineOctave = 5;
				lineNote = 2.5;
				
				while (lineOctave < octave || (lineOctave == octave && lineNote <= note)) {
					line = new QCanvasLineCm();
					itemCollection.push_front(line);
					
					Cm linePosition = (lineSpaces * ((lineOctave - 4) * 3.5)) - (lineSpaces * lineNote) + lineSpaces;
					line->setPoints(position().x() - Cm(0.2, dpi()), position().y() - linePosition,
									position().x() + Cm(0.2, dpi()), position().y() - linePosition);
					line->show();
					
					lineNote++;
					
					if (lineNote > 3.0) {
						lineOctave++;
						lineNote = -3.5 + lineNote;
					}
				}
			}
		}
	}
}

void TrackTimesClassicalView::hide()
{
	if (durationView && mTabTime->isRest() == false)
		durationView->hide();
	
	for (iterator it = itemCollection.begin(); it != itemCollection.end(); ++it)
		(*it)->hide();
	
	durationView = 0;
	itemCollection.clear();
}

void TrackTimesClassicalView::show()
{
	if (mTabTime->isRest()) {
		QCanvasTextCm* canvasText;
		QString string;
		
		switch (mTabTime->duration()) {
		case NoneDuration:
			break;
		case HundredTwentyEighth:
			string = QChar(46);
			break;
		case SixtyFourth:
			string = QChar(45);
			break;
		case ThirthSecond:  // 1/32
			string = QChar(44);
			break;
		case Sixteenth:  // 1/16
			string = QChar(43);
			break;
		case Eighth:  // 1/8
			string = QChar(42);
			break;
		case Quarter: // 1/4
			string = QChar(40);
			break;
		case Half: // 1/2
			string = QChar(34);
			break;
		case Whole: // whole
			string = QChar(33);
			break;
		}
		
		canvasText = new QCanvasTextCm(string);
		itemCollection.push_front(canvasText);
		
		canvasText->moveBy(position().x(), position().y() - lineSpaces * 7);
		canvasText->show();
	} else {
		drawFret();
		
		if (heightNote.value() == std::numeric_limits<double>::max())
			return ;
		
		durationView = new TrackClassicalDurationView(mTabTime, dpi());
		
		durationView->setBarHeight(position().y() - lineSpaces * 7);
		durationView->setTimeSpace(timeSpaces);
		
		QPointCm point(position());
		
		point.setY(lowNote);
		point.setX(position().x() + Cm(0.14, dpi()) / 2);
		
		durationView->setPosition(point);
		durationView->setNextDuration(mNextDuration);
		durationView->show();
	}
}

QRectCm TrackTimesClassicalView::boundingRect () const
{
	return QRectCm(position().x(), position().y(), width(), height());
}

Cm TrackTimesClassicalView::height() const
{
	return Cm(0, dpi());
}

Cm TrackTimesClassicalView::width() const
{
	return timeSpaces;
}
