/***************************************************************************
 * fretboard.cpp: implementation of FretBoard class
 *
 * This file is part of KTabEdit, a KDE tabulature editor
 *
 * copyright (C) 2003-2006 the KTabEdit development team <mrgwen@gmail.com>
 * copyright (C) 2002-2003 the KGuitar 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 "fretboard.h"

#include <QPainter>

#include <KStandardDirs>

#include "util/assert.h"

#include "tablature/tabbeateffects.h"
#include "tablature/tabchord.h"
#include "tablature/tabduration.h"

#include "tablature/tabbend.h"
#include "tablature/tabdynamic.h"

#include "tablature/tabgracenotetransition.h"
#include "tablature/tabgracenote.h"

#include "tablature/tabharmonic.h"
#include "tablature/tabslide.h"
#include "tablature/tabtrill.h"
#include "tablature/tabnoteeffect.h"

#include "tablature/tabnote.h"

#include "tablature/songkey.h"
#include "tablature/timesignature.h"

#include "tablature/tabmixtablechanges.h"
#include "tablature/tabtimes.h"
#include "tablature/tabbarproperties.h"

#include "tablature/tabbar.h"

#include "tablature/trackmidiproperties.h"
#include "tablature/trackproperties.h"

#include "tablature/tabtrack.h"

#include "track/trackview.h"
#include "settings.h"

const uchar Fretboard::sStringHeight(24);
const double Fretboard::sFretDivisor(1.05946);
const uchar Fretboard::sZeroFretWidth(24);
const uchar Fretboard::sInlayRadius(7);
const uchar Fretboard::sFingerRadius(8);
const uchar Fretboard::sSideBorder(2);
const uchar Fretboard::sScaleBorder(5);
const uchar Fretboard::sScaleRound(99);


const QColor Fretboard::sInlayFillColor(205, 214, 221);
const QColor Fretboard::sFretColor1(144, 151, 166);
const QColor Fretboard::sFretColor2(77,  84,  99);
const QColor Fretboard::sStringColor1(230, 230, 230);
const QColor Fretboard::sStringColor2 (166, 166, 166);
const QColor Fretboard::sFingerColor(44,  77, 240);

/**
@brief Inlay marks array
*/
// =================  0  1  2  3  4  5  6  7  8  9 10 11 12
const int marks[] = { 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 2,
// ================= 13 14 15 16 17 18 19 20 21 22 23 24
                      0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 2 };


const int steptemplate[][12] = {
// == C  C# D  D# E  F  F# G  G# A  A# B
	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Nothing
	{ 1, 0, 2, 0, 3, 0, 0, 5, 0, 0, 0, 7 }, // C Pentatonic
	{ 1, 0, 2, 0, 3, 4, 0, 5, 0, 6, 0, 7 }, // C Natural Major
	{ 1, 0, 2, 3, 0, 4, 0, 5, 6, 0, 7, 0 }, // C Natural Minor
	{ 1, 0, 2, 0, 3, 4, 0, 5, 6, 0, 0, 7 }, // C Harmonic Major (major 6-)
	{ 1, 0, 2, 3, 0, 4, 0, 5, 6, 0, 0, 7 }, // C Harmonic Minor (minor 7+)
	{ 1, 0, 2, 0, 3, 4, 0, 5, 0, 6, 0, 7 }, // C Melodic Major
	{ 1, 0, 2, 0, 3, 4, 0, 5, 0, 6, 0, 7 }, // C Melodic Minor // GREYFIX
	{ 1, 0, 2, 0, 3, 4, 0, 5, 0, 6, 7, 0 }, // C Mixolydian     (major 7-)
	{ 1, 0, 2, 0, 3, 0, 4, 5, 0, 6, 0, 7 }, // C Lydian         (major 4+)
	{ 1, 0, 2, 3, 0, 4, 0, 5, 0, 6, 7, 0 }, // C Dorian         (minor 6+)
	{ 1, 2, 0, 3, 0, 4, 0, 5, 6, 0, 7, 0 }, // C Phrygian       (minor 2-)
	{ 1, 0, 2, 0, 3, 4, 0, 5, 0, 6, 0, 7 }, // C Locrian       // GREYFIX
};

