/***************************************************************************
 * chord.h: implementation of ChordSelector 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 "chord.h"

#include <QLabel>
#include <QWidget>
#include <QRadioButton>
#include <KPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>

#include <KComboBox>
#include <KLineEdit>
#include <KListWidget>
#include <KLocale>
#include <KMessageBox>
#include <KPushButton>

// #include <tse3/Error.h>
// #include <tse3/listen/Transport.h>
#include <tse3/Metronome.h>
#include <tse3/MidiScheduler.h>
#include <tse3/Part.h>
#include <tse3/PhraseEdit.h>
#include <tse3/Song.h>
// #include <tse3/TempoTrack.h>
#include <tse3/Track.h>
#include <tse3/Transport.h>
// #include <tse3/util/MidiScheduler.h>

#include "chordanalyzer.h"
#include "chordlist.h"
#include "chordlistitem.h"
#include "fingers.h"
#include "fingerlist.h"

#include "settings.h"
#include "strumming.h"

#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"

// Chord definition
const int ChordSelector::msTemplate[][6] =
    // 3  5  7  9 11 13
    //
	{{-1, 2, 0, 0, 0, 0 },   // C
	 {-1, 2, 2, 0, 0, 0 },   // C7
	 {-1, 2, 3, 0, 0, 0 },   // C7M
	 {-1, 2, 1, 0, 0, 0 },   // C6
	 {-1, 2, 2, 2, 0, 0 },   // C9
	 {-1, 2, 2, 2, 2, 0 },   // C11
	 {-1, 2, 2, 2, 2, 2 },   // C13
	 { 3, 3, 0, 0, 0, 0 },   // Caug
	 { 2, 1, 1, 0, 0, 0 },   // Cdim
	 { 0, 2, 0, 0, 0, 0 }};  // C5

ChordSelector::ChordSelector(TSE3::MidiScheduler* midiScheduler, const TabTrack& track)
: KDialog(),
  mTabTrack(track),
  mScheduler(midiScheduler),
  mStrumScheme(0)
{
	setCaption(i18n("Chord Constructor"));
// 	setButtons(0);
	mainWidget = new QWidget(this);
	setMainWidget(mainWidget);
	setButtons(KDialog::Ok | KDialog::Cancel);

	mChordName = new KLineEdit();
	mTonic = new KListWidget();
	mBassNote = new KComboBox();
	mStep3 = new KListWidget();
	mStepHigh = new KListWidget();
	mFingerging = new Fingering(mTabTrack);
	mChords = new ChordList();
	mFingerList = new FingerList(mTabTrack);
	mInv = new KComboBox();

	mComplexity = new QVBoxLayout();
	mComplexer[0] = new QRadioButton(i18n("Usual"));
	mComplexer[1] = new QRadioButton(i18n("Rare"));
	mComplexer[2] = new QRadioButton(i18n("All"));

	// Chord function
	new QListWidgetItem("M",    mStep3);
	new QListWidgetItem("m",    mStep3);
	new QListWidgetItem("sus2", mStep3);
	new QListWidgetItem("sus4", mStep3);

	// Chord extention
	new QListWidgetItem("",    mStepHigh);
	new QListWidgetItem("7",   mStepHigh);
	new QListWidgetItem(Settings::getInstance()->maj7Name(), mStepHigh);
	new QListWidgetItem("6",   mStepHigh);
	new QListWidgetItem("9",   mStepHigh);
	new QListWidgetItem("11",  mStepHigh);
	new QListWidgetItem("13",  mStepHigh);
	new QListWidgetItem("aug", mStepHigh);
	new QListWidgetItem("dim", mStepHigh);
	new QListWidgetItem("5",   mStepHigh);

	// Butons
	buttonAnalyze = new KPushButton(i18n("&Analyze"), this);	
	buttonQuickInsert = new KPushButton(i18n("&Quick Insert"), this);
	buttonStrum = new KPushButton(i18n("&Strum..."), this);
	buttonPlay = new KPushButton(i18n("&Play"), this);
// 	buttonOk = new KPushButton(i18n("Ok"), this);

	initUI();
	connectSignals();
	initLayout();
}


//TODO: do all variables have to be destroyed?
ChordSelector::~ChordSelector()
{
	delete mFingerging;
	delete mChords;
	delete mFingerList;
}


/**
\brief Make the initialisations for the User Interface of this dialog
**/
void ChordSelector::initUI()
{
	// Tonic note
	for (int i = 0; i < 12; i++)
		new QListWidgetItem(Settings::getInstance()->noteName(i), mTonic);
	// Bass note
	for (int i = 0; i < 12; i++)
		mBassNote->insertItem(Settings::getInstance()->noteName(i));
	// Chord function
	mStep3->setMinimumWidth(40);
	// Chord extention
	mStepHigh->setMinimumWidth(40);
	// Chord notes
	QString tmp;
	for (int i = 0; i < 7; i++) {
		tmp.setNum(i * 2 + 1);
		tmp = tmp + "\'";
		stlabel[i] = new QLabel(tmp);
		stlabel[i]->setAlignment(Qt::AlignCenter);
		
		mCnote[i] = new QLabel();
		mCnote[i]->setAlignment(Qt::AlignCenter);
		
		mSt[i] = new KComboBox();
		
		if (i > 0)
			mSt[i]->insertItem("x");
		if ((i == 2) || (i >= 4)) {
			mSt[i]->insertItem(Settings::getInstance()->flatName());
			mSt[i]->insertItem("0");
			mSt[i]->insertItem(Settings::getInstance()->sharpName());
		}		
		if (i > 0)  {
			connect(mSt[i], SIGNAL(activated(int)), SLOT(findSelection()));
			connect(mSt[i], SIGNAL(activated(int)), SLOT(findChords()));
        	} else {
			mSt[i]->insertItem("0");
			mSt[i]->setEnabled(false);
		}
	}
	mSt[1]->insertItem("2");
	mSt[1]->insertItem(Settings::getInstance()->flatName());
	mSt[1]->insertItem("3");
	mSt[1]->insertItem("4");
	mSt[3]->insertItem("6");
	mSt[3]->insertItem(Settings::getInstance()->flatName());
	mSt[3]->insertItem("7");
	// Fingering
	mChords->setMinimumWidth(120);
	// Inverse
	mInv->insertItem(i18n("Root"));
	mInv->insertItem(i18n("Inv #1"));
	mInv->insertItem(i18n("Inv #2"));
	mInv->insertItem(i18n("Inv #3"));
	mInv->insertItem(i18n("Inv #4"));
	mInv->insertItem(i18n("Inv #5"));
	mInv->insertItem(i18n("Inv #6"));
	// Complexity
	for (int i = 0; i < 3; i++)
		mComplexity->addWidget(mComplexer[i]);
	// Buttons
	buttonAnalyze->setAccel(QKeySequence(Qt::SHIFT + Qt::Key_Return));
	buttonQuickInsert->setAccel(QKeySequence(Qt::CTRL + Qt::Key_Return));	
// 	buttonOk->setDefault(true);
// 	buttonCancel = new KPushButton(i18n("Cancel"), this);
}


