/****************************************************************************
 *   Copyright (C) 2004 by Arboretum Systems, Inc.                          *
 *   Technical administrator: Alain Mazy, hav@mazy.be                       *
 *   Lead program designer: Georges Jaroslaw, gjaro@arboretum.com           *
 *                                                                          *
 *   HyperEngine-AV is a video, audio and text editor that can be used to   *
 *   record, import, edit, process and arrange together video, sound and    *
 *   text. These are all achieved in the program's free-form document       *
 *   window, with no limitations to the number of tracks.                   *
 *   The purpose of this program is to provide to users at all levels of    *
 *   expertise a readily accessible tool enabling them to create movies,    *
 *   slide shows or presentations.                                          *
 *                                                                          *
 *   HyperEngine-AV itself is free software. However, plug-ins that can be  *
 *   used as add-ons to HyperEngine-AV may or may not be free software, at  *
 *   the discretion of each plug-in developer and as specified by these     *
 *   plug-ins' respective licenses.                                         *
 *   You can redistribute HyperEngine-AV itself 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.                                             *
 *                                                                          *
 *   HyperEngine-AV 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 all applicable details.                 *
 *                                                                          *
 *   You should have received a copy of the GNU General Public License      *
 *   along with this program; if not, write to:                             *
 *   Free Software Foundation, Inc.,                                        *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.              *
 ****************************************************************************/

#ifndef ZzTypes_h
#include "ZzTypes.h"
#endif //ZzTypes_h

#ifndef TARPIHost_h
#include "TARPIHost.h"
#endif //TARPIHost_h

#ifndef VARPIPlugin_h
#include "VARPIPlugin.h"
#endif VARPIPlugin_h

#ifndef VARPIPluginClass_h
#include "VARPIPluginClass.h"
#endif VARPIPluginClass_h

#ifndef VARPIPluginSet_h
#include "VARPIPluginSet.h"
#endif //VARPIPluginSet_h

#import "ARPIBlueWindow.h"
#import "ARPIBlueWindowController.h"

#ifndef chkerrcm_h
#include "../Arme/chkerrcm.h"
#endif //chkerrcm_h

#ifndef BuiltinEffectsSet_h
#include "BuiltinEffectsSet.h"
#endif //BuiltinEffectsSet_h

#ifndef TArmeMediaEngine_h
#include "TArmeMediaEngine.h"
#endif //TArmeMediaEngine_h

#ifndef TArmeWSObj_h
#include "TArmeWSObj.h"
#endif //TArmeWSObj_h

#import "MIObject.h"

void TARPIHost::LoadPluginSets()
{	
	verntl(arpiEngine,6,"TARPIHost::LoadPluginSets()");
	fPluginSetArray.AddItem(new BuiltinEffectsSet());
}
	
VARPIPlugin* TARPIHost::CreatePlugin(const VARPIPluginClass* pluginClass)
{
	VARPIPlugin* plugin = pluginClass->CreatePlugin();
	return plugin;
}
	
void* TARPIHost::CreateProcessorView(void* miobject)
{
	// this ensures there is not yet any view
	// associated with the processor
	ZzAssert( fProcessorToViewMap.GetValueForKey(miobject) == NULL);
	void* view = [(MIObject*)miobject WSObject]->GetAudioPluginClass()->CreateView(miobject);
	if(view==NULL)
		view = (void*)([[ARPIBlueWindow alloc] initWithMIObject:(id)miobject]);
	fProcessorToViewMap.SetValueForKey(miobject,view);
	fViewToProcessorMap.SetValueForKey(view,miobject);
	[(ARPIBlueWindow*)view postInit];
	return [(ARPIBlueWindow*)view autorelease];
}

void* TARPIHost::CreateBlueWindowPluginView(void* miobject)
{
	// this ensures there is not yet any view
	// associated with the processor
	ZzAssert( fProcessorToViewMap.GetValueForKey(miobject) == NULL);
	void* view = (void*)([[ARPIBlueWindow alloc] initWithMIObject:(id)miobject]);
	fProcessorToViewMap.SetValueForKey(miobject,view);
	fViewToProcessorMap.SetValueForKey(view,miobject);
	[(ARPIBlueWindow*)view postInit];
	return [(ARPIBlueWindow*)view autorelease];
}

void TARPIHost::SetValueFromView(void* view,SInt32 paramIndex,float val)
{
	MIObject* processor = (MIObject*)fViewToProcessorMap.GetValueForKey(view);
	return [processor WSObject]->SetAudioPluginParamValue(paramIndex,val);
}
	
