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

#include <QMouseEvent>
#include <QPainter>
#include <QScrollBar>

#include "tablature/trackmidiproperties.h"
#include "tablature/timesignature.h"
#include "tablature/trackproperties.h"
#include "tablature/tabnote.h"
#include "tablature/tabtimes.h"
#include "tablature/tabbar.h"
#include "tablature/tabtrack.h"

#include "settings.h"

const int Fingering::msScale    = 20;
const int Fingering::msCircle   = 16;
const int Fingering::msCircBord = 2;
const int Fingering::msBorder   = 5;
const int Fingering::msSpacer   = 3;
const int Fingering::msFretText = 10;
const int Fingering::msScroller = 15;
const int Fingering::msNotes    = 20;


/**
@brief Construct the fingering display. The constructor displays an empty chord.
@param track the current TabTrack
@see QFrame
*/
Fingering::Fingering(const TabTrack& track)
: QFrame(),
  mTabTrack(track),
  mLastFret(1)
{
	setFixedSize(mTabTrack.nbStrings() * msScale + 2 * msBorder + msFretText + msScroller,
				 NUMFRETS * msScale + msScale + 2 * msBorder + 2 * msSpacer + msNotes);
	setFrameStyle(Panel | Sunken);
	setBackgroundMode(Qt::PaletteBase);
	
	mScrollBar = new QScrollBar(1, mTabTrack.nbFrets() - NUMFRETS + 1, 1, 5, 1,
						Qt::Vertical, this);
	mScrollBar->setGeometry(width() - msScroller, 0, msScroller, height());
	connect(mScrollBar, SIGNAL(valueChanged(int)), SLOT(setFirstFret(int)));
	
	clear();
}

/**
@brief Destructor
*/
Fingering::~Fingering()
{

}


void Fingering::clear()
{
	for (int i = 0; i < mTabTrack.nbStrings(); i++)
		mChordDiagram[i] = -1;
	repaint();
	emit chordChange();
}


/**
@brief Set a chord played at the n-th string. Display the dot at the fret and chord number and refresh the widget.
@param string string fretted
@param fret the fret number
*/
void Fingering::setFinger(int string, int fret)
{
    if (mChordDiagram[string] != fret) {
        mChordDiagram[string] = fret;
        repaint();
        emit chordChange();
    }
}


/**
@brief Set the chord name
@param name the chord name
*/
void Fingering::setFingering(const int a[MAX_STRINGS])
{
    int f = 24; // GREYFIX
    bool noff = true;

    for (int i = 0; i < mTabTrack.nbStrings(); i++) {
        if ((a[i] < f) && (a[i] > 0))
            f = a[i];
        if (a[i] > 5)
            noff = false;
    }

    if (noff)
        f = 1;

    mScrollBar->setValue(f);

    for (int i = 0;i < MAX_STRINGS; i++)
        mChordDiagram[i] = a[i];
    repaint();
    emit chordChange();
}


void Fingering::setFirstFret(int fret)
{
    for (int i = 0; i < mTabTrack.nbStrings(); i++)
	if (mChordDiagram[i] > 0)
	    mChordDiagram[i] = mChordDiagram[i] - mLastFret + fret;

    mLastFret = fret;

    repaint();
    emit chordChange();
}


void Fingering::mouseHandle(const QPoint &pos, bool domute)
{
    int i = (pos.x() - msBorder - msFretText) / msScale;
    int j = 0;
    if (pos.y() > msBorder + msScale + 2 * msSpacer)
        j = (pos.y() - msBorder - msScale - 2 * msSpacer) / msScale + mScrollBar->value();

    if ((domute) && (mChordDiagram[i] == j))
        j = -1;

    if (!((i < 0) || (i >= mTabTrack.nbStrings()) || (j >= mScrollBar->value() + NUMFRETS)))
        setFinger(i, j);
}


void Fingering::mouseMoveEvent(QMouseEvent *e)
{
    mouseHandle(e->pos(), false);
}


void Fingering::mousePressEvent(QMouseEvent *e)
{
    if (e->button() == Qt::LeftButton)
	mouseHandle(e->pos(), true);
}


