//---------------------------------------------------------------------------
#include <vcl.h>
#include <Vcl.Imaging.pngimage.hpp>
#pragma hdrstop

#include "GlobalUnit.h"
#include "MidiTrack.h"
#include "TrackPanel.h"
#include "AudioFxFormUnit.h"
#include "VSTPluginFormUnit.h"
#include "Undo.h"

#define LEFT_BOUND 	16
#define ROW_GAP 	10

static const TColor
			CLR_TXT_NOR =  RGB( 156, 170, 189 ),
			CLR_TXT_HOT =  RGB( 201, 214, 229 ),
			clrStateBoard = RGB( 29,31,35 ),
			clrState[5] = {	RGB(186,185,185), RGB(216,215,215),
							RGB(248,235,159), RGB(250,244,213),
							RGB(100,99,99)};
//---------------------------------------------------------------------------
#pragma package(smart_init)

// TSMButton ///////////////////////////////////////////////////////////////
__fastcall TSMButton::TSMButton(System::Classes::TComponent* AOwner)
	: Vcl::Controls::TGraphicControl(AOwner)
{
	FType = 0;
	FHover = FDown = false;

	Canvas->Font->Name = "SimHei";
	Canvas->Font->Size = g_szFont9;
}
//---------------------------------------------------------------------------

void __fastcall TSMButton::Paint(void)
{
	Canvas->Brush->Style = bsSolid;

	if( !Enabled )
		Canvas->Brush->Color = clrState[4];
	else if( FHover ){
		if( FDown )
			Canvas->Brush->Color = clrState[3];
		else
			Canvas->Brush->Color = clrState[1];
	}
	else if( FDown )
		Canvas->Brush->Color = clrState[2];
	else
		Canvas->Brush->Color = clrState[0];

	TRect rtSM( 0, 0, Width-1, Height-1 );
	Canvas->FillRect( rtSM );
	Canvas->Brush->Color = clrStateBoard;
	Canvas->FrameRect( rtSM );

	Canvas->Brush->Style = bsClear;
	Canvas->TextOut( 5, 3, FType==TRACK_MUTE ? "M" : "S" );
	Canvas->Brush->Style = bsSolid;
}
//---------------------------------------------------------------------------

void __fastcall TSMButton::CMMouseEnter(Winapi::Messages::TMessage &Message)
{
	FHover = true;
	Repaint();
}
//---------------------------------------------------------------------------

void __fastcall TSMButton::CMMouseLeave(Winapi::Messages::TMessage &Message)
{
	FHover = false;
	Repaint();
}
//---------------------------------------------------------------------------

void __fastcall TSMButton::SetDown( bool bDown )
{
	if( FDown!=bDown ){
		FDown = bDown;
		Repaint();
	}
}
//---------------------------------------------------------------------------