void myMIDIReadProc(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon)
{
	TArmeMediaEngine::ARPIHost()->MyMIDIReadProc(pktlist, readProcRefCon, srcConnRefCon);
}

// our midi callback
// IT ONLY WORKS FOR 4CONTROL
void TARPIHost::MyMIDIReadProc(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon)
{
	// button0 is the rightmost button
	static SInt32 button0 = 0;
	static SInt32 button1 = 0;
	static id pool;
	static int initer = 0;
	
	if(initer == 0)
	{
		// one shot init
		initer = 1;
		pool = [[NSAutoreleasePool alloc] init];
	}
	
	//struct MIDIPacketList {UInt32 numPackets;MIDIPacket packet[1];};
	//struct MIDIPacket {MIDITimeStamp timeStamp;UInt16 length;Byte data[256];};
	for(UInt32 ipacket = 0; ipacket < pktlist->numPackets; ++ipacket)
	{
		verntl(midiBenjamin,5,"packet " << ipacket << " - time stamp = " << pktlist->packet[ipacket].timeStamp << "- length = " << pktlist->packet[ipacket].length);
		for(UInt16 ibyte = 0; ibyte < pktlist->packet[ipacket].length; ++ibyte)
		{
			vertl(midiBenjamin,5,pktlist->packet[ipacket].data[ibyte]);
		}
		vertl(midiBenjamin,5,"|");
		SInt32 packetLen = pktlist->packet[ipacket].length;
		SInt32 nbCmds =  packetLen / 3;
		if((packetLen % 3) != 0)
		{
//			UInt8 status = *(pktlist->packet[ipacket].data);
//			ZzAssert(status == 0xfe); // we discard active sensing
//			ZzAssert(packetLen == 1); // we discard active sensing
		}
		else
		{
			for( SInt32 cmd = 0; cmd < nbCmds; ++cmd)
			{
				SInt32 iController; // the controller number on the 4control. from 0 to 15
				UInt8 byte0,byte1,byte2;
				byte0 = pktlist->packet[ipacket].data[3*cmd+0];
				byte1 = pktlist->packet[ipacket].data[3*cmd+1];
				byte2 = pktlist->packet[ipacket].data[3*cmd+2];
				ZzAssert(byte0 = 186);
				if( byte1 == 36 && byte2 == 127)
					button1 = 1;
				else if( byte1 == 36 && byte2 == 0)
					button1 = 0;
				else if( byte1 == 37 && byte2 == 127)
					button0 = 1;
				else if( byte1 == 37 && byte2 == 0)
					button0 = 0;
				else // it's a knob
				{						
					SInt32 iKnob = byte1 - 32;
					// the param number depends on the button state
					if( button0 == 0 && button1 == 0)
						iController = iKnob;
					else if( button0 == 1 && button1 == 0)
						iController = 4 + iKnob;
					else if( button0 == 1 && button1 == 1)
						iController = 8 + iKnob;
					else if( button0 == 0 && button1 == 1)
						iController = 12 + iKnob;
					else
					{
						iController = 0;
						ZzAssert(0);
					}
					
					SInt32 intIncr = byte2 - 64;
					// make it so that 120 increments drive from the min value
					// to the max. So, the float per increment is (max val - min val) / 120
					
					// iController is between 0 and 15
					// iController / 4 gives the plugin number
					TArmeWSObj* processor = GetProcessorFromControllerNumber(iController);
					
					if(processor != NULL)
					{
						// iController % 4 gives the param number for the plugin
						SInt32 iParam = GetParamNumberFromControllerNumber(iController);
						
						if(iParam < processor->GetAudioPluginClass()->GetNbParams())
						{
							ARPIParamInfo paramInfo;
							processor->GetAudioPluginClass()->GetParamInfo(&paramInfo,iParam);
										
							float floatIncr = (float)intIncr*(paramInfo.maxVal-paramInfo.minVal)/256.0;
							
							processor->IncrAudioPluginParamValue(iParam,floatIncr);
							
							id view = (id)(fProcessorToViewMap.GetValueForKey(processor));
							if(view)
							{
								ZzAssert(0);
	//							[(ARPIBlueWindow*)view syncViewControlsFromPluginParam:iParam];
							}
						}
					}
				}
			}
		}
	}
	verntl(midiBenjamin,5," ");
}

