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

#include "util/assert.h"
#include "util/logger.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/tablyrics.h"
#include "tablature/tabproperties.h"

#include "tablature/tabsong.h"

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

#include "tablature/tabtrack.h"

GuitarPro::GuitarPro()
: mInstruments(64),
  mTrackProperties(0)
{
}

GuitarPro::~GuitarPro()
{
	for (uint i = 0; i < 64; i++)
		delete mInstruments.at(i);
}

void GuitarPro::dummies(uint i)
{
	for (uint k = 0; k < i; k++) {
		mFile.getch();
		
		ASSERT( mFile.atEnd() == false );
	}
}

unsigned char GuitarPro::getByte()
{
	int result = mFile.getch();
	
	ASSERT( mFile.atEnd() == false );
	
	return result;
}

unsigned long GuitarPro::getLong()
{
	unsigned long i = getByte() | getByte() << 8 | getByte() << 16 | getByte() << 24;
	
	return i;
}

QString GuitarPro::getString()
{
	char* readBlock;
	int result = 0;
	int max = getLong();
	QString temp;
	
	if (max > 0) {
		int i = getByte();
		
		ASSERT( i == max - 1 );
		
		readBlock = new char[i + 1];
		result = mFile.readBlock(readBlock, i);
		
		ASSERT( mFile.atEnd() == false );
		ASSERT( result != 0 || i == 0 );
		
		temp = QString::fromLocal8Bit(readBlock, i);
		
		delete[] readBlock;
	} else {
		max = getByte();
	}
	
	return temp;
}

QString GuitarPro::getStringNoInc(int i)
{
	char* readBlock;
	int result = 0;
	int nb = getByte();
	QString temp;
	
	readBlock = new char[nb + 1];
	result = mFile.readBlock(readBlock, nb);
	
	ASSERT( mFile.atEnd() == false );
	
	temp = QString::fromLocal8Bit(readBlock, nb);
	
	//ktabedit::ktabLog << "getStringNoInc " << i << " " << nb << " |" << temp << "|\n";
	
	delete[] readBlock;
	
	if (i)
		dummies(i - nb);
	
	return temp;
}

unsigned char GuitarPro::getBeatDuration()
{
	unsigned char beatDuration = getByte();
	
	//ktabedit::ktabLog << "getBeatDuration " << beatDuration << "\n";
	
	if (beatDuration > 128)
		beatDuration-=256;
	
	return beatDuration;
}

// This code is traducted from the python code of songwrite
//
bool GuitarPro::loadFile(const QString& fileName)
{
	TabSong::iterator itTrack;
	unsigned char command, beatduration;
	unsigned int i, j, k, noteVolume, nbbeats, noteLength;;
	bool rest = false;
	TabSong* tabSong = new TabSong();
	
	mFile.setName(fileName);
	
	ASSERT( mFile.open(IO_ReadOnly) == true );
	
	readHeader(tabSong);
	
	readMidi();
	
	readBars(tabSong);
	
	readTracks(tabSong);
	
	i = 0;
	while (i < mNbbars) {
		j = 0;
	
		//ktabedit::ktabLog << "Lecture de la mesure " << i << "/" << mNbbars << "\n" << "\n";
		
		itTrack = tabSong->begin();
		
		while (j < mNbtrack) {
			TabBar* bar = new TabBar(i, 4, 4);
			TabBarProperties* properties = new TabBarProperties();
			properties->clone(mTrackProperties[i]);
			bar->setProperties(properties);
			
			nbbeats = getLong();
			
			//ktabedit::ktabLog << "Analyse de la piste " << j << " (" << nbbeats << " nombre de notes)" << "\n" << "\n";
			
			noteVolume = 0xCC;
			k = 0;
			
			while (k < nbbeats) {
				TabTime* time = new TabTime(k);
				noteLength = 0;
				rest = false;
				
				command = getByte();
				
				//ktabedit::ktabLog << k << " Commande = " << static_cast<int>(command) << "\n";
				
				if (command & 0x80)
					command = command & 0x3f;
				
				if (command & 0x40) {
					// Beat status
					getByte();
					//ktabedit::ktabLog << "REST found " <<  noteLength << "\n";
					rest = true;
				}
				
				if (command & 0x01) {
					//ktabedit::ktabLog << "Dotted time" << "\n";
					time->setDotted(true);
				}
				
				beatduration = getBeatDuration();
				
				if (command & 0x20) {
					time->setNTuplet(getLong() - 1);
					//ktabedit::ktabLog << "NTuplet " << notes.nTuplet() << "\n";
				}
				
				if (command & 0x02) {
					//ktabedit::ktabLog << "readChords\n";
					time->setTabChord(readChords());
					time->setChord(true);
				}
				
				if (command & 0x04) {
					time->setText(getString());
					//ktabedit::ktabLog << "Text found " << notes.text() << "\n";
				}
				
				if (command & 0x08) {
					readEffects(time->effect());
				}
				
				if (command & 0x10) {
					readChanges(bar->mixer());
				}
				
				readNotes(rest, beatduration, time, noteLength);
				
				bar->insertTime(time);
				
				k++;
			}
			j++;
			
			(*itTrack)->addBar(bar);
			
			// Next track
			//
			++itTrack;
		}
		
		i++;
	}
	
	mFile.close();
	
	setTabSong(tabSong);
	
	for (int i = 0; i < mTrackProperties.size(); i++)
		delete mTrackProperties[i];
	
	return true;
}

