/*
 * AMwindow.c: Subtask für Parameterfenster
 *
 * Auto: make AMwindow.o
 *
 */


#include "AM.h"
#include <exec/execbase.h>
#include <exec/tasks.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <graphics/gfxmacros.h>
#include <utility/tagitem.h>
#include <libraries/gadtools.h>

#if 0
# define D(debug) debug
#else
# define D(debug)
#endif

#define abbruch SIGBREAKF_CTRL_C
#define MAX(a,b) ( ((a) > (b)) ? (a) : (b) )


/*------------------------- Daten anderer Module...*/


extern struct ExecBase	*SysBase;
extern struct Library	*DOSBase;

extern long			channel, instr,
					freqmitte, freqabw,
					volmitte, volabw;
extern double		pausen, tonaus;
extern char			skala[13], cskala[12][13];


/*------------------------- Daten dieses Moduls...*/


static struct TextAttr Topaz80 =
{
	"topaz.font",			/*  Name */
	8,						/*  YSize */
	0,						/*  Style */
	0,						/*  Flags */
};

struct GfxBase			*GfxBase = NULL;
struct IntuitionBase	*IntuitionBase = NULL;
struct Library			*GadToolsBase = NULL;

struct TextFont			*font = NULL;
struct Screen			*mysc = NULL;
struct Gadget			*glist = NULL;
struct Window			*mywin = NULL;
void					*vi = NULL;
static struct Task		*windowtask = NULL,
						*maintask = NULL;

static ULONG			topborder = 0,
						bottomEdge = 0;

enum {
	GAD_KANAL = 0, GAD_INSTR, GAD_FREQMITTE, GAD_FREQABW,
	GAD_VOLMITTE, GAD_VOLABW, GAD_PAUSEN, GAD_TONAUS, GAD_SKALA,
	GAD_CSKALA_LIST, GAD_CSKALA_STRING,
	GAD_ANZAHL
};

struct Gadget *mgg[ GAD_ANZAHL ];


char					lt_skala[12][16];	/* Texte für ListView */
struct List				lh_skala;			/* ListHeader         */
struct Node				nd_skala[12];		/* Knoten für Liste   */


/*------------------------- Code-Deklarationen...*/


extern void ProgChange( int );
extern void AllNotesOff( void );
extern LONG Printf( UBYTE *, ... );

BOOL subinit( void );
void subclosedown( void );
void handlewindowmsg( void );
struct Gadget *CreateAllGadgets(struct Gadget **, void *, UWORD);
void closewindow( void );


/*------------------------- Stub-Code...*/


struct Gadget *CreateGadget( unsigned long kind, struct Gadget *gad,
	struct NewGadget *ng, Tag tag1, ... )
{
	return CreateGadgetA( kind, gad, ng, (struct TagItem *)&tag1 );
}

APTR GetVisualInfo( struct Screen *screen, Tag tag1, ... )
{
	return GetVisualInfoA( screen, (struct TagItem *)&tag1 );
}

struct Window *OpenWindowTags( struct NewWindow *newWindow,
	unsigned long tag1Type, ... )
{
	return OpenWindowTagList( newWindow, (struct TagItem *)&tag1Type );
}

void GT_SetGadgetAttrs( struct Gadget *gad, struct Window *win,
	struct Requester *req, Tag tag1, ... )
{
	GT_SetGadgetAttrsA( gad, win, req, (struct TagItem *)&tag1 );
}


/*------------------------- Code-Definitionen...*/


static void windowtaskcode( void )
{
	long wmsg=0, sgns;

	geta4();
	
	if( !subinit() ) return;
	wmsg = 1 << mywin->UserPort->mp_SigBit;

	while(1)
	{
	
		sgns = Wait( wmsg | abbruch );
		
		if( sgns & wmsg )
		{
			handlewindowmsg();
			SetSignal( 0, wmsg );
		}
		
		if( sgns & abbruch )
		{
			subclosedown();
			mywin = 0;
			return;
		}
		
	}
}


