/***************************************************************************
 * tabtrack.cpp
 *
 * This file is part of KTabEdit, a KDE tabulature editor
 *
 * copyright (C) 2006- the KTabEdit 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 "tabtrack.h"
#include "util/assert.h"

#include "tabbar.h"
#include "tabnote.h"
#include "tabtimes.h"
#include "trackpos.h"

TabTrack::TabTrack()
: mTabBar(),
  mProperties(0x0)
{
	mProperties = new TrackProperties;
}

TabTrack::TabTrack(TrackMode tm, const QString& name, int channel, int bank, uchar patch, char string, char frets)
: mTabBar(),
  mProperties(0x0)
{
	const uchar standtune[6] = {40, 45, 50, 55, 59, 64};
	
	mProperties = new TrackProperties(tm, name, channel, bank, patch, string, frets);
	
	for (int i = 0; i < string; i++)
		setTune(i, standtune[i]);
}

TabTrack::~TabTrack()
{
	for (int i = 0; i < mTabBar.size(); i++)
		delete mTabBar[i];
	
	if (hasProperties() == true)
		delete properties();
}

void TabTrack::clone(const TabTrack* track)
{
	for (int i = 0; i < mTabBar.size(); i++)
		delete mTabBar[i];
	
	if (hasProperties() == true)
		delete properties();
	
	mTabBar.clear();
	
	TabBar* bar;
	for (int i = 0; i < track->mTabBar.size(); i++) {
		bar = new TabBar(0);
		bar->clone(track->mTabBar[i]);
		
		mTabBar.push_back(bar);
	}
	
	mProperties = new TrackProperties();
	if (track->hasProperties() == true)
		mProperties->clone(track->properties());
}

void TabTrack::addBar(TabBar* bar)
{
	PRECONDITION( bar != 0 );
	
	mTabBar.push_back(bar);
}

void TabTrack::insertBar(int barNumber, TabBar* bar)
{
	PRECONDITION( bar != 0 );
	PRECONDITION( barNumber <= mTabBar.size() );
	
	mTabBar.insert(barNumber, bar);
}

void TabTrack::insertEmptyBar(const TrackPos& position)
{
	PRECONDITION( position.bar() <= mTabBar.size() );
	
	insertEmptyBar(position.bar());
}

void TabTrack::insertEmptyBar(int barNumber)
{
	PRECONDITION( barNumber <= mTabBar.size() );
	
	TabBar* tabBar = new TabBar(0);
	TabTime* tabTime = new TabTime(0);
	
	tabBar->setNumerator(numerator());
	tabBar->setDenominator(denominator());
	
	tabBar->insertTime(tabTime);
	
	insertBar(barNumber, tabBar);
}

TabBar* TabTrack::bar(int barNumber) const
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	return mTabBar[barNumber];
}

void TabTrack::setBar(int barNumber, TabBar* bar)
{
	PRECONDITION( bar != 0 );
	PRECONDITION( barNumber < mTabBar.size() );
	
	delete mTabBar[barNumber];
	
	mTabBar[barNumber] = bar;
}

void TabTrack::deleteBar(int barNumber)
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	delete mTabBar[barNumber];
	
	mTabBar.removeAt(barNumber);
}

uint TabTrack::barSize(int barNumber) const
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	return bar(barNumber)->size();
}

uint TabTrack::numeratorOfBar(int barNumber) const
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	return bar(barNumber)->numerator();
}

uint TabTrack::denominatorOfBar(int barNumber) const
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	return bar(barNumber)->denominator();
}

bool TabTrack::areTimesSignaturesEquals(int barNumber1, int barNumber2) const
{
	PRECONDITION( barNumber1 < mTabBar.size() && barNumber2 < mTabBar.size() );
	
	return bar(barNumber1)->numerator() == bar(barNumber2)->numerator() && bar(barNumber1)->denominator() == bar(barNumber2)->denominator();
}

void TabTrack::setTime(const TrackPos& position, TabTime* tabTime)
{
	PRECONDITION( tabTime != 0 );
	PRECONDITION( position.bar() < mTabBar.size() );
	
	setTime(position.bar(), position.times(), tabTime);
}

void TabTrack::setTime(int barNumber, uint timeNumber, TabTime* tabTime)
{
	PRECONDITION( tabTime != 0 );
	PRECONDITION( barNumber < mTabBar.size() );
	
	TabBar* tabBar = bar(barNumber);
	tabBar->setTime(timeNumber, tabTime);
}

void TabTrack::insertTime(const TrackPos& position, TabTime* tabTime)
{
	PRECONDITION( tabTime != 0 );
	PRECONDITION( position.bar() < mTabBar.size() );
	
	insertTime(position.bar(), tabTime);
}

void TabTrack::insertTime(int barNumber, TabTime* tabTime)
{
	PRECONDITION( tabTime != 0 );
	PRECONDITION( barNumber < mTabBar.size() );
	
	TabBar* tabBar = bar(barNumber);
	tabBar->insertTime(tabTime);
}

void TabTrack::deleteTime(int barNumber, uint timeNumber)
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	bar(barNumber)->deleteTime(timeNumber);
}

void TabTrack::deleteTime(const TrackPos& position)
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	deleteTime(position.bar(), position.times());
}

TabTime* TabTrack::time(const TrackPos& position) const
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	return time(position.bar(), position.times());
}

TabTime* TabTrack::time(int barNumber, uint timeNumber) const
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	return bar(barNumber)->time(timeNumber);
}

void TabTrack::setTimeDuration(const TrackPos& position, Duration duration)
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	setTimeDuration(position.bar(), position.times(), duration);
}

void TabTrack::setTimeDuration(int barNumber, uint timeNumber, Duration duration)
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	TabBar* tabBar = bar(barNumber);
	tabBar->setTimeDuration(timeNumber, duration);
}

Duration TabTrack::timeDuration(const TrackPos& position) const
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	TabBar* tabBar = bar(position.bar());
	return tabBar->timeDuration(position.times());
}

Duration TabTrack::timeDuration(int barNumber, uint timeNumber) const
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	TabBar* tabBar = bar(barNumber);
	return tabBar->timeDuration(timeNumber);
}

bool TabTrack::isEmptyTime(const TrackPos& position) const
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	return isEmptyTime(position.bar(), position.times());
}

bool TabTrack::isEmptyTime(int barNumber, uint timeNumber) const
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	return bar(barNumber)->isEmptyTime(timeNumber);
}

void TabTrack::setRest(int barNumber, uint timeNumber, bool b)
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	bar(barNumber)->setRest(timeNumber, b);
}

void TabTrack::setRest(const TrackPos& position, bool b)
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	setRest(position.bar(), position.times(), b);
}

void TabTrack::setDotted(int barNumber, uint timeNumber, bool b)
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	bar(barNumber)->setDotted(timeNumber, b);
}

void TabTrack::setDotted(const TrackPos& position, bool b)
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	setDotted(position.bar(), position.times(), b);
}

void TabTrack::setNote(const TrackPos& position, TabNote* tabNote)
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	setNote(position.bar(), position.times(), position.chord(), tabNote);
}

void TabTrack::setNote(int barNumber, uint timeNumber, uint stringNumber, TabNote* tabNote)
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	bar(barNumber)->setNote(timeNumber, stringNumber, tabNote);
}

TabNote* TabTrack::note(const TrackPos& position) const
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	return note(position.bar(), position.times(), position.chord());
}

TabNote* TabTrack::note(int barNumber, uint timeNumber, uint stringNumber) const
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	return bar(barNumber)->note(timeNumber, stringNumber);
}

void TabTrack::deleteNote(int barNumber, uint timeNumber, uint stringNumber)
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	bar(barNumber)->deleteNote(timeNumber, stringNumber);
}

void TabTrack::deleteNote(const TrackPos& position)
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	deleteNote(position.bar(), position.times(), position.chord());
}

void TabTrack::setFret(const TrackPos& position, uint fret)
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	setFret(position.bar(), position.times(), position.chord(), fret);
}

void TabTrack::setFret(int barNumber, uint timeNumber, uint stringNumber, uint fret)
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	bar(barNumber)->setFret(timeNumber, stringNumber, fret);
}

char TabTrack::fret(const TrackPos& position) const
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	return fret(position.bar(), position.times(), position.chord());
}

char TabTrack::fret(int barNumber, uint timeNumber, uint stringNumber) const
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	return bar(barNumber)->fret(timeNumber, stringNumber);
}

void TabTrack::deleteFret(int barNumber, uint timeNumber, uint stringNumber)
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	bar(barNumber)->deleteFret(timeNumber, stringNumber);
}

void TabTrack::deleteFret(const TrackPos& position)
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	deleteFret(position.bar(), position.times(), position.chord());
}

bool TabTrack::isEmptyNote(int barNumber, uint time, uint note) const
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	return bar(barNumber)->isEmptyNote(time, note);
}

bool TabTrack::isEmptyNote(const TrackPos& position) const
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	return bar(position.bar())->isEmptyNote(position.times(), position.chord());
}

bool TabTrack::isTieNote(int barNumber, uint time, uint string) const
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	return bar(barNumber)->isTieNote(time, string);
}

bool TabTrack::isRest(int barNumber, uint time) const
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	return bar(barNumber)->isRestTime(time);
}

bool TabTrack::isRest(const TrackPos& position) const
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	return isRest(position.bar(), position.times());
}

bool TabTrack::isDotted(int barNumber, uint time) const
{
	PRECONDITION( barNumber < mTabBar.size() );
	
	return bar(barNumber)->isDotted(time);
}

bool TabTrack::isDotted(const TrackPos& position) const
{
	PRECONDITION( position.bar() < mTabBar.size() );
	
	return isDotted(position.bar(), position.times());
}

bool TabTrack::isLastBar(uint bar) const
{
	return (bar == size());
}

void TabTrack::setProperties(TrackProperties* properties)
{
	PRECONDITION( properties != 0x0 );
	
	if (hasProperties() == true)
		delete mProperties;
	
	mProperties = properties;
}

TrackProperties* TabTrack::properties() const
{
	PRECONDITION( mProperties != 0x0 );
	
	return mProperties;
}

bool TabTrack::hasProperties() const
{
	return (mProperties != 0x0);
}

void TabTrack::setMode(TrackMode mode)
{
	properties()->setMode(mode);
}

TrackMode TabTrack::mode() const
{
	return properties()->mode();
}

void TabTrack::setName(const QString &string)
{
	properties()->setName(string);
}

QString TabTrack::name() const
{
	return properties()->name();
}

void TabTrack::setColor(const QColor& c)
{
	properties()->setColor(c);
}

QColor TabTrack::color() const
{
	return properties()->color();
}

void TabTrack::setNbStrings(uchar newStringNumber)
{
	int barNumber;
	uint timeNumber, stringNumber;
	uchar oldStrings = nbStrings();
	
	properties()->setNbStrings(newStringNumber);
	
	if (newStringNumber < oldStrings)
		for (barNumber = 0; barNumber < mTabBar.size(); barNumber++)
			for (timeNumber = 0; timeNumber < bar(barNumber)->size(); timeNumber++)
				for (stringNumber = newStringNumber + 1; stringNumber <= oldStrings; stringNumber++)
					note(barNumber, timeNumber, stringNumber)->setEmpty(true);
}

uchar TabTrack::nbStrings() const
{
	return properties()->nbStrings();
}

void TabTrack::setNbFrets(uchar i)
{
	properties()->setNbFrets(i);
}

uchar TabTrack::nbFrets() const
{
	return properties()->nbFrets();
}

void TabTrack::setTune(uchar i, uchar j)
{
	properties()->setTune(i, j);
}

uchar TabTrack::tune(uchar i) const
{
	return properties()->tune(i);
}

void TabTrack::setCapo(uchar c)
{
	properties()->setCapo(c);
}

uchar TabTrack::capo() const
{
	return properties()->capo();
}

void TabTrack::setMidiProperties(TrackMidiProperties* midiProperties)
{
	properties()->setMidiProperties(midiProperties);
}

TrackMidiProperties* TabTrack::midiProperties() const
{
	return properties()->midiProperties();
}

bool TabTrack::hasMidiProperties() const
{
	return properties()->hasMidiProperties();
}

void TabTrack::setPatch(uchar i)
{
	properties()->setPatch(i);
}

uchar TabTrack::patch() const
{
	return properties()->patch();
}

void TabTrack::setChannel(uchar i)
{
	properties()->setChannel(i);
}

uchar TabTrack::channel() const
{
	return properties()->channel();
}

void TabTrack::setChannelEffects(uchar i)
{
	properties()->setChannelEffects(i);
}

uchar TabTrack::channelEffects() const
{
	return properties()->channelEffects();
}

void TabTrack::setMidiPort(int i)
{
	properties()->setMidiPort(i);
}

int TabTrack::midiPort() const
{
	return properties()->midiPort();
}

void TabTrack::setBank(unsigned int i)
{
	properties()->setBank(i);
}

unsigned int TabTrack::bank() const
{
	return properties()->bank();
}

void TabTrack::setVolume(uchar i)
{
	properties()->setVolume(i);
}

uchar TabTrack::volume() const
{
	return properties()->volume();
}

void TabTrack::setBalance(char i)
{
	properties()->setBalance(i);
}

char TabTrack::balance() const
{
	return properties()->balance();
}

void TabTrack::setChorus(uchar i)
{
	properties()->setChorus(i);
}

uchar TabTrack::chorus() const
{
	return properties()->chorus();
}

void TabTrack::setReverb(uchar i)
{
	properties()->setReverb(i);
}

uchar TabTrack::reverb() const
{
	return properties()->reverb();
}

void TabTrack::setPhaser(uchar i)
{
	properties()->setPhaser(i);
}

uchar TabTrack::phaser() const
{
	return properties()->phaser();
}

void TabTrack::setTremolo(uchar i)
{
	properties()->setTremolo(i);
}

uchar TabTrack::tremolo() const
{
	return properties()->tremolo();
}

void TabTrack::setTimeSignature(TimeSignature* timeSignature)
{
	properties()->setTimeSignature(timeSignature);
}

TimeSignature* TabTrack::timeSignature() const
{
	return properties()->timeSignature();
}

bool TabTrack::hasTimeSignature() const
{
	return properties()->hasTimeSignature();
}

void TabTrack::setNumerator(uchar c)
{
	properties()->setNumerator(c);
}

uchar TabTrack::numerator() const
{
	return properties()->numerator();
}

void TabTrack::setDenominator(uchar c)
{
	properties()->setDenominator(c);
}

uchar TabTrack::denominator() const
{
	return properties()->denominator();
}

uint TabTrack::timeLigatures() const
{
	return properties()->timeLigatures();
}

void TabTrack::setDeadNote(const TrackPos& position, bool b)
{
	bar(position.bar())->setDeadNote(position.times(), position.chord(), b);
}

bool TabTrack::isDeadNote(const TrackPos& position) const
{
	return bar(position.bar())->isDeadNote(position.times(), position.chord());
}

bool TabTrack::isAccentuated(const TrackPos& position) const
{
	return bar(position.bar())->isAccentuated(position.times(), position.chord());
}

void TabTrack::setGhostNote(const TrackPos& position, bool b)
{
	bar(position.bar())->setGhostNote(position.times(), position.chord(), b);
}

bool TabTrack::isGhostNote(const TrackPos& position) const
{
	return bar(position.bar())->isGhostNote(position.times(), position.chord());
}

void TabTrack::setTieNote(const TrackPos& position, bool b)
{
	bar(position.bar())->setTieNote(position.times(), position.chord(), b);
}

bool TabTrack::isTieNote(const TrackPos& position) const
{
	return bar(position.bar())->isTieNote(position.times(), position.chord());
}

void TabTrack::setHammerOnPullOff(const TrackPos& position, bool b)
{
	bar(position.bar())->setHammerOnPullOff(position.times(), position.chord(), b);
}

bool TabTrack::isHammerOnPullOff(const TrackPos& position) const
{
	return bar(position.bar())->isHammerOnPullOff(position.times(), position.chord());
}

void TabTrack::setLetRing(const TrackPos& position, bool b)
{
	bar(position.bar())->setLetRing(position.times(), position.chord(), b);
}

bool TabTrack::isLetRing(const TrackPos& position) const
{
	return bar(position.bar())->isLetRing(position.times(), position.chord());
}

bool TabTrack::isDynamic(const TrackPos& position) const
{
	return bar(position.bar())->isDynamic(position.times(), position.chord());
}

bool TabTrack::isFingeringLeft(const TrackPos& position) const
{
	return bar(position.bar())->isFingeringLeft(position.times(), position.chord());
}

bool TabTrack::isFingeringRight(const TrackPos& position) const
{
	return bar(position.bar())->isFingeringRight(position.times(), position.chord());
}

bool TabTrack::isPalmMute(const TrackPos& position) const
{
	return bar(position.bar())->isAccentuated(position.times(), position.chord());
}

bool TabTrack::isStaccato(const TrackPos& position) const
{
	return bar(position.bar())->isAccentuated(position.times(), position.chord());
}

bool TabTrack::isTremoloPicking(const TrackPos& position) const
{
	return bar(position.bar())->isTremoloPicking(position.times(), position.chord());
}

bool TabTrack::isHarmonic(const TrackPos& position) const
{
	return bar(position.bar())->isHarmonic(position.times(), position.chord());
}

bool TabTrack::isGraceNote(const TrackPos& position) const
{
	return bar(position.bar())->isGraceNote(position.times(), position.chord());
}

TabBend* TabTrack::bend(const TrackPos& position) const
{
	return bar(position.bar())->bend(position.times(), position.chord());
}

void TabTrack::setBend(const TrackPos& position, TabBend* bend) const
{
	bar(position.bar())->setBend(position.times(), position.chord(), bend);
}

bool TabTrack::isBend(const TrackPos& position) const
{
	return bar(position.bar())->isBend(position.times(), position.chord());
}

bool TabTrack::isTrill(const TrackPos& position) const
{
	return bar(position.bar())->isTrill(position.times(), position.chord());
}

bool TabTrack::isRasgueado(const TrackPos& position) const
{
	return bar(position.bar())->isRasgueado(position.times());
}

bool TabTrack::hasPickStroke(const TrackPos& position) const
{
	return bar(position.bar())->isPickStroke(position.times());
}

bool TabTrack::hasTremolo(const TrackPos& position) const
{
	return bar(position.bar())->isTremolo(position.times());
}

bool TabTrack::isTapping(const TrackPos& position) const
{
	return bar(position.bar())->isTapping(position.times());
}

bool TabTrack::isPopping(const TrackPos& position) const
{
	return bar(position.bar())->isPopping(position.times());
}

bool TabTrack::isSlapping(const TrackPos& position) const
{
	return bar(position.bar())->isSlapping(position.times());
}

bool TabTrack::isStroke(const TrackPos& position) const
{
	return bar(position.bar())->isStroke(position.times());
}

bool TabTrack::isVibrato(const TrackPos& position) const
{
	return bar(position.bar())->isVibrato(position.times());
}

bool TabTrack::isFadeIn(const TrackPos& position) const
{
	return bar(position.bar())->isFadeIn(position.times());
}

bool TabTrack::operator==(const TabTrack& tabTrack) const
{
	bool result;
	
	if (*dynamic_cast<const TrackProperties*>(this) == *dynamic_cast<const TrackProperties*>(&tabTrack))
		result = true;
	else
		result = false;
	
	if (tabTrack.size() != this->size())
		result = false;
	
	const_iterator it1 = tabTrack.mTabBar.begin();
	const_iterator it2 = this->mTabBar.begin();
	for (; result == true && it1 != tabTrack.mTabBar.end(); ++it1, ++it2) {
		if (*it1 == *it2)
			result = false;
	}
	
	if (result == true && hasProperties() == true && tabTrack.hasProperties() == true) {
		if (properties()->operator!=(*tabTrack.properties()))
			result = false;
	} else
		result = false;
	
	return result;
}

bool TabTrack::operator!=(const TabTrack& tabTrack) const
{
	return !(this->operator==(tabTrack));
}
