/***************************************************************************
 *   Copyright (C) 2006-2013 by Tomasz Bojczuk  				                   *
 *   tomaszbojczuk@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 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *  You should have received a copy of the GNU General Public License	     *
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
 ***************************************************************************/

#include "ttune.h"
#include <QVariant>
#include <QDebug>

/*static*/
Ttune Ttune::stdTune = Ttune("", Tnote(3,1,0), Tnote(7,0,0),
                                          Tnote(5,0,0), Tnote(2,0,0), Tnote(6,-1,0),Tnote(3,-1,0));

Ttune Ttune::tunes[4];
Ttune Ttune::bassTunes[4];

void Ttune::prepareDefinedTunes() {
	stdTune = Ttune(QObject::tr("Standard: E A D G B E "), Tnote(3, 1, 0), Tnote(7, 0, 0),
					Tnote(5, 0, 0), Tnote(2, 0, 0), Tnote(6, -1, 0), Tnote(3, -1, 0));
	
	tunes[0] = Ttune(QObject::tr("Dropped D: D A D G B E "), Tnote(3, 1, 0), Tnote(7, 0, 0),
									 Tnote(5, 0, 0), Tnote(2, 0, 0), Tnote(6, -1, 0),Tnote(2, -1, 0));
	tunes[1] = Ttune(QObject::tr("Dummy Lute: D A D F# B E "), Tnote(3, 1, 0), Tnote(7, 0, 0),
									 Tnote(4, 0, 1), Tnote(2, 0, 0), Tnote(6, -1, 0),Tnote(2, -1, 0));
	tunes[2] = Ttune(QObject::tr("Open: D A D G A D "), Tnote(2, 1, 0), Tnote(6, 0, 0), 
									 Tnote(5, 0, 0), Tnote(2, 0, 0), Tnote(6, -1, 0),Tnote(2, -1, 0));
	tunes[3] = Ttune(QObject::tr("Kouyanbaba: D A D A D F "), Tnote(4, 1, 0), Tnote(2, 1, 0), 
									 Tnote(6, 0, 0), Tnote(2, 0, 0), Tnote(6, -1, 0),Tnote(2, -1, 0));
	
	bassTunes[0] = Ttune(QObject::tr("Standard bass: E A D G"), Tnote(5, -1, 0), Tnote(2, -1, 0),
												Tnote(6, -2, 0), Tnote(3, -2, 0), Tnote(0, 0, 0), Tnote(0, 0, 0));
	bassTunes[1] = Ttune(QObject::tr("Bass in fifths: C G D A"), Tnote(6, -1, 0), Tnote(2, -1, 0),
												Tnote(5, -2, 0), Tnote(1, -2, 0), Tnote(0, 0, 0), Tnote(0, 0, 0));
	bassTunes[2] = Ttune(QObject::tr("5-str. bass: B E A D G"), Tnote(5, -1, 0), Tnote(2, -1, 0),
												Tnote(6, -2, 0), Tnote(3, -2, 0), Tnote(7, -3, 0), Tnote(0, 0, 0));
	bassTunes[3] = Ttune(QObject::tr("6-str. bass: B E A D G C"), Tnote(1, 0, 0), Tnote(5, -1, 0), Tnote(2, -1, 0),
												Tnote(6, -2, 0), Tnote(3, -2, 0), Tnote(7, -3, 0));
}

//##################################################################################################
Ttune::Ttune(const QString& tuneName, const Tnote& S1, const Tnote& S2, const Tnote& S3,
						 const Tnote& S4, const Tnote& S5, const Tnote& S6) :
	name(tuneName),
	m_strNumber(0),
	m_isGuitar(true)
{
		stringsArray[0] = S1;
		stringsArray[1] = S2;
		stringsArray[2] = S3;
		stringsArray[3] = S4;
		stringsArray[4] = S5;
		stringsArray[5] = S6;
		
		determineStringsNumber();
}


void Ttune::determineStringsNumber() {
		Tnote tmpStrings[6];
		quint8 strCnt = 0;
		for (int i = 0; i < 6; i++) {
			if (stringsArray[i].note != 0) { // put all defined notes to tmpStrings
				tmpStrings[strCnt] = stringsArray[i];
				strCnt++;
			}
		}
		if (strCnt < 6) // if not all were defined fill the rest of tmpStrings with empty notes
			for (int i = strCnt; i < 6; i++)
				tmpStrings[i] = Tnote(0, 0, 0);
		for (int i = 0; i < 6; i++) // copy all to main stringsArray
			stringsArray[i] = tmpStrings[i];
		m_strNumber = strCnt; // define number of strings
		if (m_strNumber < 3)
			m_isGuitar = false;
		else
			m_isGuitar = true;
}


