//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "GlobalUnit.h"
#include "Compose.h"
#include "MidiTrack.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)

/***************************************************************************************/
// 全局变量
/***************************************************************************************/
TMidi				g_Midi;										//MIDI轨道对象
TMidiTrackList&		g_aMidiTracks = g_Midi.MidiTrackList();	//MIDI轨道列表
int					g_iSelMidiTrack = -1;						//当前处理的MIDI轨道

/***************************************************************************************/
// Midi类函数
void TMidi::ClearMidiTracks()
{
	ClearMidiTrackNotes();

	while( !m_lstMidiTracks.empty() ){
		delete m_lstMidiTracks.back();
		m_lstMidiTracks.pop_back();
	}

	while( !m_aChnoUsed.empty() ){
		delete m_aChnoUsed.back();
		m_aChnoUsed.pop_back();
	}

	m_nMidiFile = 0;
}
//---------------------------------------------------------------------------

void TMidi::ClearMidiTrackNotes()
{
	for( int i=0; i<m_lstMidiTracks.size(); i++ ){
		TMidiTrack* pTrack = m_lstMidiTracks[i];

		while( !pTrack->aNotes.empty() ){
			delete pTrack->aNotes.back();
			pTrack->aNotes.pop_back();
		}

		pTrack->ptMin = 0xFF;
		pTrack->ptMax = 0;
	}
}
//---------------------------------------------------------------------------

TMidiTrack* TMidi::FindMidiTrack( BYTE file, BYTE chno )
{
	for( int i=0; i<m_lstMidiTracks.size(); i++ ){
		if( m_lstMidiTracks[i]->file==file && m_lstMidiTracks[i]->chno==chno ){
			return m_lstMidiTracks[i];
		}
	}

	return NULL;
}
//---------------------------------------------------------------------------

int TMidi::GetMidiTrackIndex( BYTE file, BYTE chno )
{
	for( int i=0; i<m_lstMidiTracks.size(); i++ ){
		if( m_lstMidiTracks[i]->file==file
						&& m_lstMidiTracks[i]->chno==chno ){
			return i;
		}
	}

	return -1;
}
//---------------------------------------------------------------------------

void TMidi::SortMidiTracks()
{
/*	ClearMidiTracks();

	if( !g_pUseStyle )
		return;

	TStyleItem* pStyleItem = g_pUseStyle;
*/
	for( int i=0; i<g_Opus.StageCount(); i++ ){
		TOpusStage* pStage = g_lstOpusStages[i];

		for( int j=0; j<pStage->aTracks.size(); j++ ){
			TTrack* pTrk = pStage->aTracks[j];

//			BYTE ch = ( pTrk->iBank==0xFF || pTrk->iBank<0x80 ) ? pTrk->iChanNo
//																: pTrk->iChanSpec;

			TMidiTrack* pMT = GetValidMidiTrack( i, pTrk->iFile, pTrk->iChanSpec,
															pTrk->iBank, pTrk->iProg );

			if( pTrk->iChanSpec!=pMT->chno || pTrk->iFile!=pMT->file ){
				pTrk->iFile = pMT->file;
				pTrk->iChanSpec = pMT->chno;
			}
		}
	}

#ifdef _DEBUG
	for( int k=0; k<m_lstMidiTracks.size(); k++ ){
		TMidiTrack* pChannel = m_lstMidiTracks[k];

		DebugPrint( "\nchan %d bank=%d chno=%d prog=%d\n", k, pChannel->bank, pChannel->chno, pChannel->prog );
		AnsiString strStages = "      stages:";
		for( int m=0; m<pChannel->aStages.size(); m++ ){
			strStages += " ";
			strStages += IntToStr( pChannel->aStages[m] );
		}

		DebugPrint( "%s\n", strStages.c_str() );
	}

	for( int m=0; m<m_aChnoUsed.size(); m++ ){
		AnsiString strStages = "\nbank=";
		strStages += IntToStr( m_aChnoUsed[m]->bank );

		strStages += "  used:";

		for( int n=0; n<16; n++ ){
			if( m_aChnoUsed[m]->chno & (1<<n) ){
				strStages += " ";
				strStages += IntToStr( n );
			}
		}

		strStages += "\n";

		DebugPrint( "%s\n", strStages.c_str() );
	}
#endif
}
//---------------------------------------------------------------------------

TChnoUsed* TMidi::GetChnoUsed( BYTE file, BYTE bank )
{
	TChnoUsed* pCU = NULL;
	for( int i=0; i<m_aChnoUsed.size(); i++ ){
		TChnoUsed* pChUs = m_aChnoUsed[i];

		if( pChUs->file==file && pChUs->bank==bank ){
			pCU = pChUs;
			break;
		}
	}

	if( !pCU ){
		pCU = new TChnoUsed( bank, file );
		AddChnoUsed( pCU );
	}

	return pCU;
}
//---------------------------------------------------------------------------

