/***************************************************************************
 * trackview.cpp: implementation of TrackView 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 "trackview.h"
#include "settings.h"

#include "tabbeateffects.h"
#include "tabchord.h"
#include "tabduration.h"

#include "tabbend.h"
#include "tabdynamic.h"

#include "tabgracenotetransition.h"
#include "tabgracenote.h"

#include "tabexception.h"
#include "tabharmonic.h"
#include "tabslide.h"
#include "tabtrill.h"
#include "tabnoteeffect.h"

#include "tabnote.h"

#include "songkey.h"
#include "timesignature.h"

#include "tabmixtablechanges.h"
#include "tabtimes.h"
#include "tabbarproperties.h"

#include "tabbar.h"

#include "trackmidiproperties.h"
#include "trackproperties.h"

#include "tabtrack.h"

#include "tablyrics.h"
#include "tabproperties.h"

#include "tabsong.h"

#include "trackbarview.h"
#include "trackcursorview.h"
#include "trackpageview.h"
#include "tracksystemview.h"
#include "tracktitleview.h"
#include "tracktuningview.h"
#include "trackwordsmusicview.h"

#include "cm.h"
#include "pixel.h"

QFont TrackView::clefFont;
QFont TrackView::timeFont;

/**
@brief Constructor
*/
TrackView::TrackView(Q3Canvas* canvas, const TabProperties& tabProperties, const TabTrack& tabTrack,
					 int dpi, const Cm& left, const Cm& bottom, const QSize& displayResolution, bool landscapeView)
: canvasView(canvas),
  mTabProperties(tabProperties),
  mTabTrack(tabTrack),
  mDpi(dpi),
  trackResolution(displayResolution),
  landscape(landscapeView),
  mVisible(false),
  leftMargins(left.toPixel()),
  bottomMargins(bottom.toPixel()),
  trackBody(0, 0, 0, 0),
  nbPages(1),
  drawEffects(true),
  drawScore(true)
{
	if (timeFont == QFont()) {
		clefFont = QFont(Settings::timeSignatureFont(), Settings::timeSignatureSize(), Settings::timeSignatureWeight(), Settings::timeSignatureItalic());
		timeFont = QFont(Settings::timeSignatureFont(), Settings::timeSignatureSize(), Settings::timeSignatureWeight(), Settings::timeSignatureItalic());
	}
	
	trackBody = QRect(leftMargins, bottomMargins, trackResolution.width() - (2 * leftMargins), trackResolution.height() - (2 * bottomMargins));
	
	cursorView = new TrackCursorView(canvas);
	cursorView->setVisible(true);
	
	pageList.setAutoDelete(true);
	
	// Size of the first bar
	//
	canvasView->resize(trackResolution.width(), trackResolution.height());
}


/**
@brief Destructor
*/
TrackView::~TrackView()
{
	delete cursorView;
}


/**
@brief Makes the track visible if yes is true, or invisible if yes is false
@param yes show or hide the track
*/
void TrackView::setVisible(bool yes)
{
	mVisible = yes;
	
	if (mVisible) {
		pageList.clear();
		systemList.clear();
		
		trackPos.setX(leftMargins);
		trackPos.setY(bottomMargins);
		
		refreshView(0);
		
		canvasView->update();
	}
}


/**
@brief Shorthand for setVisible(true)
*/
void TrackView::show()
{
	setVisible(true);
}


/**
@brief Shorthand for setVisible(false)
*/
void TrackView::hide()
{
	setVisible(false);
}


