/*
 *	SpaceScope V1.0
 *
 *  Sources are copyrighted by Tinic Urou in 1997. All rights reserved.
 */

#include <InterfaceKit.h>
#include <KernelKit.h>
#include <AppKit.h>
#include "Scope.h"

#define SCOPE_NAME 		"MonoScope"			// No spaces allowed here!
#define SCOPE_STRING 	"MonoScope"
#define SCOPE_OPEN 		"MonoScopeOpen"
#define SCOPE_ADC 		"MonoScopeADC"
#define SCOPE_FAST 		"MonoScopeFast"
#define MIK_ADC			'mkad'
#define MIK_DAC			'mkda'
#define MIK_FAST		'mkfs'

class Scope;
class ScopeView;

struct instance_struct						// instance of our class
{
	Scope *win;
	BLooper *looper;
	int isopen;
	BMessage *prefs;
};

class ScopeView : public BView				// We need only MouseDown
{
	private:

	BPopUpMenu		*menu;
	
	public:

					ScopeView(BRect rect, BPopUpMenu *m):BView(rect,"Scope View",NULL,NULL)
					{
						menu=m;
					};

	void			MouseDown(BPoint where)
					{
						ulong buttons;
						GetMouse(&where,&buttons);
						if(buttons==B_SECONDARY_MOUSE_BUTTON)
						{	
							ConvertToScreen(&where);
							menu->Go(where-BPoint(2,2),TRUE,TRUE,Frame());
						}
					};					
};

class Scope : public BWindow				// The Scope itself
{
	private:	

	BSubscriber *eqs;
	BDACStream *dac;
	BADCStream *adc;
	instance_struct *inst;

	char			colEq[65];
	char 			actEq[257];
	char			oldEq[257];
	long			eqBufferSize;
	ScopeView		*eqview;
	BBitmap			*eqbitmap;
	thread_id		eqthread;
	bool			fast;
	BMenuItem  		*fastitem;
	BMenuItem  		*dacitem;
	BMenuItem  		*adcitem;
	Object			*o;
	bool			quit;
	BScreen			*screen;
	
	public:	

					Scope(BRect frame, instance_struct *i, Object *obj):BWindow(frame,SCOPE_NAME,B_TITLED_WINDOW,B_NOT_ZOOMABLE|B_NOT_RESIZABLE)
					{
						inst=i;
						o=obj;

						screen = new BScreen();

						quit=FALSE;

						if(inst->prefs)
						{
							BRect r=inst->prefs->FindRect(SCOPE_STRING);
							MoveTo(r.left,r.top);
						}
						ResizeTo(255,127);

						fast=inst->prefs->FindBool(SCOPE_FAST);
						
						inst->isopen=TRUE;
						inst->looper->PostMessage(S_LOOPER_CLOSE);

						BPopUpMenu *menu 	= new BPopUpMenu("", FALSE, FALSE);
						BMenuItem  *item;
						dacitem = item 		= new BMenuItem("DAC stream", new BMessage(MIK_DAC),NULL);
						menu->AddItem(item);
						adcitem = item 		= new BMenuItem("ADC CD input stream", new BMessage(MIK_ADC),NULL);
						menu->AddItem(item);
						item = fastitem 	= new BMenuItem("Fast scope", new BMessage(MIK_FAST),NULL);
						menu->AddItem(item);
						menu->SetTargetForItems(this);

						item->SetMarked(fast);
						if(inst->prefs->FindBool(SCOPE_ADC))
							adcitem->SetMarked(TRUE);
						else
							dacitem->SetMarked(TRUE);

						AddChild(eqview	= new ScopeView(BRect(0,0,255,127),menu));
						eqbitmap		= new BBitmap(BRect(0,0,255,127),B_COLOR_8_BIT);
						Show();

						eqs  = new BSubscriber("Space Scope Subscriber");
						dac = new BDACStream();
						adc = new BADCStream();
						adc->SetADCInput(B_CD_IN);

						ReenterStream(inst->prefs->FindBool(SCOPE_ADC));
						eqthread=spawn_thread((thread_entry)_EqualizerThread,"Space Scope Thread",B_LOW_PRIORITY,this);
						resume_thread(eqthread);	
					};
					
