
#include <exec/types.h>
#include <exec/lists.h>
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/classes.h>
#include <dos/dos.h>
#include <midi/realtime.h>

#include <string.h>
#include <stdlib.h>
#include <stddef.h>

#include "trans.h"
#include "trans_rev.h"

#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/utility_protos.h>
#include <clib/realtime_protos.h>
#include <clib/alib_protos.h>
#include <clib/macros.h>

#include <pragmas/exec_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/gadtools_pragmas.h>
#include <pragmas/utility_pragmas.h>
#include <pragmas/realtime_pragmas.h>

char vers[] = VERSTAG;
struct Library *IntuitionBase;
struct Library *GadToolsBase;
struct Library *GfxBase;
struct Library *UtilityBase,
				*RealTimeBase;

struct IntuiText BodyText = {0,1,JAM2,20,8,NULL,(UBYTE *)TEXT_NORELEASE2,NULL};
struct IntuiText NegText  = {0,1,JAM2, 6,4,NULL,(UBYTE *)TEXT_OK,NULL};

	/* declarations for box image class */

Class					*box_class;
Class *initBoxClass(void);
void freeBoxClass(Class *);

extern struct TextFont digitsFont;

LONG					current_time;
WORD					clock_state = CLOCKSTATE_STOPPED;
struct Task				*main_task;

WORD					selected_digit = -1;

WORD					drag_state,
						drag_delay,
						drag_accum;

struct Gadget			*last_gadget;

struct PlayerInfo		*control_player;

	/* prototypes for tag functions */

struct PlayerInfo *CreatePlayer(Tag tag, ...)
{	return CreatePlayerA((struct TagItem *)&tag );
}

BOOL SetPlayerAttrs(struct PlayerInfo *pi, Tag tag, ...)
{	return SetPlayerAttrsA(pi, (struct TagItem *)&tag );
}

ULONG __asm __interrupt __saveds myHookFunc (
	register __a1 struct pmTime		*msg,
	register __a2 struct PlayerInfo *pi );

struct Hook myHook = {
	{ NULL, NULL },
	myHookFunc,
};

VOID main(int argc, char **argv)
  {
  struct TMData *TMData;
  ULONG error;

  main_task = FindTask(0);

  if(!(IntuitionBase = OpenLibrary((UBYTE *)"intuition.library", 37L)))
    {
    if(IntuitionBase = OpenLibrary((UBYTE *)"intuition.library", 0L))
      {
      AutoRequest(NULL, &BodyText, NULL, &NegText, 0, 0, 320, 80);
      CloseLibrary(IntuitionBase);
      }
    cleanexit(NULL, RETURN_FAIL);
    }

  if(!(GadToolsBase = OpenLibrary((UBYTE *)"gadtools.library", 37L)))
    {
    TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOLIBRARY, (UBYTE *)TEXT_ABORT, NULL, "gadtools.library V37");
    cleanexit(NULL, RETURN_FAIL);
    }

  if(!(UtilityBase = OpenLibrary((UBYTE *)"utility.library", 37L)))
    {
    TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOLIBRARY, (UBYTE *)TEXT_ABORT, NULL, "utility.library V37");
    cleanexit(NULL, RETURN_FAIL);
    }

  if(!(GfxBase = OpenLibrary((UBYTE *)"graphics.library", 37L)))
    {
    TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOLIBRARY, (UBYTE *)TEXT_ABORT, NULL, "graphics.library V37");
    cleanexit(NULL, RETURN_FAIL);
    }

  if(!(RealTimeBase = OpenLibrary((UBYTE *)"realtime.library", 0L)))
    {
    TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOLIBRARY, (UBYTE *)TEXT_ABORT, NULL, "realtime.library");
    cleanexit(NULL, RETURN_FAIL);
    }

  if (!(box_class = initBoxClass()))
    {
    TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOMEMORY, (UBYTE *)TEXT_ABORT, NULL, NULL);
    cleanexit(NULL, RETURN_FAIL);
    }

  control_player = CreatePlayer(
      PLAYER_Name, "Controls",
      PLAYER_Conductor, "Main",
      PLAYER_Hook, &myHook,
      TAG_DONE );

  if (!(control_player))
    {
    TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOPLAYER, (UBYTE *)TEXT_ABORT, NULL, NULL);
    cleanexit(NULL, RETURN_FAIL);
    }

  if(!(TMData = TM_Open(&error)))
    {
    switch(error)
      {
      case TMERR_MEMORY:
        TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOMEMORY, (UBYTE *)TEXT_ABORT, NULL, NULL);
        break;
      case TMERR_MSGPORT:
        TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOMSGPORT, (UBYTE *)TEXT_ABORT, NULL, NULL);
        break;
      }
    cleanexit(NULL, RETURN_FAIL);
    }

	NewList( &CDTList );

  if(!(OpenScreen_Workbench(TMData)))
    {
    TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOSCREEN, (UBYTE *)TEXT_ABORT, NULL, NULL);
    cleanexit(TMData, RETURN_FAIL);
    }

  if(!(OpenWindow_TRANSP(TMData)))
    {
    TM_Request(NULL, (UBYTE *)TEXT_ERROR, (UBYTE *)TEXT_NOWINDOW, (UBYTE *)TEXT_ABORT, NULL, NULL);
    cleanexit(TMData, RETURN_FAIL);
    }