/**
\brief Connect all the signals to some slots
**/
void ChordSelector::connectSignals()
{
	connect(mTonic, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(findChords()));

	connect(mStep3, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(setStep3()));

	connect(mStepHigh, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(setHighSteps()));

	//TODO: slot!
	connect(mChords, SIGNAL(KListWidget::executed(QListWidgetItem *)), SLOT(setStepsFromChord()));

	connect(mFingerging, SIGNAL(chordChange()), SLOT(detectChord()));
	//TODO: no such slot!

	connect(mFingerList, SIGNAL(chordSelected(const int *)), mFingerging, SLOT(setFingering(const int *)));

	connect(mInv, SIGNAL(activated(int)), SLOT(findChords()));

	connect(mComplexer[0], SIGNAL(clicked()), this, SLOT(findChords()));
	connect(mComplexer[1], SIGNAL(clicked()), this, SLOT(findChords()));
	connect(mComplexer[2], SIGNAL(clicked()), this, SLOT(findChords()));

	connect(buttonAnalyze, SIGNAL(clicked()), SLOT(analyzeChordName()));
	connect(buttonQuickInsert, SIGNAL(clicked()), SLOT(quickInsert()));
	connect(buttonStrum, SIGNAL(clicked()), SLOT(askStrum()));
	connect(buttonPlay, SIGNAL(clicked()), SLOT(playMidi()));
// 	connect(buttonOk, SIGNAL(clicked()), this, SLOT(accept()));
// 	connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject()));
}