void GuitarPro::readHeader(TabSong* tabSong)
{
	// Check file signature
	//
	const QString gtpId3("FICHIER GUITAR PRO v3.00");
	int i;
	long noticeNbLines;
	QString notice;
	QString header;
	
	header = getStringNoInc(30);
	ASSERT( header == gtpId3);
	
	tabSong->setTitle(getString());
	//ktabedit::ktabLog << "Title = " << tabSong->title() << "\n";
	
	tabSong->setSubTitle(getString());
	//ktabedit::ktabLog << "Subtitle = " << tabSong->subTitle() << "\n";

	tabSong->setArtist(getString());
	//ktabedit::ktabLog << "Artist = " << tabSong->artist() << "\n";

	tabSong->setAlbum(getString());
	//ktabedit::ktabLog << "Album = " << tabSong->album() << "\n";

	tabSong->setAuthor(getString());
	//ktabedit::ktabLog << "Author = " << tabSong->author() << "\n";

	tabSong->setCopyright(getString());
	//ktabedit::ktabLog << "Copyright = " << tabSong->copyright() << "\n";

	tabSong->setTranscriber(getString());
	//ktabedit::ktabLog << "Transcriber = " << tabSong->transcriber() << "\n";

	tabSong->setInstructions(getString());
	//ktabedit::ktabLog << "Instruction = " << tabSong->instructions() << "\n";

	noticeNbLines = getLong();
	//ktabedit::ktabLog << "Nombre de lignes des commentaires :" << noticeNbLines << "\n";
	
	i = 0;
	while (i < noticeNbLines) {
		notice+=getString();
		notice+='\n';
		i++;
	}
	
	tabSong->setComments(notice);
	//ktabedit::ktabLog << "Commentaires =" << notice << "\n";
	
	tabSong->setShuffle(static_cast<bool>(getByte()));
	//ktabedit::ktabLog << "Shuffle =" << tabSong->isShuffle() << "\n";
	
	tabSong->setTempo(getLong());
	//ktabedit::ktabLog << "Tempo =" << tabSong->tempo() << "\n";
	
	tabSong->setKey(static_cast<uchar>(getLong()));
	//ktabedit::ktabLog << "Key =" << tabSong.nameOfKey() << "\n";
}

void GuitarPro::readMidi()
{
	// Lecture des instruments
	//
	uchar i = 0;
	
	//ktabedit::ktabLog << "Lecture des instruments =" << "\n";
	while (i < 64) {
		mInstruments[i] = new TrackMidiProperties();
		mInstruments[i]->setPatch(getLong());
		mInstruments[i]->setVolume(getByte());
		mInstruments[i]->setBalance(getByte());
		mInstruments[i]->setChorus(getByte());
		mInstruments[i]->setReverb(getByte());
		mInstruments[i]->setPhaser(getByte());
		mInstruments[i]->setTremolo(getByte());
		
		//ktabedit::ktabLog << "Instrument n " << (int)i << " " << mInstruments[i].patch() << " Volume " << (int)mInstruments[i].volume() 
		//<< " balance " << (int)mInstruments[i].balance() << " chorus " << (int)mInstruments[i].chorus() << " reverb " << (int) mInstruments[i].reverb() 
		//<< " phaser " << (int)mInstruments[i].phaser() << " tremolo " << (int)mInstruments[i].tremolo() << "\n";
		 
		dummies(2);
		i++;
	}
}

void GuitarPro::readBars(TabSong* tabSong)
{
	unsigned int i;
	
	mNbbars = getLong();
	//ktabedit::ktabLog << "\n\n" << "Nombre de mesures =" << mNbbars << "\n";
	
	mNbtrack = getLong();
	//ktabedit::ktabLog << "Nombre de pistes =" << mNbtrack << "\n";
	
	// Read track infos: name
	//
	tabSong->clear();
	
	mTrackProperties.resize(mNbbars);
	
	unsigned char barFlag;
	unsigned char barRythm1 = 4, barRythm2 = 4;
	
	i = 0;
	while (i < mNbbars) {
		barFlag = getByte();
		
		//ktabedit::ktabLog << "Mesure =" << i << "\n";
		
		if (barFlag & 1) {
			barRythm1 = getByte();
			//ktabedit::ktabLog << "Bar rythm 1 =" << static_cast<int>(barRythm1) << "\n";
		}
		if (barFlag & 2) {
			barRythm2 = getByte();
			//ktabedit::ktabLog << "Bar rythm 2 =" << static_cast<int>(barRythm2) << "\n";
		}
		
		mTrackProperties[i] = new TabBarProperties();
		mTrackProperties[i]->setNumerator(barRythm1);
		mTrackProperties[i]->setDenominator( barRythm2);
		
		if (barFlag & 4) {
			//ktabedit::ktabLog << "Open repeat barFlag & 4" << "\n";
			mTrackProperties[i]->setRepeatStart(true);
		}
		
		if (barFlag & 8) {
			//ktabedit::ktabLog << "Number of repetitions" << "\n";
			mTrackProperties[i]->setRepetitions(getByte());
		}
		
		if (barFlag & 16) {
			//ktabedit::ktabLog << "Number of alternate endings" << "\n";
			mTrackProperties[i]->setAlternateEndings(getByte());
		}
		
		if (barFlag & 32) {
			//ktabedit::ktabLog << "Marker barFlag & 32" << "\n";
			QString marker(getString());
			QColor color(getByte(), getByte(), getByte());
			
			//ktabedit::ktabLog << "Marker =" << marker << "Couleur r :" << color << "\n";
			
			// Empty byte
			//
			getByte();
			
			mTrackProperties[i]->setMarker(marker);
			mTrackProperties[i]->setColor(color);
		}
		
		if (barFlag & 64) {
			//ktabedit::ktabLog << "Armor change barFlag & 64" << "\n";
			
			// Armor jump
			//
			mTrackProperties[i]->setKey(getByte());
			
			// Minor
			//
			mTrackProperties[i]->setMinor(getByte());
		}
		
		if (barFlag & 0x80) {
			//ktabedit::ktabLog << "Double bar & 80" << "\n";
			mTrackProperties[i]->setDoubleBar(true);
		}
		i++;
	}
}