/*  StopState(TMData); */
		/* Act as if we just had a state change, so that we can set the clock */

	HandleSignal( TMData, SIGBREAKF_CTRL_E );

  TM_EventLoop(TMData);

  cleanexit(TMData, RETURN_OK);
  }

VOID cleanexit(struct TMData *TMData, int returnvalue)
  {
  StripFont(&digitsFont);						/* because of my weird font		*/
  if (control_player) DeletePlayer(control_player);
  if (box_class) freeBoxClass(box_class);		/* delete BOOPSI image class	*/

  if(TMData)
    {
    CloseWindow_TRANSP(TMData);
    CloseWindow_SELECTCO(TMData);
    CloseScreen_Workbench(TMData);
    TM_Close(TMData);
    }

  if(RealTimeBase)  CloseLibrary(RealTimeBase);
  if(GfxBase)       CloseLibrary(GfxBase);
  if(UtilityBase)   CloseLibrary(UtilityBase);
  if(GadToolsBase)  CloseLibrary(GadToolsBase);
  if(IntuitionBase) CloseLibrary(IntuitionBase);

  exit(returnvalue);
  }

BOOL Window_TRANSP_CLOSEWINDOW(struct TMData *TMData, struct IntuiMessage *imsg)
  {
  last_gadget = NULL;
  return(TRUE);
  }

BOOL Window_TRANSP_MENUPICK(struct TMData *TMData, struct IntuiMessage *imsg)
  {
  UWORD menucode;
  struct MenuItem *menuitem;
  TMOBJECTDATA *tmobjectdata;
  BOOL (*eventfunc)(struct TMData *, struct IntuiMessage *, TMOBJECTDATA *);
  last_gadget = NULL;

  menucode = imsg->Code;
  while(menucode != MENUNULL)
    {
    menuitem = ItemAddress(WindowInfo_TRANSP.Menu, menucode);
    tmobjectdata = (TMOBJECTDATA *)(GTMENUITEM_USERDATA(menuitem));
    eventfunc = tmobjectdata->EventFunc;

	TMData->checked_menu = menuitem->Flags & CHECKED;

    if(eventfunc)
      {
      if((*eventfunc)(TMData, imsg, tmobjectdata)) return(TRUE);
      }

    menucode = menuitem->NextSelect;
    }
  return(FALSE);
  }

BOOL Window_TRANSP_GADGETDOWN(struct TMData *TMData, struct IntuiMessage *imsg)
  {
  struct Gadget *gadget;
  TMOBJECTDATA *tmobjectdata;
  BOOL (*eventfunc)(struct TMData *, struct IntuiMessage *, TMOBJECTDATA *);

  last_gadget =  gadget = (struct Gadget *)imsg->IAddress;
  tmobjectdata = (TMOBJECTDATA *)(gadget->UserData);
  eventfunc = tmobjectdata->EventFunc;

  if(eventfunc)
    {
    return((*eventfunc)(TMData, imsg, tmobjectdata));
    }
  return(FALSE);
  }

BOOL Window_TRANSP_GADGETUP(struct TMData *TMData, struct IntuiMessage *imsg)
  {
  struct Gadget *gadget;
  TMOBJECTDATA *tmobjectdata;
  BOOL (*eventfunc)(struct TMData *, struct IntuiMessage *, TMOBJECTDATA *);

  gadget = (struct Gadget *)imsg->IAddress;
  tmobjectdata = (TMOBJECTDATA *)(gadget->UserData);
  eventfunc = tmobjectdata->EventFunc;
  last_gadget = NULL;

	drag_state = DRAG_NONE;

  if(eventfunc)
    {
    return((*eventfunc)(TMData, imsg, tmobjectdata));
    }
  return(FALSE);
  }