	void			ReenterStream(bool adctrue)
					{
						inst->prefs->ReplaceBool(SCOPE_ADC,adctrue);
	
						eqs->ExitStream(TRUE);
						eqs->Unsubscribe();
						
						if(eqs->Subscribe(adctrue ? (BAbstractBufferStream*)adc : (BAbstractBufferStream*)dac) == B_NO_ERROR);
						{
							eqs->EnterStream
							(
								NULL, 		
						   	 	FALSE, 		
					 	   	 	this, 		
					 	   	 	(enter_stream_hook)_PerformEqualizer, 
					  	 		NULL, 		
					 	   	 	TRUE  		
							);
						}
					};
					
	void			MessageReceived(BMessage *msg)
					{
						switch(msg->what)
						{
							case	MIK_DAC:
									ReenterStream(FALSE);
									adcitem->SetMarked(FALSE);
									dacitem->SetMarked(TRUE);							
									return;
							case	MIK_ADC:
									ReenterStream(TRUE);
									adcitem->SetMarked(TRUE);
									dacitem->SetMarked(FALSE);
									return;
							case	MIK_FAST:
									if(fast)	fast=FALSE;
									else		fast=TRUE;
									inst->prefs->ReplaceBool(SCOPE_FAST,fast);
									fastitem->SetMarked(fast);
									return;
							default:
									BWindow::MessageReceived(msg);
									return;
						}
					};
					
	void			FrameMoved(BPoint p)
					{
						if(inst->prefs)
						{
							inst->prefs->RemoveName(SCOPE_STRING);
							inst->prefs->AddRect(SCOPE_STRING,Frame());
						}
					};
					
	void 			Equalizer(void)
					{
						char 		*loc,*bits=(char *)eqbitmap->Bits(),tmp,foo;
						char		*cols=colEq,*oldset=oldEq,*newset=actEq;
						int			bar;
						BView		*view=eqview;
						BWindow		*win=view->Window();
						BBitmap		*bmp=eqbitmap;

						for(int i=0;i<32;i++)
						{
							colEq[i]	=screen->IndexForColor(i*8,255,		0,0);
							colEq[i+32]	=screen->IndexForColor(255,(31-i)*8,	0,0);
						}						
						
						for(;;)
						{
							memset(bits,0,256*128);
							memcpy(oldset,newset,256); 
							
							if(fast)snooze(10000);
							else	snooze(50000);
							
							for(int i=0;i<256;i++)
							{
								foo=oldEq[i];
								bar=(i+((foo+64)<<8));
								if((bar>0)&&(bar<(256*128)))
								{
									loc=bits+bar;
									if(foo<0)
									{
										for(int h=0;h<=(-foo);h++)
										{
											*loc=colEq[h]; loc+=256;
										}
									}
									else
									{
										for(int h=0;h<=foo;h++)
										{
											*loc=colEq[h]; loc-=256;
										}
									}
								}
							}
							win->Lock();
							view->DrawBitmap(bmp);
							win->Unlock();
						}
					};
					
	bool 			Perform(short *sound, long size)
					{
						char	*newset;
						int		bufsize=size/256/sizeof(short)/2;
						if(sound)
						{
							for(int k=0;k<bufsize;k++)
							{
								newset=actEq;
								for(short i=0;i<256;i++)
								{
									*newset++=(char)(((int)sound[0]+(int)sound[1])>>9);
									sound+=2;
								}
							}
						}
						return TRUE;
					};

	static bool 	_PerformEqualizer(Scope *eq, short *sound, long size)
					{
						return eq->Perform(sound,size);
					};

	static void		_EqualizerThread(Scope *eq)
					{
						eq->Equalizer();
					};
					
	void			AllowQuit(bool q)
					{
						quit=q;
					};
					