void GuitarPro::readTracks(TabSong* tabSong)
{
	unsigned int i = 0, j;
	unsigned long tunning[7];
	
	//ktabedit::ktabLog << "Track" << "\n";
	
	while (i < mNbtrack) {
		TrackMode mode = FretTab;
		
		unsigned char header = getByte();
		
		if (header & 1) {
			//ktabedit::ktabLog << "drum track" << "\n";
			mode = DrumTab;
		}
		if (header & 2) {
			//ktabedit::ktabLog << "12 strings track" << "\n";
			mode = TwelveStringsTab;
		}
		if (header & 4) {
			//ktabedit::ktabLog << "banjo track" << "\n";
			mode = BanjoTab;
		}
		
		QString trackName = getStringNoInc(40);
		//ktabedit::ktabLog << "Track Name =" << track << "\n";
		
		ulong nbStrings = getLong();
		//ktabedit::ktabLog << "Nb Strings =" << nbStrings << "\n";
		
/*		if (mode == DrumTab)
			nbStrings = 4;*/
		
		//ktabedit::ktabLog << "Tuning =\n";
		
		j = 0;
		while (j < 7) {
			tunning[j] = getLong();
			j++;
		}
		
		int midiPort = getLong();
		//ktabedit::ktabLog << "Midi port =" << midiPort << "\n";
		
		int channel = getLong();
		//ktabedit::ktabLog << "Channel 1 =" << channel << "\n";
		
		int channelEffects = getLong();
		//ktabedit::ktabLog << "Channel effects =" << channelEffects << "\n";
		
		int nbFrets = getLong();
		//ktabedit::ktabLog << "Nb frets =" << nbFrets << "\n";
		
		int capo = getLong();
		//ktabedit::ktabLog << "Capo =" << capo << "\n";
		
		QColor color(getByte(), getByte(), getByte());
		//ktabedit::ktabLog << "Color =" << "Couleur :" << color << "\n";
		
		// Empty byte
		//
		getByte();
		
		TabTrack* track = new TabTrack(mode, trackName, 0, 0, mInstruments[channel]->patch(), nbStrings, nbFrets);
		TrackMidiProperties* midi = new TrackMidiProperties();
		
		for (j = 0; j < nbStrings; j++)
			track->setTune(nbStrings - 1 - j, tunning[j]);
		
		midi->clone(mInstruments[channel - 1]);
		track->setMidiProperties(midi);
		track->setCapo(capo);
		track->setColor(color);
		track->setChannel(channel);
		track->setChannelEffects(channelEffects);
		track->setMidiPort(midiPort);
		
		tabSong->push_back(track);
		
		i++;
	}
}

void GuitarPro::readEffects(TabTimeEffect* timeEffect)
{
	uchar command = getByte();
	//ktabedit::ktabLog << "Beat effect " << (int)command << "\n";
	
	if (command & 0x20) {
		uchar effect = getByte();
		
		if (!effect) {
			//ktabedit::ktabLog << "Tremolo bar" << "\n";
			unsigned int value = getLong();
			
			timeEffect->setTremolo(value);
		} else {
			switch(effect) {
				case 1:
					//ktabedit::ktabLog << "Tapping\n";
					timeEffect->setTapping(true);
					break;
				case 2:
					//ktabedit::ktabLog << "slapping\n";
					timeEffect->setSlapping(true);
					break;
				case 3:
					//ktabedit::ktabLog << "Popping\n";
					timeEffect->setPopping(true);
					break;
				default:
					throw "Unexpected value in effect";
			}
			
			getLong();
		}
		
	}
	
	if (command & 0x40) {
		//ktabedit::ktabLog << "Strocke" << "\n";
		timeEffect->setStroke(true);
		
		int duration = getByte();
		
		if (duration)
			timeEffect->setUpStroke(6 - duration);
		
		duration = getByte();
		
		if (duration)
			timeEffect->setDownStroke(6 - duration);
	}
	
	if (command & 0x01) {
		timeEffect->setVibrato(true);
		//ktabedit::ktabLog << "Vibrato" << "\n";
	}
	
	if (command & 0x02) {
		timeEffect->setWideVibrato(true);
		//ktabedit::ktabLog << "Wide vibrato" << "\n";
	}
	
	if (command & 0x04) {
		timeEffect->setNaturalHarmonic(true);
		//ktabedit::ktabLog << "Natural harmonic" << "\n";
	}
	
	if (command & 0x08) {
		timeEffect->setArtificialHarmonic(true);
		//ktabedit::ktabLog << "Other harmonic" << "\n";
	}
	
	if (command & 0x10) {
		timeEffect->setFadeIn(true);
		//ktabedit::ktabLog << "Fade in" << "\n";
	}
}

void GuitarPro::readChanges(TabMixTableChanges* mixer)
{
// 	//ktabedit::ktabLog << "Change" << "\n";
	
	uchar new_inst = getByte();
// 	//ktabedit::ktabLog << "Instrument " << static_cast<int>(new_inst) << "\n";
	
	uchar new_vol = getByte();
// 	//ktabedit::ktabLog << "Volume " << static_cast<int>(new_vol) << "\n";
	
	uchar new_pan = getByte();
// 	//ktabedit::ktabLog << "Pan " << static_cast<int>(new_pan) << "\n";
	
	uchar new_chorus = getByte();
// 	//ktabedit::ktabLog << "Chorus " << static_cast<int>(new_chorus) << "\n";
	
	uchar new_reverb = getByte();
// 	//ktabedit::ktabLog << "Reverb " << static_cast<int>(new_reverb) << "\n";
	
	uchar new_phaser = getByte();
// 	//ktabedit::ktabLog << "Phaser " << static_cast<int>(new_phaser) << "\n";
	
	uchar new_tremolo = getByte();
// 	//ktabedit::ktabLog << "Tremolo " << static_cast<int>(new_tremolo) << "\n";
	
	int new_tempo = getLong();
// 	//ktabedit::ktabLog << "Tempo " << static_cast<int>(new_tempo) << "\n";
	
	
	if (new_vol != 255) {
		mixer->setInstrument(new_inst);
		dummies(1);
	}
	
	if (new_pan != 255) {
		mixer->setBalance(new_pan);
		dummies(1);
	}
	
	if (new_chorus != 255) {
		mixer->setChorus(new_chorus);
		dummies(1);
	}
	
	if (new_reverb != 255) {
		mixer->setReverb(new_reverb);
		dummies(1);
	}
	
	if (new_phaser != 255) {
		mixer->setPhaser(new_phaser);
		dummies(1);
	}
	
	if (new_tremolo != 255) {
		mixer->setTremolo(new_tremolo);
		dummies(1);
	}
	
	if (new_tempo != -1) {
		mixer->setTempo(new_tempo);
		dummies(1);
	}
}