/* CreateAllGadgets()
 *
 * Diese Routine führt alle Gadget-Initialisierungen aus.
 * glistptr ist ein auf NULL initialisierter GadgetList-Zeiger
 * Rückgabe ist ein Zeiger auf das zuletzt erzeugte Gadget (also
 * NULL bei Fehler)
 *
 */

static struct Gadget *CreateAllGadgets(
	struct Gadget **glistptr,
	void *vi,
	UWORD topborder )
{
	struct NewGadget ng;
	struct Gadget *gad;
	int i;

	/*  All the gadget creation calls accept a pointer to the previous
		gadget, and link the new gadget to that gadget's NextGadget field.
		Also, they exit gracefully, returning NULL, if any previous gadget
		was NULL.  This limits the amount of checking for failure that
		is needed.  You only need to check before you tweak any gadget
		structure or use any of its fields, and finally once at the end,
		before you add the gadgets. */

	/*  We obligingly perform the following operation, required of
		any program that uses the toolkit.  It gives the toolkit a
		place to stuff context data: */
	gad = CreateContext(glistptr);

	/*  Since the NewGadget structure is unmodified by any of the
		CreateGadget() calls, we need only change those fields which
		are different. */

	ng.ng_LeftEdge = 72;
	ng.ng_TextAttr = &Topaz80;
	ng.ng_VisualInfo = vi;
	ng.ng_Width = 120;
	ng.ng_Height = 12;
	ng.ng_Flags = 0;

	ng.ng_TopEdge = topborder + INTERHEIGHT;
	ng.ng_GadgetText = "Kanal:";
	ng.ng_GadgetID = GAD_KANAL;
	
	mgg[GAD_KANAL] = gad = CreateGadget(SLIDER_KIND, gad, &ng,
		GTSL_Min, 1,
		GTSL_Max, 16,
		GTSL_Level, channel+1,
		GTSL_LevelFormat, "%2ld",
		GTSL_LevelPlace, PLACETEXT_RIGHT,
		GTSL_MaxLevelLen, 2,
		GA_IMMEDIATE, TRUE,
		GA_RELVERIFY, TRUE,
		TAG_DONE);

	ng.ng_TopEdge += 12+INTERHEIGHT;
	ng.ng_GadgetText = "Instr:";
	ng.ng_GadgetID = GAD_INSTR;

	mgg[GAD_INSTR] = gad = CreateGadget(SLIDER_KIND, gad, &ng,
		GTSL_Min, 0,
		GTSL_Max, 64,
		GTSL_Level, instr+1,
		GTSL_LevelFormat, "%2ld",
		GTSL_LevelPlace, PLACETEXT_RIGHT,
		GTSL_MaxLevelLen, 2,
		GA_RELVERIFY, TRUE,
		TAG_DONE);

	ng.ng_TopEdge += 12+2*INTERHEIGHT;
	ng.ng_GadgetText = "F-Ber:";
	ng.ng_GadgetID = GAD_FREQMITTE;

	mgg[GAD_FREQMITTE] = gad = CreateGadget(SCROLLER_KIND, gad, &ng,
		GTSC_Top, freqmitte-freqabw,
		GTSC_Total, 128,
		GTSC_Visible, freqabw*2,
		GA_RELVERIFY, TRUE,
		TAG_DONE);

	ng.ng_TopEdge += 12+INTERHEIGHT;
	ng.ng_GadgetText = "F-Abw:";
	ng.ng_GadgetID = GAD_FREQABW;

	mgg[GAD_FREQABW] = gad = CreateGadget(SLIDER_KIND, gad, &ng,
		GTSL_Min, 0,
		GTSL_Max, 63,
		GTSL_Level, freqabw,
		GTSL_LevelFormat, "±%3ld½",
		GTSL_LevelPlace, PLACETEXT_RIGHT,
		GTSL_MaxLevelLen, 5,
		GA_RELVERIFY, TRUE,
		TAG_DONE);

	ng.ng_TopEdge += 12+2*INTERHEIGHT;
	ng.ng_GadgetText = "V-Ber:";
	ng.ng_GadgetID = GAD_VOLMITTE;

	mgg[GAD_VOLMITTE] = gad = CreateGadget(SCROLLER_KIND, gad, &ng,
		GTSC_Top, volmitte-volabw,
		GTSC_Total, 128,
		GTSC_Visible, volabw*2,
		GA_RELVERIFY, TRUE,
		TAG_DONE);

	ng.ng_TopEdge += 12+INTERHEIGHT;
	ng.ng_GadgetText = "V-Abw:";
	ng.ng_GadgetID = GAD_VOLABW;

	mgg[GAD_VOLABW] = gad = CreateGadget(SLIDER_KIND, gad, &ng,
		GTSL_Min, 0,
		GTSL_Max, 63,
		GTSL_Level, volabw,
		GTSL_LevelFormat, "±%3ld½",
		GTSL_LevelPlace, PLACETEXT_RIGHT,
		GTSL_MaxLevelLen, 5,
		GA_RELVERIFY, TRUE,
		TAG_DONE);

	ng.ng_TopEdge += 12+2*INTERHEIGHT;
	ng.ng_GadgetText = "Pausen:";
	ng.ng_GadgetID = GAD_PAUSEN;

	mgg[GAD_PAUSEN] = gad = CreateGadget(SLIDER_KIND, gad, &ng,
		GTSL_Min, 0,
		GTSL_Max, 100,
		GTSL_Level, (long)(pausen*100),
		GTSL_LevelFormat, "%3ld %%",
		GTSL_LevelPlace, PLACETEXT_RIGHT,
		GTSL_MaxLevelLen, 5,
		GA_RELVERIFY, TRUE,
		TAG_DONE);

	ng.ng_TopEdge += 12+INTERHEIGHT;
	ng.ng_GadgetText = "TonAus:";
	ng.ng_GadgetID = GAD_TONAUS;

	mgg[GAD_PAUSEN] = gad = CreateGadget(SLIDER_KIND, gad, &ng,
		GTSL_Min, 0,
		GTSL_Max, 100,
		GTSL_Level, (long)(tonaus*100),
		GTSL_LevelFormat, "%3ld %%",
		GTSL_LevelPlace, PLACETEXT_RIGHT,
		GTSL_MaxLevelLen, 5,
		GA_RELVERIFY, TRUE,
		TAG_DONE);

	ng.ng_TopEdge += 12+INTERHEIGHT;
	ng.ng_GadgetText = "Skala:";
	ng.ng_GadgetID = GAD_SKALA;
	ng.ng_Height = 14;

	mgg[GAD_SKALA] = gad = CreateGadget(STRING_KIND, gad, &ng,
		GTST_MaxChars, 12,
		GTST_String, skala,
		STRINGA_ReplaceMode, TRUE,
		TAG_DONE);

	/* String-Gadget für ListView: */
	ng.ng_GadgetText = NULL;
	ng.ng_GadgetID = GAD_CSKALA_STRING;
	ng.ng_Height = 14;
	ng.ng_Width += 40;

	mgg[GAD_CSKALA_STRING] = gad = CreateGadget(STRING_KIND, gad, &ng,
		GTST_MaxChars, 15,
		STRINGA_ReplaceMode, TRUE,
		TAG_DONE);

	/* Liste der Continue-Skalen */
	ng.ng_TopEdge += 12+2*INTERHEIGHT;
	ng.ng_GadgetText = "CSkala:";
	ng.ng_Flags = PLACETEXT_LEFT;
	ng.ng_GadgetID = GAD_CSKALA_LIST;
	ng.ng_Height = 4 + 8 * 9 + 14;
	
	mgg[GAD_CSKALA_LIST] = gad = CreateGadget(LISTVIEW_KIND, gad, &ng,
		LAYOUTA_SPACING, 1,
		GTLV_Labels, &lh_skala,
		GTLV_ShowSelected, mgg[GAD_CSKALA_STRING],
		TAG_DONE);

	
	i = GAD_CSKALA_STRING;
	bottomEdge = mgg[i]->TopEdge + mgg[i]->Height + 2*INTERHEIGHT;
	
	
	return(gad);
}