	bool			QuitRequested()
					{	
						if(quit)
						{
							BWindow::QuitRequested();
							kill_thread(eqthread);
							eqs->ExitStream(TRUE);
							eqs->Unsubscribe();
							delete dac;
							delete adc;
							delete eqs;
							delete eqbitmap;
							delete screen;
							return TRUE;
						}
						else 
						{
							inst->isopen=FALSE;
							BMessage *msg=new BMessage(S_LOOPER_CLOSE);
							msg->AddInt32("Object",(long)o);
							inst->looper->PostMessage(msg);
							return FALSE;
						}
					};
};

/*
 	Dispatcher of our zOOnic class
*/

long class_dispatcher(Class *c, Object *o, ulong MethodID, ulong *data)
{
	instance_struct *inst=(instance_struct *)o->Instance(c);

	switch(MethodID)
	{
		case	B_METHOD_CONSTRUCT:				// like the C++ constructor method
				{
					inst->win=0;
					inst->isopen=FALSE;
					return 0;
				}

		case	S_OPEN:							// Open the scope
				{
					if(data[0])
					{
						if(inst->prefs)
						{
							if(!inst->win)
							{
								if(inst->prefs->FindBool(SCOPE_OPEN))
								{
									inst->win = new Scope(BRect(0,0,255,255),inst,o);
								}
							}
						}
					}
					else if(!inst->win)
					{
						inst->win = new Scope(BRect(0,0,255,255),inst,o);
					}
					return 0;
				}
				
		case	S_CLOSE:						// Close the scope
				{
					if(data[0])
					{
						if(inst->prefs)
						{
							if(inst->win)
								inst->prefs->ReplaceBool(SCOPE_OPEN,TRUE);
							else
								inst->prefs->ReplaceBool(SCOPE_OPEN,FALSE);	
						}
					}
					if(inst->win)
					{
						inst->win->AllowQuit(TRUE);
						inst->win->PostMessage(B_QUIT_REQUESTED);
						inst->isopen=FALSE;
						inst->win=0;
					}
					return 0;
				}
				
		case	S_SETATTR:						// Set variables in the instance
				{
					ulong ATTR =(ulong)data[0];
					ulong DATA =(ulong)data[1];

					switch(ATTR)
					{
						case	S_ATTR_PREFS:
								inst->prefs=(BMessage *)DATA;
								if(!inst->prefs->HasBool(SCOPE_OPEN)) 	inst->prefs->AddBool(SCOPE_OPEN,FALSE);
								if(!inst->prefs->HasBool(SCOPE_ADC)) 	inst->prefs->AddBool(SCOPE_ADC,FALSE);
								if(!inst->prefs->HasBool(SCOPE_FAST)) 	inst->prefs->AddBool(SCOPE_FAST,FALSE);
								if(!inst->prefs->HasRect(SCOPE_STRING)) inst->prefs->AddRect(SCOPE_STRING,BRect(40,80,200,200));
								break;
						case	S_ATTR_LOOPER:
								inst->looper=(BLooper *)DATA;
								break;
					}		
					o->DoSuperMethod(c,S_SETATTR,data);	
					return 0;	
				}

		case	S_GETATTR:						// Return variables from the instance
				{
					ulong ATTR =(ulong)data[0];
					ulong DATA =(ulong)data[1];

					switch(ATTR)
					{
						case	S_ATTR_ID:
								*((char **)DATA)=SCOPE_STRING;
								return 0;
						case	S_ATTR_ISOPEN:
								*((int *)DATA)=inst->isopen;
								return 0;
						default:
								return o->DoSuperMethod(c,S_GETATTR,data);
					}				
				}
				
		default:
				return o->DoSuperMethod(c,MethodID,data);
	}
}

char *class_superclass()
{
	return "dualplayer/scopes/basescope.zclass";
};

long class_instancesize()
{
	return sizeof(struct instance_struct);
};

long class_superversion()
{
	return 1;
};

long class_version()
{
	return 1;
};