/**
\brief Configure the layout
**/
void ChordSelector::initLayout()
{
	QBoxLayout* mainLayout = new QHBoxLayout();
	QBoxLayout* chordFindingLayout = new QVBoxLayout();
	QBoxLayout* chordEditLayout = new QHBoxLayout();
	QGridLayout* chordSelectionLayout = new QGridLayout();
	QBoxLayout* iconShowingLayout = new QVBoxLayout();
	QBoxLayout* chordAnalyzingLayout = new QHBoxLayout();
	QGridLayout* StepsEditorLayout = new QGridLayout();
	QBoxLayout* strummingLayout = new QVBoxLayout();
	
	strummingLayout->addWidget(buttonAnalyze);
	strummingLayout->addWidget(buttonQuickInsert);
	strummingLayout->addStretch(1);
	strummingLayout->addWidget(buttonPlay);
	strummingLayout->addWidget(buttonStrum);
// 	strummingLayout->addWidget(buttonOk);
// 	strummingLayout->addWidget(buttonCancel);
	
	StepsEditorLayout->addRowSpacing(0, 15);
	StepsEditorLayout->addRowSpacing(1, 20);
	StepsEditorLayout->addRowSpacing(2, 15);
	StepsEditorLayout->setColStretch(0, 1);

	for (int i = 0; i < 7; i++) {
		StepsEditorLayout->addWidget(stlabel[i], 0, i);
		StepsEditorLayout->addWidget(mSt[i], 1, i);
		StepsEditorLayout->addWidget(mCnote[i], 2, i);
		StepsEditorLayout->setColStretch(i, 1);
	}
	
	chordAnalyzingLayout->addWidget(mFingerging);
	chordAnalyzingLayout->addWidget(mChords);
	
	iconShowingLayout->addLayout(chordAnalyzingLayout);
	iconShowingLayout->addLayout(StepsEditorLayout);
	
	chordSelectionLayout->addMultiCellWidget(mTonic, 0, 2, 0, 0);
	chordSelectionLayout->addColSpacing(0, 40);
	chordSelectionLayout->addWidget(mStep3, 0, 1);
	chordSelectionLayout->addLayout(mComplexity, 1, 1);
	chordSelectionLayout->addWidget(mInv, 2, 1);
	chordSelectionLayout->addMultiCellWidget(mStepHigh, 0, 1, 2, 2);
	chordSelectionLayout->addWidget(mBassNote, 2, 2);
	
	chordEditLayout->addLayout(chordSelectionLayout);
	chordEditLayout->addLayout(iconShowingLayout);
	
	chordFindingLayout->addWidget(mChordName);
	chordFindingLayout->addLayout(chordEditLayout);
	chordFindingLayout->addWidget(mFingerList, 1);
	
	mainLayout->addLayout(chordFindingLayout, 1);
	mainLayout->addLayout(strummingLayout);
	
	// delete the default layout
	if (layout())
		delete layout();
	mainWidget->setLayout(mainLayout);
}


/**
\brief Pops out the strumming dialog.

TODO What settings does that really change?
*/
void ChordSelector::askStrum()
{
	Strumming strum(mStrumScheme, this);
	if (strum.exec()) {
		mStrumScheme = strum.scheme();
	}
}