Fretboard::Fretboard(const TrackProperties* trackProperties, QWidget* parent)
: QWidget(parent),
  mTrackProperties(trackProperties),
  mTabTime(0x0),
  mCalculateSizes(sMaxFrets + 1),
  mWood(0x0),
  mFret(0x0),
  mZeroFret(0x0),
  mTonic(0),
  mMode(0)
{
	mWood = new QPixmap(KStandardDirs::locate("data", "ktabedit/pics/rosewood.jpg"));
	mFret = new QImage(KStandardDirs::locate("data", "ktabedit/pics/fret.png"));
	mZeroFret = new QImage(KStandardDirs::locate("data", "ktabedit/pics/zerofret.png"));
	
	//setFocusPolicy(Qt::WheelFocus);
	setTrackProperties(trackProperties);
}

Fretboard::~Fretboard()
{
	delete mWood;
	delete mFret;
	delete mZeroFret;
}

/*QSizePolicy Fretboard::sizePolicy()
{
	return QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed);
}*/

void Fretboard::setTrackProperties(const TrackProperties* trackProperties)
{
	PRECONDITION( trackProperties != 0x0 );
	
	mTrackProperties = trackProperties;
	setFixedHeight(mTrackProperties->nbStrings() * sStringHeight);
	recalculateSizes();
}

void Fretboard::setTabTime(const TabTime* tabTime)
{
	PRECONDITION( tabTime != 0x0 );
	
	mTabTime = tabTime;
}

void Fretboard::paintEvent(QPaintEvent *)
{
	QPainter painter;
	
	recalculateSizes();
	painter.begin(this);
	painter.setBrush(QBrush(sFingerColor));
	drawBackground(&painter);
	if (mMode != 0)
		drawScaleBack(&painter);
	
	if (mTabTime != 0x0)
		drawTime(&painter);
	painter.end();
}

/*void Fretboard::mousePressEvent(QMouseEvent *e)
{
	handleMouse(e);
}

void Fretboard::mouseMoveEvent(QMouseEvent *e)
{
	handleMouse(e);
}

void Fretboard::handleMouse(QMouseEvent *e)
{
	int y = mTrackProperties->nbStrings() - (e->y() / sStringHeight) - 1;
	int x = 0;
	if (e->x() > mCalculateSizes[0]) {
		for (int i = 1; i <= mTrackProperties->nbFrets(); i++) {
			if (e->x() <= mCalculateSizes[i]) {
				x = i;
				break;
			}
		}
	}
	emit buttonPress(y, x, e->button());
}

void Fretboard::mouseReleaseEvent(QMouseEvent *e)
{
	emit buttonRelease(e->button());
}

void Fretboard::resizeEvent(QResizeEvent *)
{
	recalculateSizes();
	//drawBackground();
}
*/

void Fretboard::recalculateSizes()
{
	double l = width() - sZeroFretWidth;
	
	for (int i = 0; i <= mTrackProperties->nbFrets(); i++) {
		mCalculateSizes[i] = width() - l;
		l /= sFretDivisor;
	}
	
	l = ((double) width()) / ((double) (width() - l));
	for (int i = 0; i <= mTrackProperties->nbFrets(); i++)
		mCalculateSizes[i] *= l;
}