static BOOL subinit( void )
// Fenster öffnen etc.
{
	// zunächst mal probieren, ob wir ein Fenster kriegen können...
	mywin = OpenWindowTags(NULL,
		WA_Title, "AM Parameter",
		WA_Width, 250,
		WA_Height, bottomEdge,
		
		WA_MinWidth, 50,
		WA_MinHeight, 50,
		WA_MaxWidth, 250,
		WA_MaxHeight, bottomEdge,
		
		WA_Activate, TRUE,
		WA_DragBar, TRUE,
		WA_DepthGadget, TRUE,
		WA_CloseGadget, TRUE,
		WA_SimpleRefresh, TRUE,
		
		WA_IDCMP, CLOSEWINDOW | REFRESHWINDOW | \
			SLIDERIDCMP | SCROLLERIDCMP | STRINGIDCMP | \
			LISTVIEWIDCMP,
		
		TAG_DONE);
	
	// Na? Haben wir's?
	if( !mywin )
		return FALSE;

	// Ja! Nun Gadgets anhängen und refreshen...
	AddGList( mywin, glist, 0, -1, NULL );
	RefreshGList( glist, mywin, NULL, -1 );
	GT_RefreshWindow( mywin, NULL );
	
	return TRUE;
}


static void subclosedown( void )
{
	if(mywin) CloseWindow(mywin); mywin=0;
}