void Ttune::copy(Ttune& t) {
	name = t.name;
	for (int i = 0; i < 6; ++i)
		stringsArray[i] = t.str(i + 1);
	
}

//##################################################################################################
//###################### I/O OPERATORS #############################################################
//##################################################################################################

/** Defined tuning have following identifiers:
 * -1 - user defined
 * 0 - standard EADGBE
 * 1 to 4 - classical/electric 6 strings defined (@p tunes array)
 * 100 to 103 - bass 4-6 strings defined (@p bassTunes array)
 */
void Ttune::toXml(QXmlStreamWriter& xml, bool isExam) {
	int id = -1; // user defined tuning
	if (isExam) { // determine is tuning built-in or user defined
		xml.writeStartElement("tuning");
		if (operator==(stdTune))
			id = 0;
		else {
			for (int i = 0; i < 4; ++i) {
				if (operator==(tunes[i])) {
					id = i + 1;	break;
				}
			}
		}
		if (id == -1) { // defined tuning still not found - try bass
			for (int i = 0; i < 4; ++i) {
				if (operator==(bassTunes[i])) {
					id = i + 100;	break;
				}
			}
		}
		xml.writeAttribute("id", QVariant(id).toString());
	} else
		xml.writeStartElement("staff-details");
	if (id == -1) { // write tuning data
		if (isExam)
				xml.writeTextElement("name", name); // skip tuning name for musicXML (save it for exam only)
		xml.writeTextElement("staff-lines", QVariant(stringNr()).toString());
		for (int i = 0; i < stringNr(); ++i) {
			stringsArray[i].toXml(xml, "staff-tuning", "tuning-", "line", QVariant(i + 1).toString());
		}
	}
	xml.writeEndElement(); // <tuning> or <staff-details>
}


bool Ttune::fromXml(QXmlStreamReader& xml, bool isExam) {
	bool ok = true;
	int id = -1;
	if (isExam) {
		id = xml.attributes().value("id").toInt();
		if (id < -1 || (id > 4 && id < 100) || (id > 103)) {
			qDebug() << "Tuning had wrong 'id'. Standard tuning will be used";
			ok = false;
		}
		switch (id) {
			case -1: break;
			case 1:
			case 2:
			case 3:
			case 4:
				copy(tunes[id]); break;
			case 100:
			case 101:
			case 102:
			case 103:
				copy(bassTunes[id - 100]); break;
			default:
				copy(stdTune); break;
		}
	}
	if (id == -1) {
		int sNrGet = 6, sNrCount = 0;
		while (xml.readNextStartElement()) {
			if (xml.name() == "name") // exam only
				name = xml.readElementText();
			else if (xml.name() == "staff-lines")
				sNrGet = QVariant(xml.readElementText()).toInt();
			else if (xml.name() == "staff-tuning") {
				sNrCount++;
				int line = xml.attributes().value("line").toInt();
				if (line > 0 && line <= sNrGet && line <= 6) {
					stringsArray[line - 1].fromXml(xml, "tuning-");
					if (!stringsArray[line - 1].isValid()) {
						qDebug() << "Wrong note describing a string" << line;
						ok = false;
					}
				} else {
					qDebug() << "Wrong string (line) number";
					ok = false;
				}
			} else
				xml.skipCurrentElement();
		}
		if (sNrGet != sNrCount) {
			qDebug() << "String numbers not matched" << sNrGet << sNrCount;
			ok = false;
		}
		if (!ok)
			copy(stdTune);
    else
      determineStringsNumber();
	} else
			xml.skipCurrentElement(); // we are not reading from <tuning> tag content so it has to be skipped
	return ok;
}



QDataStream &operator<< (QDataStream &out, const Ttune &t) {
    out << t.name;
    for (int i=0; i < 6; i++)
      out << t.stringsArray[i];
    return out;
}

QDataStream &operator>> (QDataStream &in, Ttune &t) {
    in >> t.name;
    for (int i=0; i < 6; i++)
        in >> t.stringsArray[i];
		t.determineStringsNumber();
    return in;
}