TrackBarView * TrackView::barViewCreate(uint nb, uint firstBar)
{
	// Create a new bar viewer
	//
	TrackBarView *barView = new TrackBarView(nb + 1, mTabTrack.bar(nb), mTabTrack, mTabTrack.nbStrings(), mDpi, canvasView);
	
	// Display partition
	//
	barView->displayPartition(drawScore);
	
	// Display the partition clef
	//
	if (firstBar == nb)
		barView->displayPartitionClef(true);

	// Display tablature
	//
	barView->displayTablature(true);
	
	// Display bar number
	//
	barView->displayBarNumber(true);
	
	// Are we drawing guitar Fx
	//
	barView->setDrawEffects(drawEffects);
	
	// Display the word TAB on the first bar of the tablature
	//
	if (nb == 0)
		barView->displayTablatureClef(true, clefFont);
	
	// Display the clef on the beginning of a system
	//
	if (firstBar == nb || nb == 0)
		barView->displayClef(true);
	
	// Display the armor :
	//   - at the beginning
	//   - when the armor change
	//
	if (nb == 0 || (nb > 0 && mTabTrack.bar(nb - 1).key() != mTabTrack.bar(nb).key()))
		barView->displayArmor(true);
	
	// Display the time signature :
	//   - at the beginning
	//   - when the time signature change
	//   - at the beginning of a new system
	//
	if (nb == 0 || (nb > 0 && *dynamic_cast<TimeSignature*>(&mTabTrack.bar(nb - 1)) != *dynamic_cast<TimeSignature*>(&mTabTrack.bar(nb))))
		barView->displayTimeSignature(true, timeFont);
	
	// Display the tempo
	//
	if (nb == 0 || (nb > 0 && mTabTrack.bar(nb - 1).tempo() != mTabTrack.bar(nb).tempo()))
		barView->displayTempo(true);
	
	return barView;
}


void TrackView::drawPages(uint nbPages)
{
	Cm space(0.5, mDpi);
	TrackPageView* trackPage = 0;
	QRect pageSize(leftMargins, nbPages * trackResolution.height() + bottomMargins, 
				   trackResolution.width() - (2 * leftMargins), trackResolution.height() - (2 * bottomMargins));
	
	trackPos.setY(nbPages * trackResolution.height() + bottomMargins + space.toPixel());
	
// 	trackPage = new TrackPageView(mTabProperties, 	mTabTrack, canvasView, pageSize, nbPages + 1, DPI());
	
	trackPage->draw();
	
	pageList.append(trackPage);
}


void TrackView::refreshView(uint from)
{
	TrackPageView *trackPage = 0;
	
	// Width counter
	//
	long width = 0;
	
	// New width
	//
	uint newWidth = 0;
	
	// Minimal size of a bar
	//
	long minimal = 0;
	
	// First bar of the line
	//
	uint firstBar = 0;
	
	// Height of the bars
	//
	uint barHeight = 0;
	
	// List of the bars viewer.
	//
	QPtrList<TrackBarView> barList;
	
	bool greaterBar = false;
	
	kdDebug() << "mTabTrack.count() " << mTabTrack.count() << "\n";
	
	// Draw the page and the page number
	//
// 	trackPage = new TrackPageView(mTabProperties, mTabTrack, canvasView, trackBody, nbPages, DPI());
	
	trackPage->displayTitle(true);
	trackPage->displayWordsAndMusic(true);
	trackPage->displayTuning(true);
	
	trackPage->draw();
	
	trackPos.setX(leftMargins);
	trackPos.setY(trackPage->height());
	
	pageList.append(trackPage);
	
	for (uint i = from; i < mTabTrack.count(); i++) {
		// Create a new bar viewer
		//
		TrackBarView* barView = barViewCreate(i, firstBar);
		
		if (i == 0)
			barHeight = barView->height();
		
		// add the minimal size of the current bar
		//
		width = width + (minimal = barView->minimalSize());
		
		// Special case when the bar is too big to be displayed on a 
		// system so we reduce this bar
		//
		if (minimal > trackBody.width() && barList.count() == 0) {
			barView->reduceToWidth(trackBody.width());
			
			// Append the bar which will be displayed on the system (the current line)
			//
			barList.append(barView);
			
			greaterBar = true;
		}
		
		// Check if it is the last bar
		//
		const bool isLastBar = i + 1 == mTabTrack.count();
		
		// Display all the bars in the system (the line)
		//
		if (width > trackBody.width() || isLastBar) {
			bool addLastBar = false;
			
			// Page Jump
			//
			if (trackPos.y() + barView->height() > nbPages * trackResolution.height() - bottomMargins)
				drawPages(nbPages++);
			
			// Add the last bar if the bar list is empty or if the size of
			// the last bar is smaller enough to be added to the bar list
			//
			//
			if ((isLastBar && barList.count() == 0) || (isLastBar && (int)(barView->minimalSize() + width) < trackBody.width())) {
				// Append the bar which will be displayed on the system (the current line)
				//
				barList.append(barView);
				
				addLastBar = true;
			}
			
			width-=minimal;
			
			// Adapt the bars in the system to use all the space in the system
			//
			if (!isLastBar || (isLastBar && !addLastBar))
				newWidth = (trackBody.width() - width) / barList.count();
			
			if (greaterBar)
				newWidth = 0;
			
// 			TrackSystemView *trackSystem = new TrackSystemView(trackBody, DPI());
// 			trackSystem->setSystem(barList);
// 			systemList.append(trackSystem);
			
// 			trackSystem->draw(trackPos, newWidth, barHeight);
			
			trackPos.setX(leftMargins);
			trackPos.setY(trackPos.y() + barHeight);
			
			barList.clear();
			
			if ((!isLastBar && !greaterBar) || (isLastBar && ! addLastBar))
				delete barView;
			
			newWidth = width = 0;
			firstBar = i;
			
			// Decrease for the clef displaying because the clef changes
			// the length
			//
			if (i + 1 != mTabTrack.count() || (i + 1 == mTabTrack.count() && isLastBar && !addLastBar))
				i--;
			
			if (greaterBar) {
				i++;
				
				greaterBar = false;
			}
			
		} else {
			// Append the bar which will be displayed on the system (the current line)
			//
			barList.append(barView);
		}
	}
	
	canvasView->resize(trackResolution.width() , nbPages * trackResolution.height());
}