BOOL Window_TRANSP_MOUSEMOVE(struct TMData *TMData, struct IntuiMessage *imsg)
  {
#if 0
  struct Gadget *gadget;
  TMOBJECTDATA *tmobjectdata;
  BOOL (*eventfunc)(struct TMData *, struct IntuiMessage *, TMOBJECTDATA *);

  gadget = (struct Gadget *)imsg->IAddress;
  tmobjectdata = (TMOBJECTDATA *)(gadget->UserData);
  eventfunc = tmobjectdata->EventFunc;
  last_gadget = NULL;
#endif

	if (drag_state == DRAG_DIGIT && selected_digit >= 0)
	{	LONG	scale[] = {	3600*600*10, 3600*600, 60*600*10, 60*600, 10*600, 600, (600*10)/30, 600/30 };
		LONG	mod;

		drag_accum += imsg->MouseY;
		mod = drag_accum / 10;
		drag_accum -= mod * 10;

		current_time -= mod * scale[selected_digit];

		DrawTimeControl(TMData);
		SetConductorState( control_player, CLOCKSTATE_SHUTTLE, current_time);
	}
	return (FALSE);

#if 0
  if(eventfunc)
    {
    return((*eventfunc)(TMData, imsg, tmobjectdata));
    }
  return(FALSE);
#endif
  }

BOOL Window_TRANSP_MOUSEUP(struct TMData *TMData, struct IntuiMessage *imsg)
  {
  TMOBJECTDATA *tmobjectdata;
  BOOL (*eventfunc)(struct TMData *, struct IntuiMessage *, TMOBJECTDATA *);

	if (imsg->Code != SELECTUP)
	{	last_gadget = NULL;
		return FALSE;
	}

	if (last_gadget == NULL) return FALSE;

  tmobjectdata = (TMOBJECTDATA *)(last_gadget->UserData);
  eventfunc = tmobjectdata->EventFunc;

  if(eventfunc)
    {
    return((*eventfunc)(TMData, imsg, tmobjectdata));
    }
  return(FALSE);
  }

BOOL Window_TRANSP_INTUITICKS(struct TMData *TMData, struct IntuiMessage *imsg)
  {

  	if (drag_state)
  	{	if (drag_delay > 0) drag_delay--;
  		else
  		{	switch (drag_state) {
  			case DRAG_REW:
  				if (GadgetInfo_REW.Gadget->Flags & SELECTED)
  				{	current_time -= 600;
					DrawTimeControl(TMData);
					SetConductorState( control_player, CLOCKSTATE_SHUTTLE, current_time);
				}
				break;
  			case DRAG_FF:
  				if (GadgetInfo_FF.Gadget->Flags & SELECTED)
  				{	current_time += 600;
					DrawTimeControl(TMData);
					SetConductorState( control_player, CLOCKSTATE_SHUTTLE, current_time);
				}
				break;
			}
		}
	}


  return(FALSE);
  }

BOOL Window_TRANSP_IDCMPUPDATE(struct TMData *TMData, struct IntuiMessage *imsg)
  {
  return(FALSE);
  }

BOOL Window_SELECTCO_CLOSEWINDOW(struct TMData *TMData, struct IntuiMessage *imsg)
  {
  return(TRUE);
  }

BOOL Window_SELECTCO_GADGETDOWN(struct TMData *TMData, struct IntuiMessage *imsg)
  {
  struct Gadget *gadget;
  TMOBJECTDATA *tmobjectdata;
  BOOL (*eventfunc)(struct TMData *, struct IntuiMessage *, TMOBJECTDATA *);

  gadget = (struct Gadget *)imsg->IAddress;
  tmobjectdata = (TMOBJECTDATA *)(gadget->UserData);
  eventfunc = tmobjectdata->EventFunc;

  if(eventfunc)
    {
    return((*eventfunc)(TMData, imsg, tmobjectdata));
    }
  return(FALSE);
  }

BOOL Window_SELECTCO_GADGETUP(struct TMData *TMData, struct IntuiMessage *imsg)
  {
  struct Gadget *gadget;
  TMOBJECTDATA *tmobjectdata;
  BOOL (*eventfunc)(struct TMData *, struct IntuiMessage *, TMOBJECTDATA *);

  gadget = (struct Gadget *)imsg->IAddress;
  tmobjectdata = (TMOBJECTDATA *)(gadget->UserData);
  eventfunc = tmobjectdata->EventFunc;

  if(eventfunc)
    {
    return((*eventfunc)(TMData, imsg, tmobjectdata));
    }
  return(FALSE);
  }

/* ========================================================================= *
   (My code....)
 * ========================================================================= */