void Fretboard::drawBackground(QPainter* painter)
{
	painter->drawTiledPixmap(0, 0, width(), height(), *mWood);
	
	QImage scaledFret = mFret->scaleHeight(height());
	*mZeroFret = mZeroFret->scaledToWidth(sZeroFretWidth);
	*mZeroFret = mZeroFret->scaledToHeight(height());
	painter->drawImage(0, 0, *mZeroFret);
	painter->setBrush(QBrush(sInlayFillColor));
	
	for (int i = 1; i <= mTrackProperties->nbFrets(); i++) {
 		painter->setPen(sFretColor1);
 		painter->drawLine((int) mCalculateSizes[i], 0, (int) mCalculateSizes[i], height());
 		painter->setPen(sFretColor2);
 		painter->drawLine((int) mCalculateSizes[i] - 1, 0, (int) mCalculateSizes[i] - 1, height());
 		painter->drawLine((int) mCalculateSizes[i] + 1, 0, (int) mCalculateSizes[i] + 1, height());
		
		//! Draw frets
		painter->drawImage((int) mCalculateSizes[i] - 1, 0, scaledFret);

		//! Draw inlay marks, if applicable
		if (marks[i] == 0)
			continue;
		switch (Settings::getInstance()->melodyEditorInlay())  {
 		case 0: // none
			break;
		case 1: // center dots
			if (marks[i] == 1) {
				painter->drawEllipse((int) ((mCalculateSizes[i - 1] + mCalculateSizes[i]) / 2) - sInlayRadius,
				              height() / 2 - sInlayRadius,
				              sInlayRadius * 2, sInlayRadius * 2);
			} else {
				painter->drawEllipse((int) ((mCalculateSizes[i - 1] + mCalculateSizes[i]) / 2) - sInlayRadius,
				              height() / 3 - sInlayRadius,
				              sInlayRadius * 2, sInlayRadius * 2);
				painter->drawEllipse((int) ((mCalculateSizes[i - 1] + mCalculateSizes[i]) / 2) - sInlayRadius,
				              height() * 2 / 3 - sInlayRadius,
				              sInlayRadius * 2, sInlayRadius * 2);
			}
			break;
		case 2: // side dots
			if (marks[i] == 1) {
				painter->drawEllipse((int) ((mCalculateSizes[i - 1] + mCalculateSizes[i]) / 2) - sInlayRadius,
				              height() - 2 * sInlayRadius - sSideBorder,
				              sInlayRadius * 2, sInlayRadius * 2);
			} else {
				painter->drawEllipse((int) ((mCalculateSizes[i - 1] + mCalculateSizes[i]) / 2) - sInlayRadius,
				              height() - 2 * sInlayRadius - sSideBorder,
				              sInlayRadius * 2, sInlayRadius * 2);
				painter->drawEllipse((int) ((mCalculateSizes[i - 1] + mCalculateSizes[i]) / 2) - sInlayRadius,
				              height() - 4 * sInlayRadius - 2 * sSideBorder,
				              sInlayRadius * 2, sInlayRadius * 2);
			}
			break;
		case 3: // blocks
			int h = height() * ((marks[i] == 1) ? 7 : 9) / 10;
			painter->drawRect((int) ((4 * mCalculateSizes[i - 1] + mCalculateSizes[i]) / 5),
			           (height() - h) / 2, (int) (3 * (mCalculateSizes[i] - mCalculateSizes[i - 1]) / 5), h);
			break;
		}
	}
	
	for (int i = 0; i < mTrackProperties->nbStrings(); i++) {
		int y = i * sStringHeight + sStringHeight / 2;
		painter->setPen(sStringColor1);
		painter->drawLine(0, y, width(), y);
		painter->setPen(sStringColor2);
		painter->drawLine(0, y - 1, width(), y - 1);
		painter->drawLine(0, y + 1, width(), y + 1);
	}
}

void Fretboard::drawScaleBack(QPainter* painter)
{
	QVector<int> step(12);
	int now, i;
	
	now = mTonic;
	for (i = 0; i < 12; i++) {
		step[now] = steptemplate[mMode][i];
		now = (now + 1) % 12;
	}
	
	// Mark notes in scale, as designated by step[] array
	int y = height() - sStringHeight + sScaleBorder;
	
	for (i = 0; i < mTrackProperties->nbStrings(); i++) {
		now = mTrackProperties->tune(i) % 12;
		for (int j = 0; j < mTrackProperties->nbFrets(); j++) {
			if (step[now]) {
				painter->setBrush(QBrush(QColor(239, 207, 0)));
				int x = (j == 0) ? sScaleBorder : (int) (mCalculateSizes[j - 1] + sScaleBorder);
				painter->drawRoundRect(x, y, (int) (mCalculateSizes[j] - x - sScaleBorder),
				                sStringHeight - 2 * sScaleBorder,
				                sScaleRound, sScaleRound);
			}
			now = (now + 1) % 12;
		}
		y -= sStringHeight;
	}
}

void Fretboard::drawTime(QPainter* painter)
{
	int y = height() - sStringHeight / 2 - sFingerRadius;
	
	for (int i = 0; i < mTrackProperties->nbStrings(); i++) {
		if ((mTabTime->isNoteEmpty(i) == false) && (mTabTime->fret(i) <= mTrackProperties->nbFrets())) {
			int x = (mTabTime->fret(i) == 0) ? (int) mCalculateSizes[0] / 2 : (int) (mCalculateSizes[mTabTime->fret(i)] + mCalculateSizes[mTabTime->fret(i) - 1]) / 2;
			painter->drawEllipse(x - sFingerRadius, y, sFingerRadius * 2, sFingerRadius * 2);
		}
		y -= sStringHeight;
	}
}

void Fretboard::setTonic(int tonic)
{
	mTonic = tonic;
	update();
}

void Fretboard::setMode(int mode)
{
	mMode = mode;
	update();
}

#include "fretboard.moc"