BYTE TMidi::GetValidFile( BYTE bank, BYTE chno )
{
	WORD ch = ( 1<<chno );
	TChnoUsed* pCU = NULL;
	for( int i=0; i<m_aChnoUsed.size(); i++ ){
		TChnoUsed* pChUs = m_aChnoUsed[i];

		if( pChUs->bank==bank && !(pChUs->chno & ch) ){
			pCU = pChUs;
			break;
		}
	}

	if( !pCU ){
		pCU = new TChnoUsed( bank, m_nMidiFile++ );
		AddChnoUsed( pCU );
	}

	pCU->chno |= ch;
	return pCU->file;
}
//---------------------------------------------------------------------------

TMidiTrack* TMidi::GetValidMidiTrack( int iStage, BYTE file, BYTE chno, BYTE bank, BYTE prog )
{
	vector<int> aStages;
	aStages.push_back( iStage );

	return GetValidMidiTrack( aStages, file, chno, bank, prog );
}
//---------------------------------------------------------------------------

TMidiTrack* TMidi::GetValidMidiTrack( vector<int>& lstStages, BYTE file, BYTE chno, BYTE bank, BYTE prog )
{
	TMidiTrack* pMidiTrack = NULL;
	vector<TMidiTrack*> aValidTracks;
	bool bVSTi = bank!=0xFF && bank>=0x80;

	if( file!=0xFF ){
		TMidiTrack* pMT = FindMidiTrack( file, chno );

		if( !pMT ){
			pMidiTrack = new TMidiTrack( bank, chno, prog, file );
			AddMidiTrack( pMidiTrack );

			goto EXIT_LINE;
		}
		else if( pMT->bank==bank && (!bVSTi && ( pMT->chno==9 && chno==9
													|| pMT->chno!=9 && chno!=9
														&& pMT->prog==prog ) //GM的鼓通道或相同bank和prog
										|| bVSTi && pMT->chno==chno )  //VSTi的通道相同
								   && pMT->ValidStages( lstStages ) ){
			pMidiTrack = pMT;

			goto EXIT_LINE;
		}
	}

	for( int i=0; i<m_lstMidiTracks.size(); i++ ){
		TMidiTrack* pMT = m_lstMidiTracks[i];

		if( bank==pMT->bank && (!bVSTi && ( pMT->chno==9 && chno==9
													|| pMT->chno!=9 && chno!=9
														&& pMT->prog==prog ) //GM的鼓通道或相同bank和prog
										|| bVSTi && pMT->chno==chno ) //VSTi的通道相同
							&& pMT->ValidStages( lstStages ) ){
			pMidiTrack = pMT;

			goto EXIT_LINE;
		}
	}

	//未找到有效MidiTrack，新建
	for( int i=0; i<m_aChnoUsed.size(); i++ ){
		if( m_aChnoUsed[i]->bank==bank ){
			TChnoUsed* pCU = m_aChnoUsed[i];

			if( bVSTi && ( pCU->chno & (1<<chno) )) //VSTi不重设chno
				continue;

			if( chno==9 && !(pCU->chno & 0x200)	|| chno!=9 && (pCU->chno & 0xFDFF)!=0xFDFF ){
				if( pCU->chno & (1<<chno) ){
					for( int n=0; n<16; n++ ){
						if( n!=9 && !(pCU->chno & (1<<n)) ){
							chno = n;
							break;
						}
					}
				}

				pMidiTrack = new TMidiTrack( bank, chno, prog, pCU->file );
				AddMidiTrack( pMidiTrack );

				goto EXIT_LINE;
			}
		}
	}

EXIT_LINE:
	if( !pMidiTrack ){
		pMidiTrack = new TMidiTrack( bank, chno, prog, m_nMidiFile++ );
		AddMidiTrack( pMidiTrack );
	}

	for( int i=0; i<lstStages.size(); i++ ){
		pMidiTrack->AddStage( lstStages[i] );
	}

	return pMidiTrack;
}
//---------------------------------------------------------------------------

void TMidi::AddMidiTrack( TMidiTrack* pCh, int iInd )
{
	if( iInd>=0 && iInd<m_lstMidiTracks.size() ){
		m_lstMidiTracks.insert( m_lstMidiTracks.begin()+iInd, pCh );
	}
	else{
		m_lstMidiTracks.push_back( pCh );
	}

	if( m_nMidiFile<=pCh->file )
		m_nMidiFile = pCh->file+1;

	TChnoUsed* pCU = GetChnoUsed( pCh->file, pCh->bank );
	pCU->chno |= ( 1 << pCh->chno );
}
//---------------------------------------------------------------------------