static BOOL HandleGadgetEvent(
	struct Gadget *gad,
	UWORD code )
{
	char *s;
	long i;
	
	switch (gad->GadgetID)
	{
		case GAD_KANAL:
			// unkritisch
			AllNotesOff();
			channel = (WORD) code - 1;
			break;

		case GAD_INSTR:
			instr = (WORD) code - 1;
			if( instr>=0 ) ProgChange( instr );
			break;

		case GAD_FREQMITTE:
			// unkritisch
			freqmitte = (WORD) code + freqabw;
			break;
		case GAD_FREQABW:
			freqabw = (WORD) code;
			// Grenzen neu checken
			if( freqmitte+freqabw > 127 )
				freqmitte = 127 - freqabw;
			else if( freqmitte-freqabw < 0 )
				freqmitte = freqabw;
			// Scroller ändern
			GT_SetGadgetAttrs( mgg[GAD_FREQMITTE], mywin, NULL,
				GTSC_Top, freqmitte-freqabw,
				GTSC_Visible, freqabw*2,
				TAG_DONE );
			break;

		case GAD_VOLMITTE:
			// unkritisch
			volmitte = (WORD) code + volabw;
			break;
		case GAD_VOLABW:
			volabw = (WORD) code;
			// Grenzen neu checken
			if( volmitte+volabw > 127 )
				volmitte = 127 - volabw;
			else if( volmitte-volabw < 0 )
				volmitte = volabw;
			// Scroller ändern
			GT_SetGadgetAttrs( mgg[GAD_VOLMITTE], mywin, NULL,
				GTSC_Top, volmitte-volabw,
				GTSC_Visible, volabw*2,
				TAG_DONE );
			break;

		case GAD_PAUSEN:
			// unkritisch
			pausen = (WORD) code / 100.0;
			break;

		case GAD_TONAUS:
			// unkritisch
			tonaus = (WORD) code / 100.0;
			break;

		case GAD_SKALA:
			s = ((struct StringInfo *)gad->SpecialInfo)->Buffer;
			// 12 Zeichen?
			if( strlen(s)==12 )
				strcpy( skala, s );
			else
				GT_SetGadgetAttrs( gad, mywin, NULL,
					GTST_String, skala,
					TAG_DONE );
			break;
		
		case GAD_CSKALA_LIST:
			ActivateGadget( mgg[GAD_CSKALA_STRING], mywin, NULL );
			break;
		
		case GAD_CSKALA_STRING:
			s = ((struct StringInfo *)gad->SpecialInfo)->Buffer;
			i = atol(s);
			
			if( s[2]==':' && i>=0 && i<=11 )
			{
				if( strlen(s)==15 )
				{
					// komplett -> einbauen bzw. ersetzen
					strcpy(   cskala[i], s+3 );
					strcpy( lt_skala[i], s   );
					
					GT_SetGadgetAttrs( mgg[GAD_CSKALA_LIST], mywin, NULL,
						GTLV_Labels, &lh_skala,
						TAG_DONE );
					RefreshGList( mgg[GAD_CSKALA_LIST], mywin, NULL, 1 );
				}
				else if( s[3] == 0 )
				{
					// nur noch Index -> löschen
					  cskala[i][0] = 0;
					lt_skala[i][3] = 0;
					
					GT_SetGadgetAttrs( mgg[GAD_CSKALA_LIST], mywin, NULL,
						GTLV_Labels, &lh_skala,
						TAG_DONE );
					RefreshGList( mgg[GAD_CSKALA_LIST], mywin, NULL, 1 );
				}
			}
			break;
		
	}
}