#if 0

	-- function needed:
		-- refresh the window
		-- draw the locator status
		-- create list of conductors
		-- maintain conductor name

#endif

UBYTE	time[4] = { 0,0,0,0};
char	time_string[] = "  :  :  :  ";
UBYTE	digit_index[8] = { 0,1,3,4,6,7,9,10 };
BOOL	positive_only = TRUE;
BOOL	wait_for_apps = TRUE;

void DrawCurrentTime(struct RastPort *rp, int selected)
{	struct TextFont		*save_font = rp->Font;
	BOOL				negative_time;
	LONG				ctime,
						seconds,
						ticks;

		/* note that current time is is 600Hz ticks. We need to convert to 30 Hz */

	if (positive_only) current_time = MAX(0,current_time);

	ctime = clamp((-3600*9-60*59-59)*600,current_time,(3600*99+60*59+59)*600);

	if (ctime < 0) { ctime = -ctime; negative_time = TRUE; }
	else negative_time = FALSE;

	seconds = ctime / 600;
	ticks = (ctime - (seconds * 600));
	ticks = ticks * 30 / 600;

	time[3] = ticks;
	time[2] = seconds % 60;
	time[1] = (seconds / 60) % 60;
	time[0] = (seconds / 3600);

	time_string[0] = (time[0] / 10) + '0';
	if (negative_time) time_string[0] = 59;
	time_string[1] = (time[0] % 10) + '0';

	time_string[3] = (time[1] / 10) + '0';
	time_string[4] = (time[1] % 10) + '0';

	time_string[6] = (time[2] / 10) + '0';
	time_string[7] = (time[2] % 10) + '0';

	time_string[9] = (time[3] / 10) + '0';
	time_string[10]= (time[3] % 10) + '0';

	SetFont(rp,&digitsFont);
	SetAPen(rp,1);					/* change to text pen... */
	SetDrMd(rp,JAM2);				/* overwrite old digits */

		/* print one of the digits as highlighted */

	if (selected >= 0)
	{	selected = digit_index[selected];

		Text(rp,time_string,selected);
		SetAPen(rp,2);					/* change to highlight text pen... */
		Text(rp,time_string+selected,1);
		SetAPen(rp,1);					/* change to text pen... */
		selected = 11 - selected - 1;
		if (selected > 0) Text(rp,time_string+11-selected,selected);
	}
	else Text(rp,time_string,11);

	SetFont(rp,save_font);
}

void DrawTimeControl(struct TMData *TMData)
{	Move(	WindowInfo_TRANSP.Window->RPort,
			GadgetInfo_CLOCK.Gadget->LeftEdge + 5,
			GadgetInfo_CLOCK.Gadget->TopEdge + 14);

	DrawCurrentTime(WindowInfo_TRANSP.Window->RPort, selected_digit);
}

extern struct Image clocklabel_im;

void RefreshWindow_TRANSP(struct TMData *TMData)
{	DrawTimeControl(TMData);

		/* really need that DrawGlyph function... */

	DrawImage(	WindowInfo_TRANSP.Window->RPort,
				&clocklabel_im,
				GadgetInfo_CLOCK.Gadget->LeftEdge + 10,
				GadgetInfo_CLOCK.Gadget->TopEdge + 15);
}

	/* REM: Move to y.lib? */

void SelectGadget(struct Window *w, struct Gadget *g, UWORD state)
{	int pos;

	if ( (state && (g->Flags & SELECTED)) ||
		!(state || (g->Flags & SELECTED)) ) return;

	pos = RemoveGList(w,g,1);
	if (state) g->Flags |= SELECTED;
	else g->Flags &= ~SELECTED;
	RefreshGList(g,w,NULL,1);
	AddGList(w,g,pos,1,NULL);
}


void SetGadgetStates(struct TMData *TMData)
{
	SelectGadget(WindowInfo_TRANSP.Window, GadgetInfo_PLAY.Gadget,
		clock_state == CLOCKSTATE_RUNNING || clock_state == CLOCKSTATE_LOCATE );

	SelectGadget(WindowInfo_TRANSP.Window, GadgetInfo_STOP.Gadget,
		clock_state == CLOCKSTATE_STOPPED );

	SelectGadget(WindowInfo_TRANSP.Window, GadgetInfo_PAUSE.Gadget,
		clock_state == CLOCKSTATE_PAUSED );
}