void TMidi::AddChnoUsed( TChnoUsed* pCU )
{
	int m = 0;
	for( ; m<m_aChnoUsed.size(); m++ ){
		if( (char)m_aChnoUsed[m]->bank > (char)pCU->bank ){
			m_aChnoUsed.insert( m_aChnoUsed.begin()+m, pCU );
			break;
		}
	}

	if( m==m_aChnoUsed.size() )
		m_aChnoUsed.push_back( pCU );
}
//---------------------------------------------------------------------------

void TMidi::DeleteMidiTrack( TMidiTrack* pCh )
{
	for( int i=0; i<m_lstMidiTracks.size(); i++ ){
		if( pCh==m_lstMidiTracks[i] ){
			TChnoUsed* pCU = GetChnoUsed( pCh->file, pCh->bank );
			pCU->chno &= ~( 1 << pCh->chno );
			if( pCU->chno==0 )
				DeleteChnoUsed( pCU );

			delete pCh;
			m_lstMidiTracks.erase( m_lstMidiTracks.begin() + i );

			break;
		}
	}
}
//---------------------------------------------------------------------------

void TMidi::DeleteChnoUsed( TChnoUsed* pCU )
{
	for( int i=0; i<m_aChnoUsed.size(); i++ ){
		if( pCU==m_aChnoUsed[i] ){
			delete pCU;
			m_aChnoUsed.erase( m_aChnoUsed.begin() + i );

			break;
		}
	}
}
//---------------------------------------------------------------------------

void TMidi::CutStage( int iStage )
{
	for( int i=0; i<m_lstMidiTracks.size(); i++ ){
		TMidiTrack* pMT = m_lstMidiTracks[i];

		for( int j=pMT->aStages.size()-1; j>=0; j-- ){
			if( pMT->aStages[j]>iStage )
				pMT->aStages[j]++;
			else if( pMT->aStages[j]==iStage ){
				pMT->aStages.insert( pMT->aStages.begin()+j+1, iStage+1 );
				break;
			}
			else
				break;
		}
	}
}
//---------------------------------------------------------------------------

void TMidi::InsertStage( int iStage )
{
	for( int i=0; i<m_lstMidiTracks.size(); i++ ){
		TMidiTrack* pMT = m_lstMidiTracks[i];

		for( int j=pMT->aStages.size()-1; j>=0; j-- ){
			if( pMT->aStages[j]>=iStage )
				pMT->aStages[j]++;
			else
				break;
		}
	}
}
//---------------------------------------------------------------------------

void TMidi::RemoveStage( int iStage, bool bSort )
{
	for( int i=0; i<m_lstMidiTracks.size(); i++ ){
		TMidiTrack* pMT = m_lstMidiTracks[i];

		for( int j=pMT->aStages.size()-1; j>=0; j-- ){
			if( pMT->aStages[j]>iStage ){
				if( bSort )
					pMT->aStages[j]--;
			}
			else if( pMT->aStages[j]==iStage )
				pMT->aStages.erase( pMT->aStages.begin()+j );
			else
				break;
		}
	}
}
//---------------------------------------------------------------------------

int TMidi::ClearEmptyTrack()
{
	int iFirstEmpty = -1;

	for( int i=m_lstMidiTracks.size()-1; i>=0; i-- ){
		if( m_lstMidiTracks[i]->aStages.empty() ){
			TMidiTrack* pMT = m_lstMidiTracks[i];
			TChnoUsed* pCU = GetChnoUsed( pMT->file, pMT->bank );
			pCU->chno &= ~( 1 << pMT->chno );
			if( pCU->chno==0 )
				DeleteChnoUsed( pCU );

			delete pMT;
			m_lstMidiTracks.erase( m_lstMidiTracks.begin() + i );

			iFirstEmpty = i;
		}
	}

	return iFirstEmpty;
}
//---------------------------------------------------------------------------

void TMidi::ChangMidiChno( int iMT, BYTE fiNew, BYTE chNew )
{
	TMidiTrack* pMT = m_lstMidiTracks[iMT];

	for( int i=0; i<pMT->aStages.size(); i++ ){
		TOpusStage* pStage = g_lstOpusStages[pMT->aStages[i]];
		for( int j=0; j<pStage->aTracks.size(); j++ ){
			TTrack* pTrack = pStage->aTracks[j];
			if( pTrack->iFile==pMT->file && pTrack->iChanSpec==pMT->chno ){
				pTrack->iFile = fiNew;
				pTrack->iChanSpec = chNew;
				break;
			}
		}
	}

	TChnoUsed* pCU = g_Midi.GetChnoUsed( pMT->file, pMT->bank );
	pCU->chno &= ~( 1 << pMT->chno );
	pMT->file = fiNew;
	pMT->chno = chNew;
}
//---------------------------------------------------------------------------