TabChord* GuitarPro::readChords()
{
	uchar header = getByte();
	TabChord* chord = new TabChord();
	
	if ((header & 0x01) == 0) {
	   	//ktabedit::ktabLog << "no sharp\n";	
		chord->setName(getString());
		
		int base = getLong();
		
		chord->setBaseFret(base);
		
		if (base != 0) {
			for (int i = 0; i < 6; i++) {
				chord->setFret(i, getLong());
			}
		}
	} else {
		
 		//ktabedit::ktabLog << "Sharp \n"; 
		
		// Sharp
		//
		chord->setSharp(getByte());
		
		dummies(3);
		
		// GP4Root
		//
		chord->setRoot(getLong());
		
		// Chord type
		//
		chord->setType(getLong());
		
		// 9, 11, 13
		//
		chord->setNineElevenThirteen(static_cast<TabChord::ExtentionChord>(getLong()));
		
		// Bass
		//
		chord->setBass(getLong());
		
		// Augmented or diminished
		//
		chord->setAugmentedDiminished(static_cast<TabChord::AugmentedDiminished>(getByte()));
		
		// Skips 3 bytes
		//
		dummies(3);
		
		// Added note
		//
		chord->setAddedNote(getByte());
		
		// Name
		//
		chord->setName(getStringNoInc(34));
		
		// Base fret
		//
		chord->setBaseFret(getLong());
		
		// Reads the 6 frets
		for (int i = 0; i < 6; i++) {
			chord->setFret(i, getLong());
		}
		
		// Reads 28 bytes
		for (int i = 0; i < 28; i++) {
			getByte();
		}
		
		// Omission : gives the notes present in the chord
		for (int i = 0; i < 7; i++) {
			chord->setFingering(i, getByte());
		}
		
		// Show diagram
		//
		chord->setShowDiagram(getByte());
		  
	}
	
	return chord;
}

