/*
 * bb_track_container.cpp - model-component of BB-Editor
 *
 * Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * 
 * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
 *
 * 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.
 *
 * 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 (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */


#include "bb_track_container.h"
#include "bb_track.h"
#include "combobox.h"
#include "embed.h"
#include "engine.h"
#include "song.h"



bbTrackContainer::bbTrackContainer() :
	trackContainer(),
	m_bbComboBoxModel( this )
{
	connect( &m_bbComboBoxModel, SIGNAL( dataChanged() ),
			this, SLOT( currentBBChanged() ) );
	// we *always* want to receive updates even in case BB actually did
	// not change upon setCurrentBB()-call
	connect( &m_bbComboBoxModel, SIGNAL( dataUnchanged() ),
			this, SLOT( currentBBChanged() ) );
}




bbTrackContainer::~bbTrackContainer()
{
}




bool bbTrackContainer::play( midiTime _start, fpp_t _frames,
							f_cnt_t _offset,
							Sint16 _tco_num )
{
	bool played_a_note = false;
	if( lengthOfBB( _tco_num ) <= 0 )
	{
		return( false );
	}

	_start = _start % ( lengthOfBB( _tco_num ) * midiTime::ticksPerTact() );

	trackList tl = tracks();
	for( trackList::iterator it = tl.begin(); it != tl.end(); ++it )
	{
		if( ( *it )->play( _start, _frames, _offset, _tco_num ) )
		{
			played_a_note = true;
		}
	}

	return( played_a_note );
}




void bbTrackContainer::updateAfterTrackAdd()
{
	if( numOfBBs() == 0 && !engine::getSong()->isLoadingProject() )
	{
		engine::getSong()->addBBTrack();
	}

	// make sure, new track(s) have TCOs for every beat/bassline
	for( int i = 0; i < qMax<int>( 1, numOfBBs() ); ++i )
	{
		createTCOsForBB( i );
	}
}




tact_t bbTrackContainer::lengthOfBB( int _bb )
{
	midiTime max_length = midiTime::ticksPerTact();

	const trackList & tl = tracks();
	for( trackList::const_iterator it = tl.begin(); it != tl.end(); ++it )
	{
		max_length = qMax( max_length,
					( *it )->getTCO( _bb )->length() );
	}

	return( max_length.nextFullTact() );
}




int bbTrackContainer::numOfBBs() const
{
	return( engine::getSong()->countTracks( track::BBTrack ) );
}




void bbTrackContainer::removeBB( int _bb )
{
	trackList tl = tracks();
	for( trackList::iterator it = tl.begin(); it != tl.end(); ++it )
	{
		delete ( *it )->getTCO( _bb );
		( *it )->removeTact( _bb * DefaultTicksPerTact );
	}
	if( _bb <= currentBB() )
	{
		setCurrentBB( qMax( currentBB() - 1, 0 ) );
	}
}




void bbTrackContainer::swapBB( int _bb1, int _bb2 )
{
	trackList tl = tracks();
	for( trackList::iterator it = tl.begin(); it != tl.end(); ++it )
	{
		( *it )->swapPositionOfTCOs( _bb1, _bb2 );
	}
	updateComboBox();
}




void bbTrackContainer::updateBBTrack( trackContentObject * _tco )
{
	bbTrack * t = bbTrack::findBBTrack( _tco->startPosition() /
							DefaultTicksPerTact );
	if( t != NULL )
	{
		t->dataChanged();
	}
}




void bbTrackContainer::fixIncorrectPositions()
{
	trackList tl = tracks();
	for( trackList::iterator it = tl.begin(); it != tl.end(); ++it )
	{
		for( int i = 0; i < numOfBBs(); ++i )
		{
			( *it )->getTCO( i )->movePosition( midiTime( i, 0 ) );
		}
	}
}




void bbTrackContainer::play()
{
	if( engine::getSong()->isPlaying() )
	{
		if( engine::getSong()->playMode() != song::Mode_PlayBB )
		{
			engine::getSong()->stop();
			engine::getSong()->playBB();
		}
		else
		{
			engine::getSong()->pause();
		}
	}
	else if( engine::getSong()->isPaused() )
	{
		engine::getSong()->resumeFromPause();
	}
	else
	{
		engine::getSong()->playBB();
	}

}




void bbTrackContainer::stop()
{
	engine::getSong()->stop();
}




void bbTrackContainer::updateComboBox()
{
	const int cur_bb = currentBB();

	m_bbComboBoxModel.clear();

	for( int i = 0; i < numOfBBs(); ++i )
	{
		bbTrack * bbt = bbTrack::findBBTrack( i );
		m_bbComboBoxModel.addItem( bbt->name() );
	}
	setCurrentBB( cur_bb );
}




void bbTrackContainer::currentBBChanged()
{
	// first make sure, all channels have a TCO at current BB
	createTCOsForBB( currentBB() );

	// now update all track-labels (the current one has to become white,
	// the others gray)
	trackList tl = engine::getSong()->tracks();
	for( trackList::iterator it = tl.begin(); it != tl.end(); ++it )
	{
		if( ( *it )->type() == track::BBTrack )
		{
			( *it )->dataChanged();
		}
	}
}




void bbTrackContainer::createTCOsForBB( int _bb )
{
	if( numOfBBs() == 0 || engine::getSong()->isLoadingProject() )
	{
		return;
	}

	trackList tl = tracks();
	for( int i = 0; i < tl.size(); ++i )
	{
		while( tl[i]->numOfTCOs() < _bb + 1 )
		{
			midiTime position = midiTime( tl[i]->numOfTCOs(), 0 );
			trackContentObject * tco = tl[i]->createTCO( position );
			tco->movePosition( position );
			tco->changeLength( midiTime( 1, 0 ) );
		}
	}
}



#include "moc_bb_track_container.cxx"