/**
\brief Plays a chord
\deprecated Because it uses the TSE3 library
*/
void ChordSelector::playMidi()
{
	const uchar tuning[] =  {40, 45, 50, 55, 59, 64};
	
	if (mScheduler) {
		TSE3::PhraseEdit phraseEdit;
		TSE3::Clock time = 0;

		// Select the instrument
		//
		phraseEdit.insert(TSE3::MidiEvent(
					TSE3::MidiCommand(
						TSE3::MidiCommand_ProgramChange, 
						0, Settings::getInstance()->midiPortNumber(), 26)
					, 0)
				);
		
		phraseEdit.insert(TSE3::MidiEvent(
					TSE3::MidiCommand(
						TSE3::MidiCommand_ControlChange,
						0, Settings::getInstance()->midiPortNumber(), TSE3::MidiControl_ReverbDepth, 0)
					, 0)
		);
		
		phraseEdit.insert(TSE3::MidiEvent(
					TSE3::MidiCommand(
						TSE3::MidiCommand_ControlChange,
						0, Settings::getInstance()->midiPortNumber(), TSE3::MidiControl_PanMSB, 0)
					, 0)
		);
		
		phraseEdit.insert(TSE3::MidiEvent(
					TSE3::MidiCommand(
						TSE3::MidiCommand_ControlChange,
						0, Settings::getInstance()->midiPortNumber(), TSE3::MidiControl_ChorusDepth, 0)
					, 0)
		);
		
		phraseEdit.insert(TSE3::MidiEvent(
					TSE3::MidiCommand(
						TSE3::MidiCommand_ControlChange,
						0, Settings::getInstance()->midiPortNumber(), TSE3::MidiControl_ChannelVolumeMSB, 50)
					, 0)
		);
		
		int note;
		
		// Play every notes
		//
		for (int i = 5; i >= 0; i--) {
			if (mFingerging->app(i) != -1) {
				note = tuning[i] + mFingerging->app(i);
				
				phraseEdit.insert(TSE3::MidiEvent(
					TSE3::MidiCommand(
						TSE3::MidiCommand_NoteOn, 
						0, Settings::getInstance()->midiPortNumber(), note, 0x60)
					, time, 0x60, time + TSE3::Clock::PPQN)
				);
				time+=TSE3::Clock::PPQN;
			}
		}
		
		// Play the scale
		//
		for (int i = 5; i >= 0; i--) {
			if (mFingerging->app(i) != -1) {
				note = tuning[i] + mFingerging->app(i);
				
				phraseEdit.insert(TSE3::MidiEvent(
					TSE3::MidiCommand(
						TSE3::MidiCommand_NoteOn, 
						0, Settings::getInstance()->midiPortNumber(), note, 0x60)
					, time, 0x60, time + TSE3::Clock::PPQN * 2)
				);
			}
		}
		
		time+=TSE3::Clock::PPQN;
		
		phraseEdit.insert(TSE3::MidiEvent(
			TSE3::MidiCommand(
				TSE3::MidiCommand_NoteOn, 
				0, Settings::getInstance()->midiPortNumber(), 0, 0)
			, time, 0, time + TSE3::Clock::PPQN)
		);
		
		// Assemble the song
		//
		TSE3::Song    song(1);
		TSE3::Phrase *phrase = phraseEdit.createPhrase(song.phraseList());
		TSE3::Part   *part   = new TSE3::Part(0, phraseEdit.lastClock());
		part->setPhrase(phrase);
		song[0]->insert(part);
		
		TSE3::Metronome metronome;
		TSE3::Transport transport(&metronome, mScheduler);
		
		// PLay the song
		//
		transport.play(&song, 0);
		do {
			//kapp->processEvents(); // Commented out for compilation. FIXME
			transport.poll();
		} while(transport.status() != TSE3::Transport::Resting);
	}
}