void GuitarPro::readNotes(bool rest, uchar beatduration, TabTime* time, uint noteLength)
{
	char strings_fill = getByte(), numberOfStrings = 0, i;
	
	if (rest) {
		switch(beatduration) {
		case 0:
			time->setDuration(Quarter);
			break;
		case 1:
			time->setDuration(Eighth);
			break;
		case 2:
			time->setDuration(Sixteenth);
			break;
		case 3:
			time->setDuration(ThirthSecond);
			break;
		case 4:
			// Quadruple croche
			time->setDuration(SixtyFourth);
			break;
		case 0xff:
			time->setDuration(Half);
			break;
		case 0xfe:
			time->setDuration(Whole);
			break;
		}
		
		time->setRest(true);
		
		rest = false;
	}
	
	for (i = 0; i < 7; i++) {
		if (strings_fill & (1 << i)) {
			numberOfStrings++;
		}
	}
	
	//ktabedit::ktabLog << "String played = " << (int)strings_fill << " " << (int)numberOfStrings << "\n";
	
	QVector<char> notes_strings;
	
	if (strings_fill & 64) notes_strings.push_back(0);
	if (strings_fill & 32) notes_strings.push_back(1);
	if (strings_fill & 16) notes_strings.push_back(2);
	if (strings_fill & 8)  notes_strings.push_back(3);
	if (strings_fill & 4)  notes_strings.push_back(4);
	if (strings_fill & 2)  notes_strings.push_back(5);
	if (strings_fill & 1)  notes_strings.push_back(6);
	//QValueList<char>::iterator it = notes_strings.begin();
	
	i = 0;
	while (i < numberOfStrings) {
		TabNote* note = new TabNote(i + 1);
		
		uchar note_special = getByte();
		uchar note_alteration = 0;
		uchar note_value = 0;
		char chord = 0;
		
		//ktabedit::ktabLog << "Note header = " << (int)note_special << "\n";
		
		if (note_special & 0x20) {
			note_alteration = getByte();
			//ktabedit::ktabLog << "note alt�ration " << static_cast<int>(note_alteration)  << "\n";
			
			if (note_alteration == 3) {
				//ktabedit::ktabLog << "Dead note" << "\n";
				note->setDeadNote(true);
			} else if (note_alteration == 2) { 
				//ktabedit::ktabLog << "Note is linked from another note" << "\n";
				note->setTieNote(true);
			}
		}
		
		if (note_special & 0x01) {
			//ktabedit::ktabLog << "note duration != beat duration" << "\n";
			
			uchar note_duration = getByte();
			if(note_duration > 128) note_duration-=256;
			
			//ktabedit::ktabLog << static_cast<int>(note_duration) << "\n";
			
			// NTuplet
			//
			note->setNTuplet(getByte() - 1);
		}
		
		if (note_special & 0x10) {
// 			//ktabedit::ktabLog << "Note nuance" << "\n";
			note->setDynamic(static_cast<Dynamic>(getByte()));
		}
		
		if (note_special & 0x20) {
			note_value = getByte();
			chord = notes_strings[i];
			//chord = (*it);
			//ktabedit::ktabLog << "Valeur de la note =" << static_cast<int>(note_value) << " corde = " << 5 -  static_cast<int>(chord) << "\n";
			if (note_value == 255) note_value = 0;
		}
		
		if (note_special & 0x20) {
			
// 			//ktabedit::ktabLog << "Ajout de la note :" << static_cast<int>(note_value) << " corde : " << 5 -  static_cast<int>(chord) << "\n";
// 			//ktabedit::ktabLog << "beat = " << (int)beatduration << "\n";
			switch(beatduration) {
			case 0:
				note->setDuration(Quarter);
				time->setDuration(Quarter);
				break;
			case 1:
				note->setDuration(Eighth);
				time->setDuration(Eighth);
				break;
			case 2:
				note->setDuration(Sixteenth);
				time->setDuration(Sixteenth);
				break;
			case 3:
				note->setDuration(ThirthSecond);
				time->setDuration(ThirthSecond);
				break;
			case 4:
				// Quad croche
				note->setDuration(SixtyFourth);
				time->setDuration(SixtyFourth);
				break;
			case 0xff:
				note->setDuration(Half);
				time->setDuration(Half);
				break;
			case 0xfe:
				note->setDuration(Whole);
				time->setDuration(Whole);
				break;
// 			default:
// 				//ktabedit::ktabLog << "beat " << beatduration << "\n";
			}
			note->setFret(note_value);
// 			note.setNotes(chord, note_value);
			noteLength = 1;
		}
		
		if (note_special & 0x80) {
// 			//ktabedit::ktabLog << "fingerprint" << "\n";
			// Left hand
			note->setFingeringLeft(static_cast<TabFingering>(getByte()));
			// Right hand
			note->setFingeringRight(static_cast<TabFingering>(getByte()));
		}
		
		if (note_special & 0x02) {
			//ktabedit::ktabLog << "Dotted note" << "\n";
			note->setDotted(true);
		}
		
		if (note_special & 0x40) {
			//ktabedit::ktabLog << "Accentuated note" << "\n";
			note->setAccentuated(true);
		}
		
		if (note_special & 0x04) {
			//ktabedit::ktabLog << "Ghost note" << "\n";
			note->setGhostNote(true);
		}
		
		if (note_special & 0x08) {
			uchar effect_1 = getByte();
			
			//ktabedit::ktabLog << " Effect =" << (int)effect_1 << "\n";
			
			if (effect_1 & 0x01) {
				//ktabedit::ktabLog << "Note effect : Bend" << "\n";
				
				// Type of the bend
				//
				note->setBendType(static_cast<TabBend::BendType>(getByte()));
				
				//Vertical height
				//
				note->setBendValue(getLong());
				
				// num points
				//
				int numPoints = getLong();
				
				for (int i = 0; i <numPoints; i++) {
					TabBendPoint* point = new TabBendPoint;
					
					point->setPosition(getLong());
					point->setHeight(getLong());
					point->setVibrato(static_cast<TabVibrato>(getByte()));
					
					note->addBendPoint(point);
				}
			}
			
			if (effect_1 & 0x02) {
				//ktabedit::ktabLog << "Note effect : Pull" << "\n";
				note->setHammerOnPullOff(true);
			}
			
			if (effect_1 & 0x04) {
				//ktabedit::ktabLog << "Note effect : Slide" << "\n";
				note->setSlide(ShiftSlide);
			}

			if (effect_1 & 0x08) {
				//ktabedit::ktabLog << "Note effect : Let Ring" << "\n";
				note->setLetRing(true);
			}
			
			if (effect_1 & 0x10) {
				//ktabedit::ktabLog << "Note effect : Grace note" << "\n";
				// Fret
				note->setGraceNoteFret(getByte());
				// Dynamic
				note->setGraceNoteDynamic(static_cast<Dynamic>(getByte()));
				// Transition
				note->setGraceNoteTransition(static_cast<GraceNoteTransition>(getByte()));
				// Duration
				note->setGraceNoteDuration(getByte());
			}
		}
		
		time->setString(chord, note);
		
		//++it;
		i++;
	}
}

void GuitarPro::writeDummies(uint i)
{
	for (uint j = 0; j < i; j++)
		mFile.putch(0x00);
}

void GuitarPro::writeByte(char c)
{
	mFile.putch(c);
}

void GuitarPro::writeLong(long l)
{
	char *temp = reinterpret_cast<char *>(&l);
	
	mFile.putch(temp[0]);
	mFile.putch(temp[1]);
	mFile.putch(temp[2]);
	mFile.putch(temp[3]);
}

void GuitarPro::writeColor(const QColor &color)
{
	writeByte(color.blue());
	writeByte(color.green());
	writeByte(color.red());
	
	writeDummies(1);
}

void GuitarPro::writeString(const QString &s)
{
	if (s.length() > 0) {
		writeLong(s.length() + 1);
		
		writeByte(s.length());
		mFile.writeBlock(s.toAscii(), s.length());
	} else {
		writeLong(1);
		writeByte(0);
	}
}

void GuitarPro::writeStringNoInc(const QString &s, int i)
{
	writeByte(s.length());
	mFile.writeBlock(s.toAscii(), s.length());
	
	writeDummies(i - s.length());
}

void GuitarPro::writeHeader(const TabSong& tabSong)
{
	const QString gProId("FICHIER GUITAR PRO v3.00");
	
	// Write file signature
	//
	writeByte(gProId.length());
	mFile.writeBlock(gProId.toAscii(), gProId.length());
	
	// Now it's time to parse the whole thing
	// First the header: general infos
	//
	writeDummies(31 - gProId.length() - 1);
	
	writeString(tabSong.title());
	writeString(tabSong.subTitle());
	writeString(tabSong.artist());
	writeString(tabSong.album());
	writeString(tabSong.author());
	writeString(tabSong.copyright());
	writeString(tabSong.transcriber());
	writeString(tabSong.instructions());
	
	QString notice = tabSong.comments();
	QStringList noticeLine = QStringList::split('\n', notice);
	ulong noticeNbLines = notice.count('\n');
	
	writeLong(noticeNbLines);
	
	for (QStringList::Iterator it = noticeLine.begin(); it != noticeLine.end(); ++it) {
		 writeString(*it);
	}
	
	writeByte(tabSong.isShuffle());
	writeLong(tabSong.tempo());
	writeLong(tabSong.key().toAscii());
}