static void handlewindowmsg( void )
{
	struct IntuiMessage *imsg;
	struct Gadget *gad;
	ULONG imsgClass;
	UWORD imsgCode;
	
	while( imsg = GT_GetIMsg(mywin->UserPort) )
	{
		imsgClass = imsg->Class;
		imsgCode = imsg->Code;
		gad = (struct Gadget *)imsg->IAddress;
		// ^^^ natürlich nur bei Gadgets gültig
		GT_ReplyIMsg(imsg);
		
		switch (imsgClass)
		{
			case MOUSEMOVE:
			case GADGETUP:
				HandleGadgetEvent(gad, imsgCode);
				break;
			
			case CLOSEWINDOW:
				// Nur HauptTask signalisieren
				Signal( maintask, SIGBREAKF_CTRL_C );
				break;
			
			case REFRESHWINDOW:
				GT_BeginRefresh(mywin);
				GT_EndRefresh(mywin, TRUE);
				break;
		}
	}
}


//---------------------- Nach außen sichtbare Funktionen:


BOOL startwindow( void )
// Libs öffnen, etc., Task starten
{
	int i;

	if( !(GfxBase = (struct GfxBase *)
					OpenLibrary("graphics.library", 36L)))
		return( FALSE );

	if( !(IntuitionBase = (struct IntuitionBase *)
					OpenLibrary("intuition.library", 36L))) {
		closewindow();
		return( FALSE ); }
		

	if( !(GadToolsBase = OpenLibrary("gadtools.library", 36L))) {
		closewindow();
		return( FALSE ); }

	if( !(font = OpenFont(&Topaz80))) {
		closewindow();
		return( FALSE ); }

	if( !(mysc = LockPubScreen(NULL))) {
		closewindow();
		return( FALSE ); }

	if( !(vi = GetVisualInfo(mysc,TAG_DONE))) {
		closewindow();
		return( FALSE ); }

	topborder = mysc->WBorTop + (mysc->Font->ta_YSize + 1);
	
	NewList( &lh_skala );
	for( i=0; i<=11; i++ )
	{
		sprintf( lt_skala[i], "%02d:%s", i, cskala[i] );
		nd_skala[i].ln_Name = lt_skala[i];
		AddTail( &lh_skala, &nd_skala[i] );
	}
	
	if( !CreateAllGadgets(&glist, vi, topborder)) {
		closewindow();
		return( FALSE ); }
	
	maintask = SysBase->ThisTask;
	windowtask = CreateTask( "AMsub", 0, windowtaskcode, 4000 );
	return( windowtask != 0 );
}


void closewindow( void )
{
	// SubTask bescheid sagen
	if(windowtask)
	{
		Signal( windowtask, SIGBREAKF_CTRL_C );
		while( mywin ) Delay(25);
		windowtask=0;
	}

	if(GadToolsBase)
	{
		FreeVisualInfo(vi); vi=0;
		FreeGadgets(glist); glist=0;
		CloseLibrary(GadToolsBase); GadToolsBase=0;
	}

	if(mysc) UnlockPubScreen(NULL, mysc); mysc=0;
	if(font) CloseFont(font); font=0;

	if(IntuitionBase) CloseLibrary(IntuitionBase); IntuitionBase=0;
	if(GfxBase) CloseLibrary(GfxBase); GfxBase=0;
}