/**
\brief Try to detect some chord forms from a given applicature
*/
void ChordSelector::detectChord()
{
	bool cn[12];
	int i, j, numnotes, noteok; //, bass;
	QString name;
	int s3, s5, s7, s9, s11, s13;

	for (i = 0; i < 12; i++)
		cn[i] = false;
	numnotes=0; // number of different notes in a chord

	for (i = 0; i < mTabTrack.nbStrings(); i++) {
		j = mFingerging->app(i);
		if (j != -1) {
			j = (j + mTabTrack.tune(i)) % 12;
			if (!cn[j]) {
				cn[j] = true;
				numnotes++;
			}
		}
	}
	mChords->clearSelection();
	mChords->clear();
	for (i = 0; i < 12; i++)  if (cn[i]) {

		// Initializing
		s3 = -1; s5 = -1; s7 = -1; s9 = -1; s11 = -1; s13 = -1;
		noteok = numnotes - 1;
		// Detecting thirds
		if (cn[(i + 4) % 12]) {
			s3 = 4; noteok--;			// Major
		} else if (cn[(i + 3) % 12]) {
			s3 = 3; noteok--;			// Minor
		} else if (cn[(i + 5) % 12]) {
			s3 = 5; noteok--;			// Sus4
		} else if (cn[(i + 2) % 12]) {
			s3 = 2; noteok--;			// Sus2
		}
		// Detecting fifths
		if (cn[(i + 7) % 12]) {
			s5 = 7; noteok--;			// 5
		} else if (cn[(i+6) % 12]) {
			s5 = 6; noteok--;			// 5-
		} else if (cn[(i+8) % 12]) {
			s5 = 8; noteok--;			// 5+
		}
		// Detecting sevenths
		if (cn[(i + 10) % 12]) {
			s7 = 10;noteok--;			// 7
		} else if (cn[(i + 11) % 12]) {
			s7 = 11;noteok--;			// 7M
		} else if (cn[(i + 9) % 12]) {
			s7 = 9;noteok--;			// 6
		}
		// Detecting 9ths
		if ((cn[(i + 2) % 12]) && (s3 != 2)) {
			s9 = 2;noteok--;			// 9
		} else if ((cn[(i + 3) % 12]) && (s3 != 3)) {
			s9 = 3;noteok--;			// 9+
		} else if (cn[(i + 1) % 12]) {
			s9 = 1;noteok--;			// 9-
		}
		// Detecting 11ths
		if ((cn[(i+5)%12]) && (s3!=5)) {
			s11=5;noteok--;				  // 11
		} else if ((cn[(i+4)%12]) && (s3!=4)) {
			s11=4;noteok--;				  // 11-
		} else if ((cn[(i+6)%12]) && (s5!=6)) {
			s11=6;noteok--;				  // 11+
		}
		// Detecting 13ths
		if ((cn[(i+9)%12]) && (s7!=9)) {
			s13=9;noteok--;
		} else if ((cn[(i+8)%12]) && (s5!=8)) {
			s13=8;noteok--;
		} else if ((cn[(i+10)%12]) && (s7!=10)) {
			s13=10;noteok--;
		}
		if (noteok == 0) {
			ChordListItem *item = new ChordListItem(i, s3, s5, s7, s9, s11, s13);
			mChords->inSort(item);
		}
	}
	mChords->repaint();
}


void ChordSelector::setStep3()
{
	switch (mStep3->currentRow()) {
		//TODO: setcurrentItem() probably not correct
		case 0: mSt[1]->setCurrentItem("3", false, 3); break;	// Major
		case 1: mSt[1]->setCurrentItem("2", false, 2); break;	// Minor
		case 2: mSt[1]->setCurrentItem("1", false, 1); break;	// Sus2
		case 3: mSt[1]->setCurrentItem("4", false, 4); break;	// Sus4
	}
	findSelection();
	findChords();
}


void ChordSelector::setStepsFromChord()
{
	ChordListItem *it = mChords->currentItemPointer();

	mTonic->setCurrentRow(it->tonic());
	for (int i = 0; i < 6; i++)
		mSt[i + 1]->setCurrentItem("blah", false, it->step(i));

	findSelection();
	findChords();
}


void ChordSelector::setHighSteps()
{
	int j = mStepHigh->currentRow();

	if (j == -1)
		return;

	for (int i = 0; i < 6; i++)
		if (msTemplate[j][i] != -1)
			mSt[i + 1]->setCurrentItem("blah", false, msTemplate[j][i]);

	findSelection();
	findChords();
}


/**
\brief Analyses mSt[] combobox array and finds out steps templating listboxes selections from it
*/
void ChordSelector::findSelection()
{
	bool ok = true;

	switch (mSt[1]->currentItem()) {
	case 0: mStep3->clearSelection(); break;	// no3
	case 1: mStep3->setCurrentRow(2); break;	// Sus2
	case 2: mStep3->setCurrentRow(1); break;	// Minor
	case 3: mStep3->setCurrentRow(0); break;	// Major
	case 4: mStep3->setCurrentRow(3); break;	// Sus4
	}

	for (int j = mStepHigh->count() - 1; j >= 0; j--) {
		ok = true;
		for (int i = 0; i < 6; i++) {
			if ((msTemplate[j][i] != -1) &&
				(msTemplate[j][i] != mSt[i + 1]->currentItem())) {
				ok = false;
				break;
			}
		}
		if (ok) {
			mStepHigh->setCurrentRow(j);
			break;
		}
	}
	if (!ok)
		mStepHigh->clearSelection();
}