void GuitarPro::writeMidiHeader(const TabSong &tabSong)
{
	TabSong::const_iterator it(tabSong.begin());
	const TabTrack *track = 0x0;
	uint i = 0;
	bool drum = false;
	
	while(it != tabSong.end()) {
		const TrackMidiProperties* trackMidiProperties = (*it)->midiProperties();
		
		if (i != 8 && track->mode() != DrumTab) {
			TrackMidiProperties* properties = new TrackMidiProperties();
			properties->clone(trackMidiProperties);
			mInstruments[i] = properties;
			
			properties = new TrackMidiProperties();
			properties->clone(trackMidiProperties);
			mInstruments[i + 1] = properties;
			
			i+=2;
		} else if (track->mode() == DrumTab) {
			TrackMidiProperties* properties = new TrackMidiProperties();
			properties->clone(trackMidiProperties);
			mInstruments[9] = properties;
			
			drum = true;
			
			if (i == 8)
				i = 10;
		} else{
			i = 10;
			
			TrackMidiProperties* properties = new TrackMidiProperties();
			properties->clone(trackMidiProperties);
			mInstruments[i] = properties;
			
			properties = new TrackMidiProperties();
			properties->clone(trackMidiProperties);
			mInstruments[i + 1] = properties;
			
			i+=2;
		}
		
		++it;
	}
	
	for (; i < 64; i++) {
		if (i == 9 && drum)
			continue;
		
		mInstruments[i] = new TrackMidiProperties();
		mInstruments[i]->setPatch(24);
		mInstruments[i]->setVolume(13);
		mInstruments[i]->setBalance(8);
		mInstruments[i]->setChorus(0);
		mInstruments[i]->setReverb(0);
		mInstruments[i]->setPhaser(0);
		mInstruments[i]->setTremolo(0);
	}
	
	for (i = 0; i < 64; i++) {
		if ((i - 9) % 16 != 0)
			writeLong((char)mInstruments[i]->patch());
		else
			if (mInstruments[9]->patch() == 0xff)
				writeLong(-1);
			else
				writeLong(0);
		
		writeByte(mInstruments[i]->volume());
		writeByte(mInstruments[i]->balance());
		writeByte(mInstruments[i]->chorus());
		writeByte(mInstruments[i]->reverb());
		writeByte(mInstruments[i]->phaser());
		writeByte(mInstruments[i]->tremolo());
		
		writeDummies(2);
	}
	
	for (i = 0; i < 64; i++) {
		delete mInstruments[i];
	}
}

void GuitarPro::writeBarsHeader(const TabSong &tabSong)
{
	TabSong::const_iterator it(tabSong.begin());
	uint i = 0;
	uchar barFlag;
	uchar barRythm1 = 0, barRythm2 = 0;
	
	// Number of bars
	//
	writeLong((*it)->size());
	
	// Number of tracks
	//
	writeLong(tabSong.size());
	
	i = 0;
	while (it != tabSong.end()) {
		const TabBar* bar = (*it)->bar(i);
		barFlag = 0;
		
		if (bar->numerator() != barRythm1) {
			 barRythm1 = bar->numerator();
			 barFlag = barFlag | 1;
		}
		
		if (bar->denominator() != barRythm2) {
			 barRythm2 = bar->denominator();
			 barFlag = barFlag | 2;
		}
		
		if (bar->isRepeatStart()) {
			barFlag = barFlag | 4;
		}
		
		if (bar->repetitions() != 0) {
			barFlag = barFlag | 8;
		}
		
		if (bar->isAlternateEndings()) {
			barFlag = barFlag | 16;
		}
		
		if (bar->color() != QColor() || bar->marker().isEmpty() == false) {
			barFlag = barFlag | 32;
		}
		
		if (bar->key() != QChar(7)) {
			barFlag = barFlag | 64;
		}
		
		if (bar->isDoubleBar()) {
			barFlag = barFlag | 128;
		}
		
		writeByte(barFlag);
		
		if (barFlag & 1)
			writeByte(barRythm1);
		
		if (barFlag & 2)
			writeByte(barRythm2);
		
		if (barFlag & 8)
			writeByte(bar->repetitions());
		
		if (barFlag & 16)
			writeByte(bar->isAlternateEndings());
		
		if (barFlag & 32) {
			writeString(bar->marker());
			writeColor(bar->color());
		}
		
		if (barFlag & 64) {
			writeByte(bar->key().toAscii());
			writeByte(bar->isMinor());
		}
		
		i++;
	}
}

void GuitarPro::writeTracksHeader(const TabSong &tabSong)
{
	TabSong::const_iterator it(tabSong.begin());
	uint i = 0;
	
	while(it != tabSong.end()) {
		const TabTrack& track = **it;
		
		switch(track.mode()) {
		case FretTab:
			writeByte(0);
			break;
		case DrumTab:
			writeByte(1);
			break;
		case TwelveStringsTab:
			writeByte(2);
			break;
		case BanjoTab:
			writeByte(4);
			break;
		}
		
		writeStringNoInc(track.name(), 40);
		
		if (track.mode() == DrumTab)
			writeLong(6);
		else
			writeLong(track.nbStrings());
		
		for (i = 0; i < track.nbStrings(); i++)
			writeLong(track.tune(i));
		
		for (; i < 7; i++)
			writeLong(0);
		
		writeLong(track.midiPort());
		
		writeLong(track.channel());
		
		writeLong(track.channelEffects());
		
		writeLong(track.nbFrets());
		
		writeLong(track.capo());
		
		writeColor(track.color());
		
		++it;
	}
}