void PlayState(struct TMData *TMData)
{
		/* if clock not already running */

	if (clock_state != CLOCKSTATE_RUNNING && clock_state != CLOCKSTATE_LOCATE)
	{		/* start the clock */

		SetConductorState(	control_player,
							wait_for_apps ? CLOCKSTATE_LOCATE : CLOCKSTATE_RUNNING,
							current_time);

			/* tell RealTime that we're done locating and are ready to get ticks */

		SetPlayerAttrs(control_player, PLAYER_Ready, TRUE, TAG_END);
	}

	SetGadgetStates( TMData );
}

void StopState(struct TMData *TMData)
{
	SetConductorState(control_player,CLOCKSTATE_STOPPED,0);

	SetGadgetStates( TMData );
}

void PauseState(struct TMData *TMData)
{
	SetConductorState(control_player,CLOCKSTATE_PAUSED,0);

	SetGadgetStates( TMData );
}

void HandleSignal(struct TMData *TMData, LONG signals)
{
	if (signals & SIGBREAKF_CTRL_E)
	{	char			*status;

		clock_state = control_player->pi_Source->cdt_State;

		switch (clock_state) {
		case CLOCKSTATE_STOPPED: status = "STOPPED"; break;
    	case CLOCKSTATE_PAUSED: status = "PAUSED"; break;
    	case CLOCKSTATE_LOCATE:
			SetPlayerAttrs(control_player, PLAYER_Ready, TRUE, TAG_END);
    		status = "LOCATING";
    		break;
    	case CLOCKSTATE_RUNNING: status = "RUNNING"; break;
    	default: status = "UNKNOWN";
		}

		DrawTimeControl(TMData);

		GT_SetGadgetAttrs(GadgetInfo_STATUS.Gadget, WindowInfo_TRANSP.Window, NULL,
			GTTX_Text, status, TAG_DONE );

		SetGadgetStates( TMData );
	}

	if (signals & SIGBREAKF_CTRL_F /* && clock_state == CLOCKSTATE_RUNNING */ )
	{
		DrawTimeControl(TMData);
	}
}

struct CondEntry {
	struct Node			node;
	char				name[ 32 ];
};

	/* REM: Move to new library... */

WORD AlphaInsertNode(struct List *l, struct Node *n)
{	struct Node			*search;
	WORD				pos = 0;

	for (	search=l->lh_Head ;				/* insert alphabetically		*/
			search->ln_Succ ;
			search=search->ln_Succ, pos++ )
	{	if (Stricmp(search->ln_Name,n->ln_Name) >= 0) break;
	}
	Insert(l,n,search->ln_Pred);
	return pos;
}

void FreeConductorList( struct List *list )
{	struct CondEntry	*ce;

	while (ce = (struct CondEntry *)RemHead( list ) )
		FreeMem( ce, sizeof *ce );
}

void GetConductorList( struct List *list )
{	struct Conductor	*cond;

	NewList( list );

	for (cond = NextConductor( NULL ); cond; cond = NextConductor ( cond ) )
	{	struct CondEntry *ce;

		if ( ce = AllocMem( sizeof *ce, MEMF_CLEAR ))
		{	strncpy( ce->name, cond->cdt_Node.ln_Name, sizeof ce->name );
			ce->node.ln_Name = ce->name;
			AlphaInsertNode( list, &ce->node );
		}
	}
}

void ShowConductorName(struct TMData *TMData)
{	GT_SetGadgetAttrs(GadgetInfo_NEWCON.Gadget, WindowInfo_SELECTCO.Window, NULL,
		GTST_String, control_player->pi_Source->cdt_Node.ln_Name, TAG_DONE );
}

void NewConductorName( char *newname )
{	SetPlayerAttrs(control_player, PLAYER_Conductor, newname, TAG_END);
}

	/* Hook function... */

ULONG __asm __interrupt __saveds myHookFunc (
	register __a1 struct pmTime		*msg,
	register __a2 struct PlayerInfo *pi )
{	struct Conductor *cond = pi->pi_Source;
	static LONG last_time;

	switch (msg->pmt_Method) {
	case PM_TICK:
		current_time = msg->pmt_Time;
		if ( (current_time ^ last_time) & ~0x3f)
    	{	Signal(main_task,SIGBREAKF_CTRL_F);
    		last_time = current_time;
    	}
		break;
    case PM_STATE:
    	clock_state = cond->cdt_State;
    	Signal(main_task,SIGBREAKF_CTRL_E);
    	break;

    case PM_SHUTTLE:
    	current_time = msg->pmt_Time;
		if ( (current_time ^ last_time) & ~0x3f)
    	{	Signal(main_task,SIGBREAKF_CTRL_F);
    		last_time = current_time;
    	}
	}
	return 0L;
}