/**
\brief Calculate absolute notes from tonic + step user input
*/
bool ChordSelector::calculateNotesFromSteps(int need[], int &notenum)
{
	//                  1  5  7   9  11 13
	int toneshift[6] = {0, 7, 10, 2, 5, 9};
	int t = mTonic->currentRow();
	if (t == -1)                        // no calculations without tonic
		return false;

	notenum = 1;
	need[0] = t;
	mCnote[0]->setText(Settings::getInstance()->noteName(t));

	switch (mSt[1]->currentItem()) {
	case 1: need[1] = (t + 2) % 12; notenum++; break; // 2
	case 2: need[1] = (t + 3) % 12; notenum++; break; // 3-
	case 3: need[1] = (t + 4) % 12; notenum++; break; // 3+
	case 4: need[1] = (t + 5) % 12; notenum++; break; // 4
	}

	if (mSt[1]->currentItem()!=0) {
		mCnote[1]->setText(Settings::getInstance()->noteName(need[1]));
	} else {
		mCnote[1]->clear();
	}

	for (int i = 1; i < 6; i++) {
		int j = mSt[i + 1]->currentItem();
		if (j) {
			need[notenum] = (t + toneshift[i] + (j - 2)) % 12;
			mCnote[i + 1]->setText(Settings::getInstance()->noteName(need[notenum]));
			notenum++;
		} else {
			mCnote[i + 1]->clear();
		}
	}
	return true;
}


/** 
\brief Finds chords...
Most complex and longest method that does the real calculation of chords. Translates steps settings input to the list of chords and adds them to displayer
*/
void ChordSelector::findChords()
{
	int i, j, k = 0, min, max, bass = 0, muted = 0;
	int app[sMaxStrings];				// raw fingering itself
	int ind[sMaxStrings];				// indexes in hfret array
	
	int fb[sMaxStrings][sMaxFrets];	// array with an either -1 or number of note from a chord
	
	int hfret[sMaxStrings][sMaxFrets];// numbers of frets to hold on every string
	int hnote[sMaxStrings][sMaxFrets];// numbers of notes in a chord that make ^^
	
	bool needrecalc;                    // needs recalculate max/min
	
	// CALCULATION OF REQUIRED NOTES FOR A CHORD FROM USER STEP INPUT
	//
	int need[7], got[7];
	int notenum;
	int numStrings = mTabTrack.nbStrings();
	int numFrets = mTabTrack.nbFrets();

	if (!calculateNotesFromSteps(need, notenum))
		return;
	
	// BEGIN THE CHORD FILLING SESSION
	//
	mFingerList->beginSession();

	// CHECKING IF NOTE NUMBER GREATER THAT AVAILABLE STRINGS
	//
	
	// Ex: it's impossible to play 13th chords on 6 strings, but it's
	//	   possible on 7 string guitar. This way we optimize things a bit
	//
	if (numStrings < notenum) {
		mFingerList->endSession();
		return;
	}

	// CHECKING THE INVERSION NUMBER RANGE

	if (mInv->currentItem() >= notenum)
		mInv->setCurrentItem(0);

	int span = 3; // maximal fingerspan

	if (mComplexer[1]->isChecked())
		span = 4;
	if (mComplexer[2]->isChecked())
		span = 5;

	// PREPARING FOR FINGERING CALCULATION

	for (i = 0; i < numStrings; i++) {
		for (j = 0; j <= numFrets; j++)
			fb[i][j] = -1;
		for (k = 0; k < notenum; k++) {
			j = (need[k] - mTabTrack.tune(i) % 12 + 12) % 12;
			while (j <= numFrets) {
				fb[i][j] = k;
				j += 12;
			}
		}
	}

	for (i = 0; i < numStrings; i++) {
		k=1;
		hfret[i][0] = -1;
		hnote[i][0] = -2;
		for (j = 0; j <= numFrets; j++)
			if (fb[i][j] != -1) {
				hfret[i][k] = j;
				hnote[i][k] = fb[i][j];
				k++;
			}
		hnote[i][k] = -1;
	}

	// After all the previous funky calculations, we would have 2 arrays:
	// hfret[string][index] with numbers of frets where we can hold the string,
	//						(any other fret would make a chord unacceptable)
	// hnote[string][index] with numbers of notes in the chord that correspond
	//						to each hfret array's fret. -1 means end of string,
	//						-2 means muted string.

	for (i = 0; i < numStrings; i++)
		ind[i] = 0;

	min = -1; max = -1; needrecalc = false;
	
	// MAIN FINGERING CALCULATION LOOP
	//
	i = 0;
	do {
		// end of string not reached
		if (!( (hnote[i][ind[i]]==-1) || ( (!needrecalc) && (max-min>=span)))) {
			if (needrecalc) {
				min=numFrets+1;max=0;
				for (j=0;j<numStrings;j++) {
					if (hfret[j][ind[j]]>0) {
						if (hfret[j][ind[j]]<min)  min=hfret[j][ind[j]];
						if (hfret[j][ind[j]]>max)  max=hfret[j][ind[j]];
					}
					if (max-min>=span)
						break;
				}
			}
			if (max-min<span) {
				for (k=0;k<notenum;k++)
					got[k]=0;
				k=0;bass=255;muted=0;
				for (j=0;j<numStrings;j++) {
					if (hfret[j][ind[j]]>=0) {
						if (mTabTrack.tune(j)+hfret[j][ind[j]]<bass)
							bass=mTabTrack.tune(j)+hfret[j][ind[j]];
						if (!got[hnote[j][ind[j]]]) {
							got[hnote[j][ind[j]]]=1;
							k++;
						}
					} else {
						muted++;
					}
				}
			}

			if ((k==notenum) && (max-min<span) && (bass%12==need[mInv->currentItem()])) {
				for (j=0;j<numStrings;j++)
					app[j]=hfret[j][ind[j]];
				if (mComplexer[0]->isChecked()) {
					if ((muted==0) ||										// No muted strings
						((muted==1) && (app[0]==-1)) ||						// Last string muted
						((muted==2) && (app[0]==-1) && (app[1]==-1))) {		// Last and pre-last muted
						mFingerList->addFingering(app);
					}
				} else {
					mFingerList->addFingering(app);
				}
			}

			i=0;
		} else {						// end of string reached
			ind[i]=0;i++;
			needrecalc = true;
			if (i>=numStrings)
				break;
		}

		if (hfret[i][ind[i]]>min) {
			ind[i]++;
			if (hfret[i][ind[i]]>max)
				max = hfret[i][ind[i]];
			needrecalc = false;
		} else {
			ind[i]++;
			needrecalc = true;
		}
	} while (true);
	
	mFingerList->endSession();
}