void GuitarPro::writeBars(uint nbChords, const TabBar &bar)
{
	//ktabedit::ktabLog << "bar.count() " << bar.size() << "\n";
	writeLong(bar.size());
	
	for (uint i = 0; i < bar.size(); i++) {
		writeTime(nbChords, *bar.mixer(), bar.time(i));
	}
}

void GuitarPro::writeTime(uint nbChords, const TabMixTableChanges &/*changes*/, const TabTime* time)
{
	uchar cmd = 0;
	TabTimeEffect effect;
	TabMixTableChanges mixChanges;
	
	if (time->isDotted()) {
		cmd = cmd | 1;
	}
	
/*	if (times.isChord()) {
		cmd = cmd | 2;
	}*/
	
	if (time->text().isEmpty() == false) {
		cmd = cmd | 4;
	}
	
/*	if (times != effects) {
		cmd = cmd | 8;
	}*/
	
/*	if (changes != mixChanges) {
		cmd = cmd | 16;
	}*/
	
	if (time->nTuplet()) {
		cmd = cmd | 32;
	}
	
	if (time->isRest()) {
		cmd = cmd | 64;
	}
	
	//ktabedit::ktabLog << "times cmd " << cmd << "\n";
	writeByte(cmd);
	
	if (cmd & 64) {
		//ktabedit::ktabLog << "beat status " << 0x0 << "\n";
		// Beat status : 0x00 empty
		//               0x02 rest
		//
		writeByte(2);
	}
	
	writeBeatDuration(time);
	
	if (cmd & 32) {
		//ktabedit::ktabLog << "tupletDuration " << times.nTuplet() + 1 << "\n";
		writeLong(time->nTuplet() + 1);
	}
	
/*	if (cmd & 2) {
		writeChord(times);
	}*/
	
	if (cmd & 4) {
		writeString(time->text());
	}
	
/*	if (cmd & 8) {
		writeEffects(times);
	}*/
	
/*	if (cmd & 16) {
		writeChanges(times);
	}*/
	
	writeFrets(nbChords, time);
}

void GuitarPro::writeBeatDuration(const TabTime* time)
{
	uchar duration = 0;
	
	switch(time->duration()) {
	case NoneDuration:
	case HundredTwentyEighth:
	case Quarter:
		duration = 0;
		break;
	case Eighth:
		duration = 1;
		break;
	case Sixteenth:
		duration = 2;
		break;
	case ThirthSecond:
		duration = 3;
		break;
	case SixtyFourth:
		duration = 4;
		break;
	case Half:
		duration = 0xff;
		break;
	case Whole:
		duration = 0xfe;
		break;
	}
	
	//ktabedit::ktabLog << "time duration " << duration << "\n";
	writeByte(duration);
}

void GuitarPro::writeChord(const TabChord &chord)
{
	if (chord.isSharp() == 0) {
		writeByte(0);
		
		writeString(chord.name());
		writeLong(chord.baseFret());
		
		if (chord.baseFret() != 0) {
			for (uchar i = 0; i < 6; i++)
				writeLong(chord.fret(i));
		}
	} else {
		writeByte(1);
		writeDummies(3);
		
		writeLong(chord.root());
		writeLong(chord.type());
		writeLong(chord.nineElevenThirteen());
		writeLong(chord.bass());
		writeByte(chord.augmentedDiminished());
		writeDummies(3);
		writeByte(chord.addedNote());
		writeStringNoInc(chord.name(), 34);
		writeLong(chord.baseFret());
		
		for (uchar i = 0; i < 6; i++)
			writeLong(chord.fret(i));
		
		writeDummies(28);
		
		for (uchar i = 0; i < 7; i++)
			writeByte(chord.fingering(i));
		
		writeByte(chord.isShowDiagram());
	}
}

void GuitarPro::writeEffects(const TabTimeEffect* effect)
{
	uchar cmd = 0;
	
	if (effect->isVibrato())
		cmd = cmd | 1;
	
	if (effect->isWideVibrato())
		cmd = cmd | 2;
	
	if (effect->isNaturalHarmonic())
		cmd = cmd | 4;
	
	if (effect->isArtificialHarmonic())
		cmd = cmd | 8;
	
	if (effect->isFadeIn())
		cmd = cmd | 16;
	
	if (effect->tremolo() || effect->isTapping() || effect->isSlapping() || effect->isPopping())
		cmd = cmd | 32;
	
	if (effect->isStroke())
		cmd = cmd | 64;
	
	//ktabedit::ktabLog << "write effects " << cmd << "\n";
	writeByte(cmd);
	
	if (effect->tremolo() || effect->isTapping() || effect->isSlapping() || effect->isPopping()) {
		uchar cmd2 = 0;
		
		if (effect->tremolo())
				cmd2 = 0;
		else {
			if (effect->isTapping())
				cmd2 = 1;
			else if (effect->isSlapping())
				cmd2 = 2;
			else if (effect->isPopping())
				cmd2 = 3;
		}
		
		writeByte(cmd2);
		
		writeDummies(4);
	}
	
	if (effect->isStroke()) {
		writeByte(6 - effect->upStroke());
		writeByte(6 - effect->downStroke());
	}
}

void GuitarPro::writeChanges(const TabMixTableChanges &mix)
{
	writeByte(mix.instrument());
	writeByte(mix.volume());
	writeByte(mix.balance());
	writeByte(mix.chorus());
	writeByte(mix.reverb());
	writeByte(mix.phaser());
	writeByte(mix.tremolo());
	writeLong(mix.tempo());
	
	if (mix.instrument() != -1)
		writeDummies(1);
	
	if (mix.volume() != -1)
		writeDummies(1);
	
	if (mix.balance() != -1)
		writeDummies(1);
	
	if (mix.chorus() != -1)
		writeDummies(1);
	
	if (mix.reverb() != -1)
		writeDummies(1);
	
	if (mix.phaser() != -1)
		writeDummies(1);
	
	if (mix.tremolo() != -1)
		writeDummies(1);
}