void Fingering::paintEvent(QPaintEvent* event)
{
	int barre, eff;
	QPainter painter(this);
	
	// Horizontal separator line
	//
	painter.drawLine(msBorder + msFretText, msBorder + msScale + msSpacer,
	            msBorder + msFretText + mTabTrack.nbStrings() * msScale, msBorder + msScale + msSpacer);

	// Horizontal lines
	//
	for (int i = 0; i <= NUMFRETS; i++)
		painter.drawLine(msScale / 2 + msBorder + msFretText, msBorder + msScale + 2 * msSpacer + i * msScale,
		            msScale / 2 + msBorder + mTabTrack.nbStrings() * msScale - msScale + msFretText,
		            msBorder +msScale + 2 * msSpacer + i * msScale);
	
	// Beginning fret number
	//
	QString tmp;
	tmp.setNum(mScrollBar->value());
	painter.drawText(msBorder - msSpacer, msBorder + msScale + 2 * msSpacer, 50, 50, Qt::AlignLeft | Qt::AlignTop, tmp);
	
	// Vertical lines, fingering and note names
	//
	for (int i = 0; i < mTabTrack.nbStrings(); i++) {
		painter.drawLine( i * msScale + msBorder + msScale / 2 + msFretText, msBorder + msScale + 2 * msSpacer,
		         i * msScale + msBorder + msScale / 2 + msFretText,
		         msBorder + msScale + 2 * msSpacer + NUMFRETS *msScale);
		if (mChordDiagram[i] == -1) {
			painter.drawLine(i * msScale + msBorder + msCircBord + msFretText, msBorder + msCircBord,
			            i * msScale + msBorder + msScale - msCircBord + msFretText, msBorder + msScale - msCircBord);
			painter.drawLine(i * msScale + msBorder + msScale - msCircBord + msFretText, msBorder +msCircBord,
			            i * msScale + msBorder + msCircBord + msFretText, msBorder + msScale - msCircBord);
		} else {
			if (mChordDiagram[i] == 0) {
				painter.setBrush(Qt::NoBrush);
				painter.drawEllipse(i * msScale + msBorder + msCircBord + msFretText,
				               msBorder + msCircBord, msCircle, msCircle);
			} else {
				painter.setBrush(Qt::SolidPattern);
				painter.drawEllipse(i * msScale + msBorder + msCircBord + msFretText,
				               msBorder + msScale + 2 * msSpacer + (mChordDiagram[i] - mScrollBar->value()) * msScale + msCircBord,
				               msCircle, msCircle);
			};
			painter.drawText(msBorder + msFretText + i * msScale, msBorder + NUMFRETS * msScale + msScale + 2 * msSpacer,
			            msScale, 30, Qt::AlignHCenter | Qt::AlignTop,
			            Settings::getInstance()->noteName((mChordDiagram[i] + mTabTrack.tune(i)) % 12));
		}
	}
	
	// Analyze & draw barre
	//
	painter.setBrush(Qt::SolidPattern);
	
	for (int i = 0; i < NUMFRETS; i++) {
		barre = 0;
		while ((mChordDiagram[mTabTrack.nbStrings() - barre - 1] >= (i + mScrollBar->value())) ||
		       (mChordDiagram[mTabTrack.nbStrings() - barre - 1] == -1)) {
			barre++;
			if (barre>mTabTrack.nbStrings() - 1)
				break;
		}

		while ((mChordDiagram[mTabTrack.nbStrings() - barre] != (i + mScrollBar->value())) && (barre > 1))
			barre--;

		eff = 0;
		for (int j = mTabTrack.nbStrings() - barre; j < mTabTrack.nbStrings(); j++) {
			if (mChordDiagram[j] != -1)
				eff++;
		}
		
		if (eff > 2) {
			painter.drawRect((mTabTrack.nbStrings() - barre) * msScale + msScale / 2 + msBorder + msFretText,
			            msBorder + msScale + 2 * msSpacer + i * msScale + msCircBord,
			            (barre - 1) * msScale, msCircle);
		}
	}
}


/**
@brief Returns a fret at the chord number.
@param string The string number
@return The note fretted at the given string
*/
int Fingering::app(int string) const
{
	return mChordDiagram[string];
}


/**
@brief Set the note fretted at the string the number.
@param string the string number
@param fret the fret number
*/
void Fingering::setApp(int string, int fret)
{
	mChordDiagram[string] = fret;
}

#include "fingers.moc"
