/***************************************************************************
 * tabnotes.cpp: implementation of TabTimes class
 *
 * This file is part of KTabEdit, a KDE tabulature editor
 *
 * copyright (C) 2003-2007 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 "tabtimes.h"
#include "util/assert.h"

#include "tabnote.h"

TabTime::TabTime(uchar time)
: mTime(time),
  mDuration(Quarter),
  mDotted(false),
  mRest(false),
  mTuplet(0),
  mChordFretted(),
  mComment(),
  mChord(false),
  mEffect(0x0),
  mTabChord(0x0)
{
	mEffect = new TabTimeEffect;
	mTabChord = new TabChord;
}

TabTime::~TabTime()
{
	iterator it = mChordFretted.begin();
	
	while (it != mChordFretted.end()) {
		delete it.value();
		++it;
	}
	
	if (hasEffect() == true)
		delete effect();
	
	if (hasChord() == true)
		delete tabChord();
}

void TabTime::clone(const TabTime* time)
{
	PRECONDITION( time != 0x0 );
	
	iterator it = mChordFretted.begin();
	
	while (it != mChordFretted.end()) {
		delete it.value();
		++it;
	}
	
	mChordFretted.clear();
	
	if (hasEffect() == true)
		delete effect();
	
	if (hasChord() == true)
		delete tabChord();
	
	mTime = time->mTime;
	mDuration = time->mDuration;
	mDotted =time->mDotted;
	mRest = time->mRest;
	mTuplet = time->mTuplet;
	mComment = time->mComment;
	mChord = time->mChord;
	
	const_iterator itTime = time->mChordFretted.begin();
	
	TabNote* note;
	while (itTime != time->mChordFretted.end()) {
		note = new TabNote(0);
		note->clone(itTime.value());
		
		mChordFretted[itTime.key()] = note;
		
		++itTime;
	}
	
	mEffect = new TabTimeEffect();
	if (time->hasEffect())
		mEffect->clone(time->effect());
	
	mTabChord = new TabChord();
	if (time->hasChord())
		mTabChord->clone(time->mTabChord);
}

uchar TabTime::time() const
{
	return mTime;
}

void TabTime::setString(uchar stringNumber, TabNote* note)
{
	PRECONDITION( note != 0 );
	
	if (isNoteExist(stringNumber)) {
		delete mChordFretted[stringNumber];
	}
	
	mChordFretted[stringNumber] = note;
}

TabNote* TabTime::string(uchar stringNumber) const
{
	const_iterator it = mChordFretted.find(stringNumber);
	TabNote* result;
	
	if (it != mChordFretted.end())
		result = it.value();
	else {
		result = new TabNote(stringNumber);
		mChordFretted[stringNumber] = result;
	}
	
	return result;
}

void TabTime::deleteString(uchar stringNumber)
{
	setString(stringNumber, new TabNote(stringNumber));
}

bool TabTime::isNoteExist(uchar stringNumber) const
{
	const_iterator it = mChordFretted.find(stringNumber);
	
	return (it != mChordFretted.end());
}

void TabTime::setFret(uchar stringNumber, uint fret)
{
	string(stringNumber)->setFret(fret);
}

uchar TabTime::fret(uchar stringNumber) const
{
	return string(stringNumber)->fret();
}

void TabTime::deleteFret(uchar stringNumber)
{
	string(stringNumber)->setEmpty(true);
}

bool TabTime::isNoteEmpty(uchar stringNumber) const
{
	return string(stringNumber)->isEmpty();
}

void TabTime::setDeadNote(uchar stringNumber, bool b)
{
	string(stringNumber)->setDeadNote(b);
}

bool TabTime::isDeadNote(uchar stringNumber) const
{
	return string(stringNumber)->isDeadNote();
}

bool TabTime::isNoteGhostNote(uchar stringNumber) const
{
	return string(stringNumber)->isGhostNote();
}

unsigned int TabTime::size() const
{
	return mChordFretted.size();
}

void TabTime::setDuration(Duration i)
{
	mDuration = i;
}

Duration TabTime::duration() const
{
	return mDuration;
}

double TabTime::realDuration() const
{
	double duration = mDuration;
	
	if (mDotted == true)
		duration = duration + duration / 2.00;
	
	// For the triplet double time duration
	//
	if (mTuplet == 2)
		duration*=2;
	else if (mTuplet > 2 && mTuplet < 8)
		duration*=4;
	else if (mTuplet > 8 && mTuplet < 12)
		duration*=8;
	
	return duration / (mTuplet + 1.0);
}

void TabTime::setRest(bool i)
{
	mRest = i;
}

bool TabTime::isRest() const
{
	return mRest;
}

bool TabTime::isEmpty() const
{
	bool result = true;
	
	for (const_iterator it = mChordFretted.begin(); it != mChordFretted.end() && result == true; ++it)
		if (it.value()->isEmpty() == false)
			result = false;
	
	return result;
}

void TabTime::setDotted(bool i)
{
	mDotted = i;
}

bool TabTime::isDotted() const
{
	return mDotted;
}

void TabTime::setNTuplet(uint i)
{
	mTuplet = i;
}

uint TabTime::nTuplet() const
{
	return mTuplet;
}

void TabTime::setText(const QString& s)
{
	mComment = s;
}

QString TabTime::text() const
{
	return mComment;
}

void TabTime::setChord(bool b)
{
	mChord = b;
}

bool TabTime::isChord() const
{
	return mChord;
}

void TabTime::setEffect(TabTimeEffect* effect)
{
	PRECONDITION( effect != 0x0 );
	
	if (mEffect != 0x0)
		delete mEffect;
	
	mEffect = effect;
}

TabTimeEffect* TabTime::effect() const
{
	PRECONDITION( mEffect != 0x0 );
	
	return mEffect;
}

bool TabTime::hasEffect() const
{
	return (mEffect != 0x0);
}

void TabTime::setRasgueado(bool b)
{
	effect()->setRasgueado(b);
}

bool TabTime::isRasgueado() const
{
	return effect()->isRasgueado();
}

bool TabTime::hasPickStroke() const
{
	return effect()->hasPickStroke();
}

void TabTime::setPickStroke(PickStroke p)
{
	effect()->setPickStroke(p);
}

PickStroke TabTime::pickstroke() const
{
	return effect()->pickstroke();
}

bool TabTime::hasTremolo() const
{
	return effect()->hasTremolo();
}

void TabTime::setTremolo(int i)
{
	effect()->setTremolo(i);
}

int TabTime::tremolo() const
{
	return effect()->tremolo();
}

void TabTime::setTapping(bool b)
{
	effect()->setTapping(b);
}

bool TabTime::isTapping() const
{
	return effect()->isTapping();
}

void TabTime::setPopping(bool b)
{
	effect()->setPopping(b);
}

bool TabTime::isPopping() const
{
	return effect()->isPopping();
}

void TabTime::setSlapping(bool b)
{
	effect()->setSlapping(b);
}

bool TabTime::isSlapping() const
{
	return effect()->isSlapping();
}

void TabTime::setStroke(bool b)
{
	effect()->setStroke(b);
}

bool TabTime::isStroke() const
{
	return effect()->isStroke();
}

void TabTime::setUpStroke(int i)
{
	effect()->setUpStroke(i);
}

int TabTime::upStroke() const
{
	return effect()->upStroke();
}

void TabTime::setDownStroke(int i)
{
	effect()->setDownStroke(i);
}

int TabTime::downStroke() const
{
	return effect()->downStroke();
}

void TabTime::setVibrato(bool b)
{
	effect()->setVibrato(b);
}

bool TabTime::isVibrato() const
{
	return effect()->isVibrato();
}

void TabTime::setWideVibrato(bool b)
{
	effect()->setWideVibrato(b);
}

bool TabTime::isWideVibrato() const
{
	return effect()->isWideVibrato();
}

void TabTime::setNaturalHarmonic(bool b)
{
	effect()->setNaturalHarmonic(b);
}

bool TabTime::isNaturalHarmonic() const
{
	return effect()->isNaturalHarmonic();
}

void TabTime::setArtificialHarmonic(bool b)
{
	effect()->setArtificialHarmonic(b);
}

bool TabTime::isArtificialHarmonic() const
{
	return effect()->isArtificialHarmonic();
}

void TabTime::setFadeIn(bool b)
{
	effect()->setFadeIn(b);
}

bool TabTime::isFadeIn() const
{
	return effect()->isFadeIn();
}

void TabTime::setBend(TabBend* bend)
{
	effect()->setBend(bend);
}

TabBend* TabTime::bend() const
{
	return effect()->bend();
}

void TabTime::setBendType(TabBend::BendType b)
{
	effect()->setBendType(b);
}

TabBend::BendType TabTime::bendType() const
{
	return effect()->bendType();
}

void TabTime::setBendValue(int i)
{
	effect()->setBendValue(i);
}

int TabTime::bendValue() const
{
	return effect()->bendValue();
}

QString TabTime::bendValueToString() const
{
	return effect()->bendValueToString();
}

int TabTime::sizeOfBendPoints() const
{
	return effect()->sizeOfBendPoints();
}

void TabTime::addBendPoint(TabBendPoint* point)
{
	effect()->addBendPoint(point);
}

void TabTime::setBendPoint(uint i, TabBendPoint* point)
{
	effect()->setBendPoint(i, point);
}

void TabTime::removeBendPoint(uint i)
{
	effect()->removeBendPoint(i);
}

TabBendPoint* TabTime::bendPoint(uint i) const
{
	return effect()->bendPoint(i);
}

void TabTime::setTabChord(TabChord* mChord)
{
	PRECONDITION( mChord != 0x0 );
	
	if (mTabChord)
		delete mTabChord;
	
	mTabChord = mChord;
}

TabChord* TabTime::tabChord() const
{
	PRECONDITION( mTabChord != 0x0 );
	
	return mTabChord;
}

bool TabTime::hasChord() const
{
	return (mTabChord != 0x0);
}

void TabTime::setName(const QString& s)
{
	tabChord()->setName(s);
}

QString TabTime::name() const
{
	return tabChord()->name();
}

void TabTime::setSharp(bool b)
{
	tabChord()->setSharp(b);
}

bool TabTime::isSharp() const
{
	return tabChord()->isSharp();
}

void TabTime::setRoot(uchar c)
{
	tabChord()->setRoot(c);
}

uchar TabTime::root() const
{
	return tabChord()->root();
}

void TabTime::setType(uchar c)
{
	tabChord()->setType(c);
}

uchar TabTime::type() const
{
	return tabChord()->type();
}

void TabTime::setNineElevenThirteen(TabChord::ExtentionChord c)
{
	tabChord()->setNineElevenThirteen(c);
}

TabChord::ExtentionChord TabTime::nineElevenThirteen() const
{
	return tabChord()->nineElevenThirteen();
}

void TabTime::setBass(uchar c)
{
	tabChord()->setBass(c);
}

uchar TabTime::bass() const
{
	return tabChord()->bass();
}

void TabTime::setAugmentedDiminished(TabChord::AugmentedDiminished a)
{
	tabChord()->setAugmentedDiminished(a);
}

TabChord::AugmentedDiminished TabTime::augmentedDiminished() const
{
	return tabChord()->augmentedDiminished();
}

void TabTime::setAddedNote(uchar c)
{
	tabChord()->setAddedNote(c);
}

uchar TabTime::addedNote() const
{
	return tabChord()->addedNote();
}

void TabTime::setTonalityFive(uchar c)
{
	tabChord()->setTonalityFive(c);
}

uchar TabTime::tonalityFive() const
{
	return tabChord()->tonalityFive();
}

void TabTime::setTonalityNine(uchar c)
{
	tabChord()->setTonalityNine(c);
}

uchar TabTime::tonalityNine() const
{
	return tabChord()->tonalityNine();
}

void TabTime::setTonalityEleven(uchar c)
{
	tabChord()->setTonalityEleven(c);
}

uchar TabTime::tonalityEleven() const
{
	return tabChord()->tonalityEleven();
}

void TabTime::setBaseFret(long l)
{
	tabChord()->setBaseFret(l);
}

long TabTime::baseFret() const
{
	return tabChord()->baseFret();
}

void TabTime::setFretChord(uchar c, long l)
{
	tabChord()->setFret(c, l);
}

long TabTime::fretChord(uchar c) const
{
	return tabChord()->fret(c);
}

void TabTime::setNumBarres(uchar c)
{
	tabChord()->setNumBarres(c);
}

uchar TabTime::numBarres() const
{
	return tabChord()->numBarres();
}

void TabTime::setBarreFret(uchar c, uchar d)
{
	tabChord()->setBarreFret(c, d);
}

uchar TabTime::barreFret(uchar c) const
{
	return tabChord()->barreFret(c);
}

void TabTime::setBarreStart(uchar c, uchar d)
{
	tabChord()->setBarreStart(c, d);
}

uchar TabTime::barreStart(uchar c) const
{
	return tabChord()->barreStart(c);
}

void TabTime::setBarreEnd(uchar c, uchar d)
{
	tabChord()->setBarreEnd(c, d);
}

uchar TabTime::barreEnd(uchar c) const
{
	return tabChord()->barreEnd(c);
}

void TabTime::setFingering(uchar c, uchar d)
{
	tabChord()->setFingering(c, d);
}

uchar TabTime::fingering(uchar c) const
{
	return tabChord()->fingering(c);
}

void TabTime::setShowDiagram(bool b)
{
	tabChord()->setShowDiagram(b);
}

bool TabTime::isShowDiagram() const
{
	return tabChord()->isShowDiagram();
}

bool TabTime::isAccentuated(uint stringNumber) const
{
	return string(stringNumber)->isAccentuated();
}

void TabTime::setGhostNote(uint stringNumber, bool b)
{
	string(stringNumber)->setGhostNote(b);
}

bool TabTime::isGhostNote(uint stringNumber) const
{
	return string(stringNumber)->isGhostNote();
}

void TabTime::setTieNote(uint stringNumber, bool b)
{
	string(stringNumber)->setTieNote(b);
}

bool TabTime::isTieNote(uint stringNumber) const
{
	return string(stringNumber)->isTieNote();
}

void TabTime::setHammerOnPullOff(uint stringNumber, bool b)
{
	string(stringNumber)->setHammerOnPullOff(b);
}

bool TabTime::isHammerOnPullOff(uint stringNumber) const
{
	return string(stringNumber)->isHammerOnPullOff();
}

void TabTime::setLetRing(uint stringNumber, bool b)
{
	string(stringNumber)->setLetRing(b);
}

bool TabTime::isLetRing(uint stringNumber) const
{
	return string(stringNumber)->isLetRing();
}

bool TabTime::isDynamic(uint stringNumber) const
{
	return string(stringNumber)->hasDynamic();
}

bool TabTime::isFingeringLeft(uint stringNumber) const
{
	return string(stringNumber)->hasFingeringLeft();
}

bool TabTime::isFingeringRight(uint stringNumber) const
{
	return string(stringNumber)->hasFingeringRight();
}

bool TabTime::isPalmMute(uint stringNumber) const
{
	return string(stringNumber)->isPalmMute();
}

bool TabTime::isStaccato(uint stringNumber) const
{
	return string(stringNumber)->isStaccato();
}

bool TabTime::isTremoloPicking(uint stringNumber) const
{
	return string(stringNumber)->hasTremoloPicking();
}

bool TabTime::isHarmonic(uint stringNumber) const
{
	return string(stringNumber)->hasHarmonic();
}

bool TabTime::isGraceNote(uint stringNumber) const
{
	return string(stringNumber)->hasGraceNote();
}

TabBend* TabTime::bend(uint stringNumber) const
{
	return string(stringNumber)->bend();
}

void TabTime::setBend(uint stringNumber, TabBend* bend) const
{
	string(stringNumber)->setBend(bend);
}

bool TabTime::isBend(uint stringNumber) const
{
	return string(stringNumber)->hasBend();
}

bool TabTime::isTrill(uint stringNumber) const
{
	return string(stringNumber)->hasTrill();
}

bool TabTime::operator==(const TabTime& time) const
{
	bool mChordResult;
	bool effectResult;
	
	if (mTabChord == time.mTabChord)
		mChordResult = true;
	else if (mTabChord != 0 && time.mTabChord != 0)
		mChordResult = *mTabChord == *time.mTabChord;
	else
		mChordResult = false;
	
	if (mEffect == time.mEffect)
		effectResult = true;
	else if (mEffect != 0 && time.mEffect != 0)
		effectResult = *mEffect == *time.mEffect;
	else
		effectResult = false;
	
	return (mDuration == time.mDuration && mDotted == time.mDotted &&
			mRest == time.mRest && mTuplet == time.mTuplet &&
			mChordFretted == time.mChordFretted && mComment == time.mComment &&
			mChord == time.mChord && effectResult && mChordResult);
}

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