void GuitarPro::writeFrets(uint nbChords, const TabTime* time)
{
	uchar string = 0;
	uchar cmd;
	
/*	for (i = 0; i < 7; i++) {
		if (strings_fill & (1 << i)) {
			numberOfStrings++;
		}
	}
	//ktabedit::ktabLog << "String played = " << (int)strings_fill << " " << (int)numberOfStrings << "\n";	
	
	QValueList<char> notes_strings;
	
	if (strings_fill & 64) notes_strings.append(0);
	if (strings_fill & 32) notes_strings.append(1);
	if (strings_fill & 16) notes_strings.append(2);
	if (strings_fill & 8)  notes_strings.append(3);
	if (strings_fill & 4)  notes_strings.append(4);
	if (strings_fill & 2)  notes_strings.append(5);
	if (strings_fill & 1)  notes_strings.append(6);
	QValueList<char>::iterator it = notes_strings.begin();

	note_value = getByte();
	chord = (*it);
	//ktabedit::ktabLog << "Valeur de la note =" << static_cast<int>(note_value) << " corde = " << 5 -  static_cast<int>(chord) << "\n";
	if (note_value == 255) note_value = 0;*/
	
	for (uchar i = 0; i < nbChords; i++) {
		if (time->string(i)->isEmpty() == false) {
			string = string | (1 << (6 - i));
// 			string = string | (1 << (nbChords - i));
		}
	}
	
	//ktabedit::ktabLog << "string " << string << "\n";
	writeByte(string);
	
	for (uchar i = 0; i < nbChords; i++) {
		cmd = 0;
		const TabNote* note = time->string(i);
		
		if (note->isEmpty() == true)
			continue ;
		
		// if (note duration != beatDuration)
		//	cmd = 1;
		
		if (note->isDotted())
			cmd = cmd | 2;
		
		if (note->isGhostNote())
			cmd = cmd | 4;
		
		if (note->bendType() != TabBend::NoneBend || note->isHammerOnPullOff() || note->slide() != NoneSlide || note->isLetRing() || note->graceNoteFret())
			cmd = cmd | 8;
		
		if (note->dynamic() != NoneDynamic)
			cmd = cmd | 16;
		
		if (note->isDeadNote() || note->isTieNote() || note->isEmpty() == false)
			cmd = cmd | 32;
		
		if (note->isAccentuated())
			cmd = cmd | 64;
		
		if (note->fingeringLeft() != NoneFingering || note->fingeringLeft() != NoneFingering)
			cmd = cmd | 128;
		
		//ktabedit::ktabLog << "chord = " << i << " cmd " << cmd << "\n";
		writeByte(cmd);
		
		if (cmd & 32) {
			uchar effectCmd;
			
			if (note->isDeadNote())
				effectCmd = 3;
			else if (note->isTieNote())
				effectCmd = 2;
			else
				effectCmd = 1;
			
			//ktabedit::ktabLog << "cmd & 32 = " << effectCmd << "\n";
			writeByte(effectCmd);
		}
		
		if (cmd & 16)
			writeByte(note->dynamic());
		
		if (cmd & 32) {
			//ktabedit::ktabLog << "cmd & 32 = " << note.fret() << "\n";
			writeByte(note->fret());
		}
		
		if (cmd & 128) {
			writeByte(note->fingeringLeft());
			writeByte(note->fingeringRight());
		}
		
		if (cmd & 8) {
			uchar effectCmd = 0;
			
			if (note->bendType() != TabBend::NoneBend)
				effectCmd = 1;
			
			if (note->isHammerOnPullOff())
				effectCmd = effectCmd | 2;
			
			if (note->slide() != NoneSlide)
				effectCmd = effectCmd | 4;
			
			if (note->isLetRing())
				effectCmd = effectCmd | 8;
			
			if (note->graceNoteFret())
				effectCmd = effectCmd | 16;
			
			//ktabedit::ktabLog << "cmd & 8 = " << effectCmd << "\n";
			writeByte(effectCmd);
			
			if (effectCmd & 1) {
				writeByte(note->bendType());
				writeLong(note->bendValue());
				writeLong(note->sizeOfBendPoints());
				
				for (int i = 0; i < note->sizeOfBendPoints(); ++i) {
					writeLong(note->bendPoint(i)->position());
					writeLong(note->bendPoint(i)->height());
					writeByte(note->bendPoint(i)->tabVibrato());
				}
			}
			
			if (effectCmd & 16) {
				writeByte(note->graceNoteFret());
				writeByte(note->graceNoteDynamic());
				writeByte(note->graceNoteTransition());
				writeByte(note->graceNoteDuration());
			}
		}
	}
}

bool GuitarPro::saveFile(const QString& fileName, const TabSong& tabSong)
{
	TabSong::const_iterator it;
	const uint trackCount = (*it)->size();
	uint i, j;
	
	// Open file
	//
	mFile.setName(fileName);
	
	if (!mFile.open(IO_WriteOnly)) {
		//ktabedit::ktabLog << "Cannot write file" << "\n";
		throw "Cannot open file";
	}
	
	writeHeader(tabSong);
	
	writeMidiHeader(tabSong);
	
	writeBarsHeader(tabSong);
	
	writeTracksHeader(tabSong);
	
	//ktabedit::ktabLog << "GuitarPro::SaveFile\n";
	i = 0;
	while (i < trackCount) {
		it = tabSong.begin();
		
		j=0;
		//ktabedit::ktabLog << "[mesure = " << i << "]\n";
		while (it != tabSong.end()) {
			//ktabedit::ktabLog << "[piste = " << j++ << "]\n";
			writeBars((*it)->nbStrings(), *(*it)->bar(i));
			
			++it;
		}
		
		i++;
	}
	
	writeLong(0);
	
	mFile.close();
	
	return true;
}