/**
\brief Try to decipher text-written chord name and set steps accordingly
*/
void ChordSelector::analyzeChordName()
{
	ChordAnalyzer ca(mChordName->text());
	
	if (ca.analyze()) 
	{
		mTonic->setCurrentRow(ca.getTonic());
		//mTonic->setCurrentItem(ca.tonic);
		for (int i = 0; i < 6; i++)
			mSt[i + 1]->setCurrentItem("blah", false, ca.getStep(i));
			//mSt[i + 1]->setCurrentItem(ca.step[i]);
		findSelection();
		findChords();
	}
	else
	{
		KMessageBox::error(this, ca.message(), i18n("Unable to understand chord name"));
	}
}


/** 
\brief Analyze chord by text-written name, automatically select best one and insert it into track
*/
void ChordSelector::quickInsert()
{
	analyzeChordName();
	if (mFingerList->count() > 0)  {
		mFingerList->selectFirst();
		accept();
	}
}


/**
\brief Returns the fret number at the string x of the current fingering(?)
\param x the string number
\return the fret number
*/
int  ChordSelector::app(int x)
{
	return mFingerging->app(x);
}


/**
TODO i don't know what this function do
*/
void ChordSelector::setApp(int x, int fret)
{
	mFingerging->setApp(x, fret);
}


/**
TODO i don't know what this function do
*/
int  ChordSelector::scheme()
{
	return mStrumScheme;
}

/**
\brief Returns the Fingering class (???)
\return A reference to *mFingerging
*/
Fingering & ChordSelector::getFingering() const
{
	return *mFingerging;
}

/**
\brief Returns the chord list
\return A reference to mChords
*/
ChordList & ChordSelector::getChordList() const
{
	return *mChords;
}

#include "chord.moc"