void TMidi::ChangeNotesVolume( int iMT, int iSBar, int iEBar, BYTE volume )
{
	TMidiTrack* pMT = m_lstMidiTracks[iMT];
	int	iTickS = iSBar * POINTS_PER_BAR,
		iTickE = iEBar * POINTS_PER_BAR;

	for( int i=0; i<pMT->aNotes.size(); i++ ){
		TMidiNote* pNote = pMT->aNotes[i];
		if( pNote->tkOn < pNote->tkOff && pNote->tkOn < iTickE && pNote->tkOff > iTickS ){
			pNote->volume = volume;
		}
	}
}
//---------------------------------------------------------------------------

TColor u_clrNote[64];
void TMidi::InitNoteColor()
{
	int  clrSeed[2][3] = {{52,101,100},{162,180,180}};

	int r0 = clrSeed[0][0],
		r1 = clrSeed[1][0],
		g0 = clrSeed[0][1],
		g1 = clrSeed[1][1],
		b0 = clrSeed[0][2],
		b1 = clrSeed[1][2];
	double dR = (r1-r0) / 64.0,
		   dG = (g1-g0) / 64.0,
		   dB = (b1-b0) / 64.0;

	u_clrNote[63] = RGB( r0, g0, b0 );

	for (int i=62; i>0; i--){
	   r0 += dR;
	   g0 += dG;
	   b0 += dB;

	   u_clrNote[i] = RGB( r0, g0, b0 );
	}

	u_clrNote[0] = RGB( r1, g1, b1 );
}
//---------------------------------------------------------------------------

TColor TMidi::GetNoteColor( int vol )
{
	return u_clrNote[vol>>1];
}
//---------------------------------------------------------------------------

void TMidi::GetSpecBanks( vector<BYTE>& lstBank )
{
	for( int i=0; i<m_lstMidiTracks.size(); i++ ){
		TMidiTrack* pMT = m_lstMidiTracks[i];
		if( pMT->bank!=0xFF ){
			bool bAdd = true;

			for( int b=0; b<lstBank.size(); b++ ){
				if( pMT->bank==lstBank[b] ){
					bAdd = false;
					break;
				}
			}

			if( bAdd )
				lstBank.push_back( pMT->bank );
		}
	}
}
//---------------------------------------------------------------------------

void TMidi::SetSpecBanks( BANKMAPLIST& lstMap )
{
	for( int i=0; i<m_lstMidiTracks.size(); i++ ){
		TMidiTrack* pMT = m_lstMidiTracks[i];
		if( pMT->bank!=0xFF ){
			for( int b=0; b<lstMap.size(); b++ ){
				TBankMap* pMap = lstMap[b];

				if( pMT->bank==pMap->iBankOld ){
					pMT->bank = pMap->iBankNew;
					break;
				}
			}
		}
	}
}
//---------------------------------------------------------------------------

void TMidi::WriteProj( FILE* pFile )
{
	int num = m_lstMidiTracks.size();
	fwrite( &num, sizeof(int), 1, pFile );

	for( int i=0; i<num; i++ ){
		TMidiTrack* pMT = m_lstMidiTracks[i];

		fwrite( &pMT->state, sizeof(BYTE), 1, pFile );
		fwrite( &pMT->volume, sizeof(BYTE), 1, pFile );
		fwrite( &pMT->pan, sizeof(char), 1, pFile );
		fwrite( &pMT->gain, sizeof(char), 1, pFile );

		fwrite( &pMT->bank, sizeof(BYTE), 1, pFile );
		fwrite( &pMT->chno, sizeof(BYTE), 1, pFile );
		fwrite( &pMT->prog, sizeof(BYTE), 1, pFile );
		fwrite( &pMT->file, sizeof(BYTE), 1, pFile );
	}
}
// ---------------------------------------------------------------------------

int TMidi::ReadProj( FILE* pFile, DWORD dwVersion )
{
	int num = 0;
	fread( &num, sizeof(int), 1, pFile );

	for( int i=0; i<num; i++ ){
		TMidiTrack* pMT = new TMidiTrack;

		fread( &pMT->state, sizeof(BYTE), 1, pFile );
		fread( &pMT->volume, sizeof(BYTE), 1, pFile );
		fread( &pMT->pan, sizeof(char), 1, pFile );
		fread( &pMT->gain, sizeof(char), 1, pFile );

		fread( &pMT->bank, sizeof(BYTE), 1, pFile );
		fread( &pMT->chno, sizeof(BYTE), 1, pFile );
		fread( &pMT->prog, sizeof(BYTE), 1, pFile );
		fread( &pMT->file, sizeof(BYTE), 1, pFile );

		AddMidiTrack( pMT );
	}

	return 0;
}
// ---------------------------------------------------------------------------