void TrackView::refreshCursor(const TrackPos& cursorPos)
{
	if (const TrackBarView* barView = systemList.atBar(cursorPos.bar()))
		cursorView->refresh(barView->positionTablature(cursorPos));
	
	canvasView->update();
}


QPoint TrackView::cursorPosition() const
{
	return cursorView->position();
}


// Click event
//
TrackPos TrackView::mousePressEvent(const QPoint& position)
{
/*	QPtrListIterator<TrackBarView> it(barViewList);
	TrackBarView *barView;
	TrackPos trackPos;
	uint bar = 0;
	
	// Check in what bar the user click
	//
	while ((barView = it.current()) != 0) {
		++it;
	
		if (position.x() > barView->positionViewer().x() &&
		    position.x() < (int)(barView->positionViewer().x() + barView->size()) && 
		    position.y() > barView->positionViewer().y() && 
		    position.y() < (int)(barView->positionViewer().y() + barView->height())) {
				break;
			}
		
		bar++;
	}
	
	// The end of the list
	//
	if (barView == 0)
		barView = barViewList.last();
	
	trackPos.setBar(bar);
	trackPos.setTimes(0);
	trackPos.setChord(0);
	
	QPoint point = barView->positionTablature(trackPos);
	
	// Check in what times the user click
	//
	while (point.isNull() == false && position.x() > point.x()) {
		trackPos.incTimes();
		point = barView->positionTablature(trackPos);
	}
	
	// The point passes the position so
	// decrease the times
	//
	if (position.x() < point.x())
		trackPos.decTimes();
	
	refreshCursor(trackPos);*/
	
// 	return trackPos;
}


/**
@brief Shows or hides the score
@param b Show the score or not
@param cursorPos Position of the cursor
*/
void TrackView::setViewScore(bool b, const TrackPos& cursorPos)
{
	if (b == drawScore)
		return;
	drawScore = b;
	pageList.clear();
	systemList.clear();
	trackPos.setX(leftMargins);
	trackPos.setY(bottomMargins);
	refreshView(0);
	refreshCursor(cursorPos);
	canvasView->update();
}


bool TrackView::viewScore() const
{
	return drawScore;
}