// TTrackPanel ////////////////////////////////////////////////////////////
__fastcall TTrackPanel::TTrackPanel(TComponent* AOwner)
	: TToolpalPanel(AOwner)
{
	m_iType = -1;
	m_iTrack = -1;
	m_lblName = NULL;
	m_iconMidi = m_iconWave = NULL;
	m_imgType = NULL;
	m_btnMute = m_btnSolo = NULL;

	m_strTitle = "轨道属性";
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::CreateWnd(void)
{
	TToolpalPanel::CreateWnd();

	int top = m_nTitleHeight + ROW_GAP + 4;

	m_iconMidi = new TBitmap();
	m_iconMidi->LoadFromResourceName(int(HInstance), L"imgMidiIcon");
	m_iconWave = new TBitmap();
	m_iconWave->LoadFromResourceName(int(HInstance), L"imgWaveIcon");

	m_imgType = new TImage(NULL);
	m_imgType->Parent = this;
	m_imgType->SetBounds( LEFT_BOUND, top, m_iconMidi->Width, m_iconMidi->Height );

	m_lblName = new TLabel(NULL);
	m_lblName->Parent = this;
	m_lblName->AutoSize = false;
	m_lblName->EllipsisPosition = epEndEllipsis;
	m_lblName->Font->Name = g_strTxtFontName;
	m_lblName->Font->Size = g_szFont9;
	m_lblName->Font->Color = CLR_TXT_NOR;
	m_lblName->Caption = "轨道：未选";
	m_lblName->SetBounds( LEFT_BOUND + 22, top, Width - 100, 20 );

	m_btnMute = new TSMButton( NULL );
	m_btnMute->Parent = this;
	m_btnMute->Type = TRACK_MUTE;
	m_btnMute->SetBounds( Width - 56, top, 17, 17 );
	m_btnMute->OnMouseDown = SMMouseDown;

	m_btnSolo = new TSMButton( NULL );
	m_btnSolo->Parent = this;
	m_btnSolo->Type = TRACK_SOLO;
	m_btnSolo->SetBounds( Width - 36, top, 17, 17 );
	m_btnSolo->OnMouseDown = SMMouseDown;

	top += m_lblName->Height + ROW_GAP*2;
	int	lLabel = LEFT_BOUND + 24,
		wLabel = 40,
		lComp = lLabel + wLabel,
		wComp = Width - 64 - lComp;

	for( int i=0; i<CTRL_VOL; i++ ){
		m_lstCaption[i] = new TLabel(NULL);
		m_lstCaption[i]->Parent = this;
		m_lstCaption[i]->AutoSize = false;
		m_lstCaption[i]->Layout = tlBottom;
		m_lstCaption[i]->Font->Name = g_strTxtFontName;
		m_lstCaption[i]->Font->Size = g_szFont9;
		m_lstCaption[i]->Font->Color = CLR_TXT_NOR;
		m_lstCaption[i]->SetBounds( lLabel, top, wLabel, 18 );

		m_lstCaption[i]->Caption = i==0 ? "音量" : i==1 ? "声像" : "增益";

		m_lstTrack[i] = new TSkinTrackBar(NULL);
		m_lstTrack[i]->Parent = this;
		m_lstTrack[i]->Tag = i;
		m_lstTrack[i]->SetBounds( lComp-2, top, wComp, 18 );
		m_lstTrack[i]->Max = i==0 ? 100 : 100;
		m_lstTrack[i]->Min = i==0 ? 0 : -100;
		m_lstTrack[i]->PageSize = i==0 ? 10 : 20;
		m_lstTrack[i]->Position = i==0 ? 100 : 0;
		m_lstTrack[i]->OnChange = TrackBarChange;

		top += 18 + ROW_GAP;
	}

	top += ROW_GAP;

	m_lblAudio = new TLabel(NULL);
//	m_lblAudio->Visible = false;
	m_lblAudio->Parent = this;
	m_lblAudio->AutoSize = false;
	m_lblAudio->Layout = tlCenter;
	m_lblAudio->Font->Name = g_strTxtFontName;
	m_lblAudio->Font->Size = g_szFont9;
	m_lblAudio->Font->Color = CLR_TXT_NOR;
	m_lblAudio->Caption = "音效";
	m_lblAudio->SetBounds( lLabel, top, wLabel, 20 );

	for( int i=0; i<CTRL_MIDI; i++ ){
		m_lblMidi[i] = new TLabel(NULL);
		m_lblMidi[i]->Parent = this;
		m_lblMidi[i]->AutoSize = false;
		m_lblMidi[i]->Layout = tlCenter;
		m_lblMidi[i]->Font->Name = g_strTxtFontName;
		m_lblMidi[i]->Font->Size = g_szFont9;
		m_lblMidi[i]->Font->Color = CLR_TXT_NOR;
		m_lblMidi[i]->Caption = i==1 ? "通道" : i==0 ? "音源" : i==2 ? "乐器" : "织体";
		m_lblMidi[i]->SetBounds( lLabel, top, wLabel, 20 );

		if( i==1 ){
			m_cbChannel = new TComboBoxComponent(NULL);
			m_cbChannel->Parent = this;
			m_cbChannel->SetFont(g_strTxtFontName,g_szFont9);
			m_cbChannel->ShowItemCount = 16;
			m_cbChannel->SetBounds( lComp, top, wComp, 20 );

			for( int j=0; j<16; j++ ){
				char txt[16];
				sprintf( txt, "ch: %02d", j+1 );
				m_cbChannel->AddItem( txt );
			}
			m_cbChannel->OnChange = ComboBoxChange;
		}
		else{
			int ind = i>1 ? i-1 : 0;
			m_btnMidi[ind] = new TCapButton( NULL );
			m_btnMidi[ind]->Parent = this;
			m_btnMidi[ind]->SetFont(g_strTxtFontName,g_szFont9);
			m_btnMidi[ind]->Tag = i;
			m_btnMidi[ind]->SetBounds( lComp, top, wComp, 20 );
			m_btnMidi[ind]->OnClick = CapButtonClick;

			if( ind==2 )
				m_btnMidi[ind]->Caption = "选择替换";
		}

		top += 20 + ROW_GAP;
	}

	if(!m_pImgDel) {
		m_pImgDel = new TPngImage();
		m_pImgDel->LoadFromResourceName(int(HInstance), L"imgDelete");
	}

	if(!m_pImgOff) {
		m_pImgOff = new TPngImage();
		m_pImgOff->LoadFromResourceName(int(HInstance), L"imgEffectOff");
	}

	if(!m_pImgOn) {
		m_pImgOn = new TPngImage();
		m_pImgOn->LoadFromResourceName(int(HInstance), L"imgEffectOn");
	}

	if(!m_pngAdd) {
		m_pngAdd = new TPngImage();
		m_pngAdd->LoadFromResourceName(int(HInstance), L"imgEffectAdd");
	}

	if( !m_pLstView ) {
		m_pBtnAdd = new TPNGButton(this);
		m_pBtnAdd->Parent = this;
		m_pBtnAdd->SetStateCount(4);
		m_pBtnAdd->SetPngImg(m_pngAdd);
		m_pBtnAdd->OnClick = DoLstAddItem;
		m_pBtnAdd->SetBounds(Width - 56, m_lblAudio->Top,
							m_pngAdd->Width/3, m_pngAdd->Height);

		m_pLstView = new TListViewMove(this);
		m_pLstView->Parent = this;
		m_pLstView->Border = 1;
		m_pLstView->OnItemDBLClick    = DoLstItemDBLClick;
		m_pLstView->OnItemRightClick  = DoLstItemRightClick;
		m_pLstView->OnItemStateChange = DoLstItemStateChange;
		m_pLstView->OnItemDelete  	  = DoLstItemDelete;
		m_pLstView->OnItemMoveEnd     = DoLstItemMovEnd;
		m_pLstView->OnMouseEnter      = DoLstMouseEnter;
		m_pLstView->SetTextFontSize( g_szFont9 );
		int nMaxCount = (Height - m_lblAudio->Top - 40) / CELL_HEIGHT;

		RECTSET( m_pLstView, lComp, m_lblAudio->Top, wComp, CELL_HEIGHT*nMaxCount+12 )
		m_pLstView->AddIcon(m_pImgDel, m_pImgOff, m_pImgOn);
		m_pLstView->ReloadData();
	}

	EnableComponets();
}
//---------------------------------------------------------------------------

__fastcall TTrackPanel::~TTrackPanel()
{
	SAFE_DELETE( m_lblName );
	SAFE_DELETE( m_iconMidi );
	SAFE_DELETE( m_iconWave );
	SAFE_DELETE( m_imgType );

	for( int i=0; i<CTRL_VOL; i++ ){
		SAFE_DELETE( m_lstCaption[i] );
		SAFE_DELETE( m_lstTrack[i] );
	}

	for( int i=0; i<CTRL_MIDI; i++ ){
		SAFE_DELETE( m_lblMidi[i] );

		if( i==0 )
			SAFE_DELETE( m_cbChannel )
		else
			SAFE_DELETE( m_btnMidi[i-1] );
	}

	SAFE_DELETE(m_pImgDel)
	SAFE_DELETE(m_pImgOff)
	SAFE_DELETE(m_pImgOn)
	SAFE_DELETE(m_pBtnAdd)
	SAFE_DELETE(m_pngAdd)
	SAFE_DELETE(m_pLstView)
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::SetSelTrack( int iType, int iTrack )
{
	if( iType==m_iType && iTrack==m_iTrack )
		return;

	m_pLstView->ClearAllItems();

	TBaseTrack* pBase = NULL;

	if( iType==ST_MIDI ){
		TMidiTrack* pMT = g_aMidiTracks[iTrack];
		String strProg = GetProgName( pMT->bank, pMT->chno, pMT->prog );

		m_lblName->Caption = "MIDI轨道：" + strProg;
		m_imgType->Picture->Bitmap = m_iconMidi;
		m_cbChannel->DefItemIndex = pMT->chno;

		int iFont = pMT->bank==0xFF && !g_bExtFont ? g_idFont: pMT->bank;
		String strFont;
		if( iFont==0xFF ){
			strFont = "扩展音色库";
		}
		else if( iFont<0x80 ){
			FONTINFO* pFI = g_lstFonts[iFont];
			strFont = pFI->strFontName;
		}
		else{
			VSTINFO* pFI = g_lstVSTi[iFont-0x80];
			strFont = pFI->strVSTName;
		}

		m_btnMidi[0]->Caption = strFont;
		m_btnMidi[0]->Invalidate();


		m_btnMidi[1]->Caption = strProg;
		m_btnMidi[1]->Invalidate();

		pBase = pMT;
	}
	else if( iType==ST_AUDIO ){
		TAudioTrack* pAT = g_aAudioTracks[iTrack];

		m_lblName->Caption = "音频轨道：音轨 " + IntToStr( iTrack );
		m_imgType->Picture->Bitmap = m_iconWave;

		pBase = pAT;

		if( !pAT->lstFxs.empty() ){
			for( int i=0; i<pAT->lstFxs.size(); i++ ){
				TAudioFx* pFx = pAT->lstFxs[i];
				char strName[256];

				if( pFx->iFx<FX_VST ){
					int no = pFx->iFx==FX_PITCH ? MAX_FX_NUM-1 : pFx->iFx;
					strcpy( strName, g_strEffect[no] );
				}
				else{
					VSTINFO* pVST = g_lstVSTFx[pFx->iFx - FX_VST];

					if( pFx->iPreset<0 ){
						char *name = strrchr( pVST->strFileName, '\\' )+1,
						*ext = strrchr( pVST->strFileName, '.' );

						if( ext>name ){
							strncpy( strName, name, ext-name );
							strName[ext-name] = '\0';
						}
						else
							strcpy(strName, name );
					}
					else{
						strcpy( strName, pVST->aPlugins[pFx->iPreset]->name );
					}
				}

				m_pLstView->AddItem( strName, !pFx->bBypass, false );
			}

			m_pLstView->ReloadData();
		}
	}
	else{
		m_imgType->Picture->Bitmap = NULL;
		m_lblName->Caption = "轨道：未选";
	}

	m_btnMute->Down = pBase && pBase->IsMute();
	m_btnSolo->Down = pBase && pBase->IsSolo();

	if( pBase ){
		for( int i=0; i<CTRL_VOL; i++ ){
			int pos = i==0 ? (int)pBase->volume : ( i==1 ? (int)pBase->pan : (int)pBase->gain);
			m_lstTrack[i]->OnChange = NULL;
			m_lstTrack[i]->Position = pos;
			m_lstTrack[i]->OnChange = TrackBarChange;
		}
	}

	m_iType = iType;
	m_iTrack = iTrack;

	EnableComponets();
}
//---------------------------------------------------------------------------

void TTrackPanel::EnableComponets()
{
	m_btnMute->Enabled = m_iType>=0;
	m_btnSolo->Enabled = m_iType>=0;

	m_lstTrack[0]->Enabled = m_iType>=0;
	m_lstTrack[1]->Enabled = m_iType==ST_AUDIO;
	m_lstTrack[2]->Enabled = m_iType==ST_AUDIO;

	m_lblAudio->Visible = m_iType==ST_AUDIO;
	m_pLstView->Visible = m_iType==ST_AUDIO;
	m_pBtnAdd->Visible  = m_iType==ST_AUDIO;

	for( int i=0; i<CTRL_MIDI; i++ ){
		m_lblMidi[i]->Visible = m_iType==ST_MIDI;

		if( i==0 ){
			m_cbChannel->Visible = m_iType==ST_MIDI;
		}
		else{
			m_btnMidi[i-1]->Visible = m_iType==ST_MIDI;
		}
	}
}
//---------------------------------------------------------------------------

void TTrackPanel::OnTrackParamChange( int iParam, int nValue, bool bAddUndo )
{
	TBaseTrack* pBase = m_iType==ST_AUDIO ? (TBaseTrack*)g_aAudioTracks[m_iTrack]
										: (TBaseTrack*)g_aMidiTracks[m_iTrack];

	if( !IsTrackMute( m_iType, m_iTrack ) ){
		if( m_iType==ST_MIDI ){
			TMidiTrack* pMT = (TMidiTrack*)pBase;
			char val = iParam==0 ? nValue : iParam==1 ? ( nValue * 63 / 100 )
											: nValue * 127 / 100;

			f_MM_SetMidiTrackParam( pMT->file, pMT->chno, iParam, val );
		}
		else{
			f_MM_SetAudioTrackParam( m_iTrack, iParam, (float)(nValue / 100.0) );
		}
	}

	int iUndoType, nOriVal;
	if( iParam==0 ){
		if( bAddUndo ){
			iUndoType = UNDO_TRACK_VOL;
			nOriVal = pBase->volume;
		}

		pBase->volume = nValue;

		if( m_iType==ST_MIDI ){
			int iShowTrack = m_pContentPanel->GetShowTrackId( m_iType, m_iTrack );
			m_pContentPanel->InvalidateShowBars( BAR_TRACK | iShowTrack );
		}
	}
	else if( iParam==1 ){
		if( bAddUndo ){
			iUndoType = UNDO_TRACK_PAN;
			nOriVal = pBase->pan;
		}

		pBase->pan = nValue;
	}
	else{
		if( bAddUndo ){
			iUndoType = UNDO_TRACK_GAIN;
			nOriVal = pBase->gain;
		}

		pBase->gain = nValue;
	}

	if( bAddUndo ){
		int iCode = MAKELONG( m_iType, m_iTrack );
		bool bSetUndo = true;
		TUndoLite* pLite = g_Undo.GetLastLite();
		if( pLite && pLite->iType==iUndoType && pLite->iCode==iCode ){
			bSetUndo = false;

			if( (int)(pLite->pBefore)==nValue )
				g_Undo.CancelUndo();
			else
				pLite->pAfter = (void*)nValue;
		}

		if( bSetUndo )
			g_Undo.AddUndo( iUndoType, iCode, 0, 0, (void*)nOriVal, (void*)nValue );
	}
	else{
		m_lstTrack[iParam]->OnChange = NULL;
		m_lstTrack[iParam]->Position = nValue;
		m_lstTrack[iParam]->OnChange = TrackBarChange;
	}
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::TrackBarChange(TObject *Sender)
{
	TSkinTrackBar* pSTB = (TSkinTrackBar*)Sender;
	OnTrackParamChange( pSTB->Tag, pSTB->Position );
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::ComboBoxChange(TComboBoxItem *Sender, int Index)
{
	TMidiTrack* pMT = g_aMidiTracks[m_iTrack];
	BYTE chOld = pMT->chno,
		chNew = Index,
		fiOld = pMT->file,
		fiNew = g_Midi.GetValidFile( pMT->bank, chNew );

	g_Midi.ChangMidiChno( m_iTrack, fiNew, chNew );

	g_Undo.AddUndo( UNDO_TRACK_CHNO, MAKELONG( ST_MIDI, m_iTrack ), 0, 0,
									(void*)MAKELONG( fiOld, chOld ), (void*)MAKELONG( fiNew, chNew ) );
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::CapButtonClick(TObject *Sender)
{
	if( Sender==m_btnMidi[0] || Sender==m_btnMidi[1] )
		PostMessage( m_pContentPanel->Handle, UM_MIDISWITCH, 0, 0 );
	else if( Sender==m_btnMidi[2] )
		PostMessage( m_pContentPanel->Handle, UM_MIDISWITCH, 1, 0 );
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::SMMouseDown(System::TObject* Sender, System::Uitypes::TMouseButton Button,
												System::Classes::TShiftState Shift, int X, int Y)
{
	TSMButton* pSM = (TSMButton*)Sender;
	OnTrackStateChanged( pSM->Type );
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::WMSize(Winapi::Messages::TWMSize &Message)
{
	TToolpalPanel::WMSize( Message );

	if( m_lblName ){
		m_lblName->Width = Width - 100;
		m_btnMute->Left = Width - 56;
		m_btnSolo->Left = Width - 36;
		m_pBtnAdd->Left = Width - 56;

		int	wComp = Width - 128 - LEFT_BOUND;

		for( int i=0; i<CTRL_VOL; i++ )
			m_lstTrack[i]->Width = wComp;

		for( int i=0; i<CTRL_MIDI; i++ ){
			if( i==0 )
				m_cbChannel->Width = wComp;
			else
				m_btnMidi[i-1]->Width = wComp;
		}

		int nMaxCount = (Height - m_lblAudio->Top - 40) / CELL_HEIGHT;
		m_pLstView->SetBounds( m_pLstView->Left, m_pLstView->Top, wComp, CELL_HEIGHT*nMaxCount+12 );
		m_pLstView->ReloadData(m_pLstView->GetCellBeg());
	}
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::RefreshTrackState( int iState, bool bUnset )
{
	TBaseTrack* pBaseTrk = m_iType==ST_AUDIO ? (TBaseTrack*)g_aAudioTracks[m_iTrack]
											: (TBaseTrack*)g_aMidiTracks[m_iTrack];

	if( iState==0 ){
		m_lstTrack[0]->OnChange = NULL;
		m_lstTrack[0]->Position = pBaseTrk->volume;
		m_lstTrack[0]->OnChange = TrackBarChange;
	}
	else{
		TSMButton *pBtnSet = iState==TRACK_MUTE ? m_btnMute : m_btnSolo;

		pBtnSet->Down = iState==TRACK_MUTE ? pBaseTrk->IsMute() : pBaseTrk->IsSolo();

		if( bUnset ){
			TSMButton * pBtnOth = iState==TRACK_MUTE ? m_btnSolo : m_btnMute;
			pBtnOth->Down = false;
		}
	}
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::OnTrackStateChanged( int iState, bool bAddUndo )
{
	TBaseTrack* pBaseTrk = m_iType==ST_AUDIO ? (TBaseTrack*)g_aAudioTracks[m_iTrack]
											: (TBaseTrack*)g_aMidiTracks[m_iTrack];
	int iOriState = pBaseTrk->state & (TRACK_MUTE|TRACK_SOLO);
	bool bSet = false, bUnset = false;

	ChangeTrackState( m_iType, m_iTrack, iState, bSet, bUnset );
	RefreshTrackState( iState, bUnset );

	m_pContentPanel->OnTrackStateChanged( m_iType, m_iTrack, iState, bUnset );

	if( bAddUndo ){
		int iNewState = pBaseTrk->state & (TRACK_MUTE|TRACK_SOLO),
			iCode = MAKELONG( m_iType, m_iTrack );
		bool bSetUndo = true;
		TUndoLite* pLite = g_Undo.GetLastLite();
		if( pLite && pLite->iType==UNDO_TRACK_STATE && pLite->iCode==iCode ){
			bSetUndo = false;

			if( (int)(pLite->pBefore)==iNewState )
				g_Undo.CancelUndo();
			else
				pLite->pAfter = (void*)iNewState;
		}

		if( bSetUndo )
			g_Undo.AddUndo( UNDO_TRACK_STATE, iCode, 0, 0, (void*)iOriState, (void*)iNewState );
	}
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::DoLstMouseEnter(TObject *Sender)
{
	m_pLstView->SetFocus();
}
//---------------------------------------------------------------------------

bool __fastcall TTrackPanel::DoLstItemDBLClick(int index, bool bOn)
{
	DebugPrint("DoLstItemDBLClick index %d bOn %d\n", index, bOn);

	PostMessage( Handle, UM_AUDIOFX, 1, index );
}
//---------------------------------------------------------------------------

bool __fastcall TTrackPanel::DoLstItemRightClick(int index, bool bOn, TPoint pt)
{
	DebugPrint("DoLstItemRightClick index %d bOn %d x %d y %d\n", index, bOn, pt.X, pt.Y);

	m_PopMenu->ClearItems();

	m_PopMenu->CreateMenu( bOn ? "关闭音效" : "打开音效", "", DoAudioFxState );
	m_PopMenu->CreateMenu( "调节音效参数", "", DoAudioFxParam );
	m_PopMenu->CreateMenu( "删除音效", "", DoAudioFxDelete );
//	m_PopMenu->AddMenuItem( bOn ? "关闭音效" : "打开音效", "", DoAudioFxState );
//	m_PopMenu->AddMenuItem( "调节音效参数", "", DoAudioFxParam );
//	m_PopMenu->AddMenuItem( "删除音效", "", DoAudioFxDelete );

	pt = m_pLstView->ClientToParent( pt );

	m_PopMenu->Parent = this;
	m_PopMenu->Show( pt.X, pt.Y );
}
//---------------------------------------------------------------------------

bool __fastcall TTrackPanel::DoLstItemStateChange(int index, bool bOn)
{
	DebugPrint("DoLstItemStateChange index %d bOn %d\n", index, bOn);

	OnAudioFxState( index, bOn );
}
//---------------------------------------------------------------------------

bool __fastcall TTrackPanel::DoLstItemDelete(int indSrc, int indDes)
{
	DebugPrint("DoLstItemDelete indSrc %d indDes %d\n", indSrc, indDes);

	if( mrYes==MessageForm->ShowMessage( L"确定删除该音效？", MB_ICONQUESTION|MB_YESNO ) ){
		OnAudioFxDelete( indSrc );
		return true;
	}

	return false;
}
//---------------------------------------------------------------------------
bool __fastcall TTrackPanel::DoLstItemMovEnd(int indSrc, int indDes)
{
	DebugPrint("DoLstItemMovEnd indSrc %d indDes %d\n", indSrc, indDes);

	if( indDes>indSrc )
		indDes--;

	if( indDes==indSrc )
		return false;

	f_MM_AudioMoveFx( m_iTrack, indSrc, indDes );

	g_Undo.AddUndo( UNDO_FX_MOVE, MAKELONG( m_iType, m_iTrack ), indSrc, indDes,
																		NULL, NULL );

	TAudioFxList& lstFx = g_aAudioTracks[m_iTrack]->lstFxs;
	TAudioFx* pFx = lstFx[indSrc];
	lstFx.erase( lstFx.begin() + indSrc );

	if( indDes>=lstFx.size() )
		lstFx.push_back( pFx );
	else
		lstFx.insert( lstFx.begin()+indDes, pFx );

	return true;
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::DoLstAddItem(TObject *Sender)
{
	m_PopMenu->ClearItems();

	CMenuItem *pMenuFx =  m_PopMenu->CreateMenu( "Fx音效" ),
			*pMenuVst =  m_PopMenu->CreateMenu( "VST音效" );
	pMenuVst->Enabled = !g_lstVSTFx.empty();

	for( int i=0; i<MAX_FX_NUM-1; i++ ){
		pMenuFx->AppendMenu( g_strEffect[i], "", DoAudioAddFx, i );
//		m_PopMenu->AddMenuItem( g_strEffect[i], "", DoAudioAddFx, iMenuFx );
	}

	if( !g_lstVSTFx.empty() ){
		for( int i=0; i<g_lstVSTFx.size(); i++ ){
			VSTINFO* pVST = g_lstVSTFx[i];

			char fname[128],
				*name = strrchr( pVST->strFileName, '\\' )+1,
				*ext = strrchr( pVST->strFileName, '.' );

			if( ext>name ){
				strncpy( fname, name, ext-name );
				fname[ext-name] = '\0';
			}
			else
				strcpy(fname, name );

			if( !pVST->aPlugins.empty() )
				strcat( fname, " ..." );

			pMenuVst->AppendMenu( fname, "", DoAudioAddVst, i );
			//m_PopMenu->AddMenuItem( fname, "", DoAudioAddVst, iMenuVst );
		}
	}

	m_PopMenu->Parent = this;
	m_PopMenu->Show( m_pBtnAdd->Left, m_pBtnAdd->Top+m_pBtnAdd->Height );
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::DoAudioFxState(System::TObject* Sender)
{
	int ind = m_pLstView->IndSel;
	TAudioFx* pFx = g_aAudioTracks[m_iTrack]->lstFxs[ind];
	bool bOn = pFx->bBypass;
	m_pLstView->SetItemState( ind, bOn );

	OnAudioFxState( ind, bOn );
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::DoAudioFxParam(System::TObject* Sender)
{
	int ind = m_pLstView->IndSel;

	OnAudioFxParam( ind );
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::DoAudioFxDelete(System::TObject* Sender)
{
	int ind = m_pLstView->IndSel;

	int mrRet = MessageForm->ShowMessage( L"确定删除该音效？", MB_ICONQUESTION|MB_YESNO );

	if( mrRet==mrYes )
		OnAudioFxDelete( ind );
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::DoAudioAddFx(System::TObject* Sender)
{
	int ind = (int)Sender;
	PostMessage( Handle, UM_AUDIOFX, 0, ind );
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::DoAudioAddVst(System::TObject* Sender)
{
	int ind = (int)Sender;
	PostMessage( Handle, UM_AUDIOFX, 0, FX_VST+ind );
}
//---------------------------------------------------------------------------

void __fastcall TTrackPanel::UMAudioFx(TMessage &msg)
{
	Parent->Perform( UM_STOP, STOP_ALL, 0 );

	if( msg.WParam==0 ){
		int iFxType = msg.LParam;
		OnAddAudioFx( iFxType );
	}
	else if( msg.WParam==1 ){
		int ind = msg.LParam;
		OnAudioFxParam( ind );
	}
}
//---------------------------------------------------------------------------

void TTrackPanel::GetAudioDemoRange( double& dblStart, double& dblEnd )
{
	TAudioTrack* pAT = g_aAudioTracks[m_iTrack];
	TWaveBarList& list = pAT->lstBars;
	int iBarStart = -1, iPointStart = 0,
		iBarEnd = -1, iPointEnd = 0;

	//查找波形起点
	int nBarNum = g_Opus.BarCount();
	for( int i=0; i<nBarNum && iBarStart<0; i++ ){
		for( int j=0; j<POINTS_PER_BAR; j++ ){
			DWORD dwValue = list[i]->aVols[j];
			WORD wVol = max( LOWORD(dwValue), HIWORD(dwValue) );

			if( wVol>0 ){
				iBarStart = i;
				iPointStart = j;

				break;
			}
		}
	}

	//查找波形终点
	if( iBarStart>=0 ){
		for( int i=nBarNum-1; i>=0 && iBarEnd<0; i-- ){
			for( int j=POINTS_PER_BAR-1; j>=0; j-- ){
				DWORD dwValue = list[i]->aVols[j];
				WORD wVol = max( LOWORD(dwValue), HIWORD(dwValue) );

				if( wVol>0 ){
					iBarEnd = i;
					iPointEnd = j;

					break;
				}
			}
		}
	}

	if( iBarStart>=0 ){
		dblStart = iBarStart + (double)iPointStart/POINTS_PER_BAR;
		dblEnd = iBarEnd + (double)iPointEnd/POINTS_PER_BAR;
	}
	else{
		dblStart = 0;
		dblEnd = nBarNum;
	}
}
//---------------------------------------------------------------------------

void TTrackPanel::OnAddAudioFx( int iFxType )
{
	int iPlugin = -1;
	UINT pluginID = 0;
	char* strVSTFile = NULL;

	if( iFxType>=FX_VST ){
		VSTINFO* pVST = g_lstVSTFx[iFxType-FX_VST];
		strVSTFile = pVST->strFileName;

		if( !pVST->aPlugins.empty() ){
			TVSTPluginForm* pPlugForm = new TVSTPluginForm( NULL, pVST );
			if( pPlugForm->ShowModal()==mrOk ){
				iPlugin = pPlugForm->m_iSel;
				pluginID = pVST->aPlugins[iPlugin]->id;
			}

			delete pPlugForm;

			if( iPlugin<0 )
				return;
		}
	}

	HRESULT hr = f_MM_AudioAddFx( m_iTrack, iFxType, strVSTFile, pluginID, false );

	if( hr!=S_OK ){
		return;
	}

	GenerateCompose();

	TAudioTrack* pAT = g_aAudioTracks[m_iTrack];
	int ind = pAT->lstFxs.size();
	double dblStart, dblEnd;

	GetAudioDemoRange( dblStart, dblEnd );

	TAudioFxForm * pForm = new TAudioFxForm(NULL, m_iTrack, dblStart, dblEnd, ind, iFxType, iPlugin );

	if( pForm->ShowModal()==mrOk ){
		int nParamSize = 0,
			iPreset = -1;
		void* pParam = NULL;

		if( iFxType<FX_VST ){
			iPreset = pForm->GetPreset();
			nParamSize = pForm->GetParamCount();

			if( nParamSize>0 ){
				pParam = new float[nParamSize];
				memcpy( pParam, pForm->GetParams(), sizeof(float)*nParamSize );
			}
		}
		else{
			iPreset = iPlugin;

			nParamSize = f_MM_AudioGetFxParam( m_iTrack, ind, NULL, 0 );
			if( nParamSize>0 ){
				pParam = new float[nParamSize];
				f_MM_AudioGetFxParam( m_iTrack, ind, pParam, nParamSize );
			}
		}

		TAudioFx* pFx = new TAudioFx( iFxType, iPreset, pParam, nParamSize );
		pFx->bBypass = pForm->IsBypass();
		pAT->lstFxs.push_back( pFx );

		char strName[256];

		if( pFx->iFx<FX_VST ){
			int no = pFx->iFx==FX_PITCH ? MAX_FX_NUM-1 : pFx->iFx;
			strcpy( strName, g_strEffect[no] );
		}
		else{
			VSTINFO* pVST = g_lstVSTFx[pFx->iFx - FX_VST];


			if( pFx->iPreset<0 ){
				char *name = strrchr( pVST->strFileName, '\\' )+1,
				*ext = strrchr( pVST->strFileName, '.' );

				if( ext>name ){
					strncpy( strName, name, ext-name );
					strName[ext-name] = '\0';
				}
				else
					strcpy(strName, name );
			}
			else{
				strcpy( strName, pVST->aPlugins[pFx->iPreset]->name );
			}
		}

		m_pLstView->AddItem( strName, !pFx->bBypass );

		TAudioFx* pAfter = new TAudioFx( pFx );
		g_Undo.AddUndo( UNDO_FX_ADD, MAKELONG( m_iType, m_iTrack ), -1, pAT->lstFxs.size()-1,
																			NULL, (void*)pAfter );
	}
	else{
		f_MM_AudioMoveFx( m_iTrack, ind, -1 );
	}

	delete pForm;
}
//---------------------------------------------------------------------------

void TTrackPanel::OnAudioFxParam( int ind )
{
	Parent->Perform( UM_STOP, STOP_ALL, 0 );
	GenerateCompose();

	TAudioTrack* pAT = g_aAudioTracks[m_iTrack];
	TAudioFx* pFx = pAT->lstFxs[ind];
	int iPlugin = pFx->iFx < FX_VST ? -1 : pFx->iPreset;
	double dblStart, dblEnd;

	GetAudioDemoRange( dblStart, dblEnd );

	TAudioFxForm * pForm = new TAudioFxForm(NULL, m_iTrack, dblStart, dblEnd, ind, pFx->iFx, iPlugin );
	int ret = pForm->ShowModal();

	int nParamSize = pFx->nParamSize,
		iPresetB = 0,
		iPresetA = 0,
		bBypassB = pFx->bBypass,
		bBypassA = pForm->IsBypass();
	float *pParamB = NULL,
		*pParamA = nParamSize>0 ? new float[nParamSize] : NULL;

	if( pFx->iFx < FX_VST ){
		iPresetB = pFx->iPreset;
		iPresetA = pForm->GetPreset();
		memcpy( pParamA, pForm->GetParams(), sizeof(float)*nParamSize );
	}
	else if( nParamSize>0 ){
		f_MM_AudioGetFxParam( m_iTrack, ind, pParamA, nParamSize );
	}

	if( nParamSize>0 ){
		if( memcmp( pParamA, pFx->pParam, sizeof(float)*nParamSize )){
			pParamB = new float[nParamSize];

			memcpy( pParamB, pFx->pParam, sizeof(float)*nParamSize );
		}
		else{
			delete pParamA;
			pParamA = NULL;
		}
	}

	if( pParamA || iPresetB!=iPresetA || bBypassB!=bBypassA ){
		if( ret==mrOk ){
			if( iPresetB!=iPresetA )
				pFx->iPreset = iPresetA;

			if( bBypassB!=bBypassA ){
				pFx->bBypass = bBypassA;
				m_pLstView->SetItemState( ind, !bBypassA );
			}

			if( pParamA )
				memcpy( pFx->pParam, pParamA, sizeof(float)*nParamSize );

			AddAudioFxParamUndo( ind, MAKELONG( iPresetB, bBypassB ),
										MAKELONG( iPresetA, bBypassA ),
										(void*)pParamB, (void*)pParamA );
		}
		else{
			if( pParamB && ( !bBypassB || pFx->iFx>=FX_VST ) ){
				f_MM_AudioSetFxParam( m_iTrack, ind, pParamB, pFx->nParamSize );
			}

			if( bBypassB ){
				f_MM_AudioSetFxParam( m_iTrack, ind, (void*)NULL, 0 );
			}

			delete pParamA;
		}
	}

	delete pForm;
}
//---------------------------------------------------------------------------

void TTrackPanel::OnAudioFxState( int ind, bool bOn )
{
	TAudioTrack* pAT = g_aAudioTracks[m_iTrack];
	TAudioFx* pFx = pAT->lstFxs[ind];
	int iPreset = pFx->iFx < FX_VST ? pFx->iPreset : 0,
		bBypassB = pFx->bBypass,
		bBypassA = !bOn;

	pFx->bBypass = bBypassA;

	if( pFx->pParam && !bBypassA ){
		f_MM_AudioSetFxParam( m_iTrack, ind, pFx->pParam, pFx->nParamSize );
	}

	if( bBypassA ){
		f_MM_AudioSetFxParam( m_iTrack, ind, (void*)NULL, 0 );
	}

	AddAudioFxParamUndo( ind, MAKELONG( iPreset, bBypassB ),
								MAKELONG( iPreset, bBypassA ),
								NULL, NULL );
}
//---------------------------------------------------------------------------

void TTrackPanel::AddAudioFxParamUndo( int ind, DWORD iBegin, DWORD iEnd,
												void* pBefore, void* pAfter )
{
	bool bSetUndo = true;
	TUndoLite* pLite = g_Undo.GetLastLite();
	int iCode = MAKELONG( m_iType, m_iTrack );
	if( pLite && pLite->iType==UNDO_FX_PARAM && pLite->iCode==iCode
								&& pLite->iExt1==ind && !pBefore && !pAfter
								&& !pLite->pBefore && !pLite->pAfter ){
		WORD iPresetB = LOWORD( iBegin ),
			bBypassB = HIWORD( iBegin ),
			bBypassA = HIWORD( iEnd ),
			iPresetBPrev = LOWORD( pLite->iBegin ),
			bBypassBPrev = HIWORD( pLite->iBegin ),
			bBypassAPrev = HIWORD( pLite->iEnd );

		if( iPresetBPrev==iPresetB ){
			bSetUndo = false;

			if( bBypassBPrev==bBypassA )
				g_Undo.CancelUndo();
			else
				pLite->iEnd = iEnd;
		}
	}

	if( bSetUndo ){
		g_Undo.AddUndo( UNDO_FX_PARAM, iCode, iBegin, iEnd, pBefore, pAfter );
		g_Undo.SetUndoExtra( ind, 0, 0, NULL, NULL );
	}
}
//---------------------------------------------------------------------------

void TTrackPanel::OnAudioFxDelete( int ind )
{
	f_MM_AudioMoveFx( m_iTrack, ind, -1 );

	TAudioFxList& lstFx = g_aAudioTracks[m_iTrack]->lstFxs;
	TAudioFx* pBefore = lstFx[ind];
	lstFx.erase( lstFx.begin() + ind );

	g_Undo.AddUndo( UNDO_FX_DEL, MAKELONG( m_iType, m_iTrack ), ind, -1,
														(void*)pBefore, NULL );
}
//---------------------------------------------------------------------------

void TTrackPanel::Undo( bool bUndo )
{
	TUndoLite* pLite = bUndo ? g_Undo.Undo() : g_Undo.Redo();
	int iUndoType = pLite->iType,
		iType = LOWORD( pLite->iCode ),
		iTrack = HIWORD( pLite->iCode );

	if( iType==ST_MIDI && m_pContentPanel->IsVertical() )
		m_pContentPanel->SetScrollMode( false );

	if( m_iType!=iType || m_iTrack!=iTrack ){
		int iShowTrack = m_pContentPanel->GetShowTrackId( iType, iTrack ) - 1;
		m_pContentPanel->SelectTrack( iShowTrack, -1, -1 );
	}

	if( iUndoType==UNDO_TRACK_CHNO ){
		DWORD dwNew = (DWORD)( bUndo ? pLite->pBefore : pLite->pAfter );
		BYTE chNew = HIWORD( dwNew ),
			fiNew = LOWORD( dwNew );

		g_Midi.ChangMidiChno( iTrack, fiNew, chNew );

		if( m_cbChannel->DefItemIndex!=chNew ){
			m_cbChannel->OnChange = NULL;
			m_cbChannel->DefItemIndex = chNew;
			m_cbChannel->OnChange = ComboBoxChange;
		}
	}
	else if( iUndoType>=UNDO_TRACK_STATE && iUndoType<=UNDO_TRACK_GAIN ){
		int	iNewVal = (int)(bUndo ? pLite->pBefore : pLite->pAfter);

		if( iUndoType==UNDO_TRACK_STATE ){
			int iOriVal = (int)( bUndo ? pLite->pAfter : pLite->pBefore);
			OnTrackStateChanged( iNewVal==0 ? iOriVal : iNewVal, false );
		}
		else{
			OnTrackParamChange( iUndoType-UNDO_TRACK_VOL, iNewVal, false );
		}
	}
	else if( iUndoType==UNDO_FX_ADD || iUndoType==UNDO_FX_DEL ){
		TAudioFx* pFx = bUndo ? (TAudioFx*)pLite->pBefore : (TAudioFx*)pLite->pAfter;
		int ind = iUndoType==UNDO_FX_DEL ? pLite->iBegin : pLite->iEnd;

		if( pFx ){
			TAudioTrack* pAT = g_aAudioTracks[iTrack];
			UINT pluginID = 0;
			char* strVSTFile = NULL;

			if( pFx->iFx>=FX_VST ){
				VSTINFO* pVST = g_lstVSTFx[pFx->iFx-FX_VST];
				strVSTFile = pVST->strFileName;

				if( pFx->iPreset>=0 && pFx->iPreset < pVST->aPlugins.size() ){
					pluginID = pVST->aPlugins[pFx->iPreset]->id;
				}
			}

			HRESULT hr = f_MM_AudioAddFx( iTrack, pFx->iFx, strVSTFile, pluginID, false );
			if( hr==S_OK ){
				char strName[256];

				if( pFx->iFx<FX_VST ){
					int no = pFx->iFx==FX_PITCH ? MAX_FX_NUM-1 : pFx->iFx;
					strcpy( strName, g_strEffect[no] );
				}
				else{
					VSTINFO* pVST = g_lstVSTFx[pFx->iFx - FX_VST];

					if( pFx->iPreset<0 ){
						char *name = strrchr( pVST->strFileName, '\\' )+1,
						*ext = strrchr( pVST->strFileName, '.' );

						if( ext>name ){
							strncpy( strName, name, ext-name );
							strName[ext-name] = '\0';
						}
						else
							strcpy(strName, name );
					}
					else{
						strcpy( strName, pVST->aPlugins[pFx->iPreset]->name );
					}
				}

				TAudioFx* pNewFx = new TAudioFx( pFx );

				if( ind<pAT->lstFxs.size() ){
					f_MM_AudioMoveFx( iTrack, pAT->lstFxs.size(), ind );

					if( ind>=pAT->lstFxs.size() )
						pAT->lstFxs.push_back( pNewFx );
					else
						pAT->lstFxs.insert( pAT->lstFxs.begin()+ind, pNewFx );

					m_pLstView->AddItem( strName, !pFx->bBypass, true, ind );
				}
				else{
					pAT->lstFxs.push_back( pNewFx );
					m_pLstView->AddItem( strName, !pFx->bBypass );
				}

				if( !pFx->bBypass || pFx->iFx>=FX_VST )
					f_MM_AudioSetFxParam( iTrack, ind, pFx->pParam, pFx->nParamSize );

				if( pFx->bBypass )
					f_MM_AudioSetFxParam( iTrack, ind, NULL, 0 );
			}
		}
		else if( ind>=0 ){
			f_MM_AudioMoveFx( iTrack, ind, -1 );

			TAudioFxList& lstFx = g_aAudioTracks[iTrack]->lstFxs;
			lstFx.erase( lstFx.begin() + ind );
			m_pLstView->DeleteItem( ind );
		}
	}
	else if( iUndoType==UNDO_FX_MOVE ){
		int indFrom = bUndo ? pLite->iEnd : pLite->iBegin,
			indTo = bUndo ? pLite->iBegin : pLite->iEnd;

		f_MM_AudioMoveFx( iTrack, indFrom, indTo );
		m_pLstView->MoveItems( indFrom, indTo );

		TAudioFxList& lstFx = g_aAudioTracks[iTrack]->lstFxs;
		TAudioFx* pFx = lstFx[indFrom];
		lstFx.erase( lstFx.begin() + indFrom );
		if( indTo>=lstFx.size() )
			lstFx.push_back( pFx );
		else
			lstFx.insert( lstFx.begin() + indTo, pFx );
	}
	else if( iUndoType==UNDO_FX_PARAM ){
		int ind = pLite->iExt1,
			iState = bUndo ? pLite->iBegin : pLite->iEnd,
			iPreset = LOWORD(iState);
		WORD bBypass = HIWORD(iState);
		float *pParam = (float*)(bUndo ? pLite->pBefore : pLite->pAfter);
		TAudioFx* pFx = g_aAudioTracks[iTrack]->lstFxs[ind];

		if( pFx->iFx < FX_VST )
			pFx->iPreset = iPreset;

		if( pParam ){
			memcpy( pFx->pParam, pParam, sizeof(float)*pFx->nParamSize );
		}

		if( bBypass!=pFx->bBypass ){
			pFx->bBypass = bBypass;

			if( pFx->iFx>=FX_VST ){
				if( !bBypass && pParam )
					f_MM_AudioSetFxParam( m_iTrack, ind, pParam, pFx->nParamSize );

				f_MM_AudioSetFxParam( iTrack, ind, (void*)!bBypass, 0 );
			}
			else
				f_MM_AudioSetFxParam( iTrack, ind, (void*)(bBypass ? NULL : pFx->pParam), 0 );

			m_pLstView->SetItemState( ind, !bBypass );
		}
		else if( !bBypass && pParam ){
			f_MM_AudioSetFxParam( m_iTrack, ind, pParam, pFx->nParamSize );
		}

		m_pLstView->IndSel = ind;

		if( pParam )
			OnAudioFxParam( ind );
	}
}
//---------------------------------------------------------------------------