void TARPIHost::SpawnMIDIThread()
{
	debugMIDIPrintSystemInfo();
	
	ItemCount ndev = MIDIGetNumberOfDevices();
	if(ndev==0)
	{
		vertl(midiBenjamin,2,"TARPIHost::SpawnMIDIThread() : there are no MIDI devices on this machine\n");
		fHasMidiSupport = false;
		return;
	}
	fHasMidiSupport = true;
	
	// we take the first source of the first entity of the first device.
	MIDIDeviceRef device = MIDIGetDevice(0);
	if(device == NULL)
	{
		vertl(midiBenjamin,0,"TARPIHost::SpawnMIDIThread() : error while calling MIDIGetDevice(0)\n");
		return;
	}
	
	MIDIEntityRef entity = MIDIDeviceGetEntity(device,0);
	if(entity == NULL)
	{
		vertl(midiBenjamin,0,"TARPIHost::SpawnMIDIThread() : error while calling MIDIDeviceGetEntity(device,0)");
		return;
	}
	fMIDIEndPointRefSrc = MIDIEntityGetSource(entity,0);

	// we don't listen to syswide notifications right now.
	chkerrcm(MIDIClientCreate( CFSTR("CoreMIDITest1"), NULL, NULL, &fMIDIClientRef ));
	
	chkerrcm(MIDIInputPortCreate(
		fMIDIClientRef,CFSTR("CoreMIDITest1 only port"),myMIDIReadProc,NULL,&fMIDIPortRef));
			
	chkerrcm(MIDIPortConnectSource(fMIDIPortRef,fMIDIEndPointRefSrc,0));
}

TARPIHost::~TARPIHost()
{
	// close windows here
//	if(fHasMidiSupport)
//	{
//		chkerrcm(MIDIPortDisconnectSource(fMIDIPortRef,fMIDIEndPointRefSrc));
//		chkerrcm(MIDIClientDispose(fMIDIClientRef));
//	}
}

TArmeWSObj* TARPIHost::GetProcessor(void* view)
{
	return 	(TArmeWSObj*)(fViewToProcessorMap.GetValueForKey((void*)view));
}

void TARPIHost::DeleteView(void* view)
{
	void* processor = fViewToProcessorMap.GetValueForKey(view);
	fViewToProcessorMap.DeletePairForKey(view);
	fProcessorToViewMap.SetValueForKey(processor,NULL);
}

TArmeWSObj* TARPIHost::GetProcessorFromControllerNumber(SInt32 controller)
{
	// right now we return the first plugin
	// $$$TODO hack for 4control
	int iProcessor = controller / 4;
	if( iProcessor < fProcessorToViewMap.GetNbPairs())
		return (TArmeWSObj*)fProcessorToViewMap.GetKey(iProcessor);
	else
		return NULL;
}

SInt32 TARPIHost::GetParamNumberFromControllerNumber(SInt32 controller)
{	
	// $$$TODO hack for 4control
	return controller % 4;
}

TARPIHost::TARPIHost()
{
	fHasMidiSupport = false;
//	SpawnMIDIThread();
}

VARPIPluginClass* TARPIHost::GetAudioPluginClassFromSig(const VARPIPluginClassSig& sig)
{
	VARPIPluginClass* firstFoundPluginClass = NULL;
	for(SInt32 iset = 0; iset < GetNbPluginSets(); iset++)
	{
		VARPIPluginSet* set = GetPluginSet(iset);
		for(SInt32 iPluginClass = 0; iPluginClass < set->GetNbPluginClasses(); iPluginClass++)
		{
			VARPIPluginClass* pluginClass = set->GetPluginClass(iPluginClass);
			if(pluginClass->GetSig().IsCompatibleWithArchivedPluginSig(sig))
			{
				ZzAssert(firstFoundPluginClass == NULL);
				return pluginClass;
			}
		}
	}
	return NULL;
}

void TARPIHost::CloseWindowForProcessor(TArmeWSObj* processor)
{
	for(int i = 0; i < fProcessorToViewMap.GetNbPairs(); i++)
	{
		TArmeWSObj* thisProcessor = [(MIObject*)(fProcessorToViewMap.GetKey(i)) WSObject];
		if(processor == thisProcessor)
		{
			id view = (id)(fProcessorToViewMap.GetValue(i));
			if(view)
			{	
				NSWindow* window = [view window];
				[window close]; // this will call the notifs that will remove the view blah blah
			}
		}
	}
}






