/*
    CCPre: A Bars and Pipes tool for adding fixed CC messages to other
    	CC messages.

    Copyright (C) 1997 Richard Hagen
        This code is released into the Public Domain, and may be freely
	distributed in its original form.

	It is supplied ``as is'', and comes with no warranty.
	This program code was released because it might be useful as a
	starting point for other programmers. However, if any damage arises
	from its use, the original author will not be held liable.

	You are free to use and modify this code to your heart's content,
	provided you acknowledge me as the original author in any code
	that you might distribute which is based largely on this code.

	I acknowledge that the design of this accessory is influenced
	strongly by the example code supplied with the Rules for Tools
	package. However, I have made substantial contributions of my
	own.

	Richard Hagen
	richard@it.uq.edu.au
	August 1997

    History:
	Version 1.0 (14 September 1997)
	Initial release.
*/

#include "bars.h"
#include <libraries/dos.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <exec/memory.h>
#include <string.h>
#include <intuition/intuition.h>

#include "myheader.h"

#define CCPRE_NAME "CC Prefix"
#define CCPRE_ID  MAKE_IDS("CCPR")
#define CCPRE_TYPE (TOOL_NORMAL)

__chip static UWORD ccpre[] = { 
  /*------ plane # 0: --------*/
  0x0, 0x0,
  0x3fff, 0xfc00,
  0x2000, 0x0400,
  0x2000, 0x0400,
  0x2000, 0x0400,
  0x2000, 0x0400,
  0x2000, 0x0400,
  0x2000, 0x0400,
  0x2000, 0x0400,
  0x2000, 0x0400,
  0x3fff, 0xfc00,
  0x0, 0x0,

  /*------ plane # 1: --------*/
  0x0, 0x0,
  0x0, 0x0,
  0x1fff, 0xfc00,
  0x18c6, 0x1c00,
  0xdb5a, 0xff00,
  0xd8c6, 0x3f00,
  0xdbd6, 0xff00,
  0xdbda, 0xff00,
  0x1bda, 0x1c00,
  0x1fff, 0xfc00,
  0x1fff, 0xfc00,
  0x0, 0x0,

  /*------ plane # 2: --------*/
  0x0, 0x0,
  0x0, 0x0,
  0x0, 0x0,
  0x0739, 0xe000,
  0x04a5, 0x0,
  0x0739, 0xc000,
  0x0429, 0x0,
  0x0425, 0x0,
  0x0425, 0xe000,
  0x0, 0x0,
  0x0, 0x0,
  0x0, 0x0,
};

static struct Image ccpre_image = {
  0,0,
  24 , 12 , 3 ,
  &ccpre[0],
  0x1f,0x00,
  NULL
};
 
struct CCPreTool {
  struct Tool tool;

  UBYTE input_cc;		/* CC # to watch out for. */
  UBYTE output_cc;		/* CC # to emit. */
  UBYTE output_value;		/* Constant CC value. */
  UBYTE output_delay;		/* Small delay in clocks. */
};

static void
ccpre_tool_init(struct CCPreTool *tool)
{
  tool->tool.touched = TOUCH_INIT;

  tool->input_cc = 0;
  tool->output_cc = 1;
  tool->output_value = 0;
  tool->output_delay = 1;
}

static struct ToolMaster master;

extern struct Functions *functions;

__geta4		/* Callback. */
static struct Event *
processeventcode(struct Event *event)
{
  struct CCPreTool *tool = (struct CCPreTool *)event->tool;

  if (!tool->tool.touched)
    {
      ccpre_tool_init(tool);
    }

  event->tool = event->tool->next;

  if (event->type == EVENT_VOICE &&
      event->status == MIDI_CCHANGE)
    {
      if (event->byte1 == tool->input_cc)
	{
	  struct Event *new = (struct Event *)(*functions->allocevent)();
	  if (new)
	    {
	      new->type = EVENT_VOICE;
	      new->status = MIDI_CCHANGE;
	      new->byte1 = tool->output_cc;
	      new->byte2 = tool->output_value;
	      new->time = event->time;
	      new->tool = event->tool;
	      new->next = event;

	      event->time += tool->output_delay;
	      event = new;
	    }
	}
    }
  
  return(event);
}

#define INPUT_CC_GADGET 1
#define INPUT_CCDOWN_GADGET 2
#define INPUT_CCUP_GADGET 3

#define OUTPUT_CC_GADGET 4
#define OUTPUT_CCDOWN_GADGET 5
#define OUTPUT_CCUP_GADGET 6

#define OUTPUT_VALUE_GADGET 7
#define OUTPUT_VALUEDOWN_GADGET 8
#define OUTPUT_VALUEUP_GADGET 9

#define DELAY_GADGET 10
#define DELAYDOWN_GADGET 11
#define DELAYUP_GADGET 12

/* Input CC# gadget. */
struct PropInfo CCPreCCPreGadget1SInfo = {
  AUTOKNOB+FREEHORIZ,
  -16384, -1,
  16384, -1,
};

struct Image CCPreImage1 = {
  0,0,
  102,6,
  0,
  NULL,
  0x0000,0x0000,
  NULL
};

struct IntuiText CCPreIText1 = {
  2,0,JAM1,
  -96,1,
  NULL,
  "Input CC#:",
  NULL
};

struct Gadget CCPreGadget1 = {
  NULL,
  103,14,
  100,10,
  GFLG_GADGHBOX|GFLG_GADGHIMAGE,
  RELVERIFY|GADGIMMEDIATE,
  PROPGADGET,
  (APTR)&CCPreImage1,
  NULL,
  &CCPreIText1,
  NULL,
  (APTR)&CCPreCCPreGadget1SInfo,
  INPUT_CC_GADGET,
  NULL
};

/* Output CC# gadget. */
struct PropInfo CCPreCCPreGadget2SInfo = {
  AUTOKNOB+FREEHORIZ,
  -16384, -1,
  16384, -1,
};

struct Image CCPreImage2 = {
  0,0,
  102,6,
  0,
  NULL,
  0x0000,0x0000,
  NULL
};

struct IntuiText CCPreIText2 = {
  2,0,JAM1,
  -96,1,
  NULL,
  "Output CC#:",
  NULL
};

struct Gadget CCPreGadget2 = {
  &CCPreGadget1,
  103,26,
  100,10,
  GFLG_GADGHBOX|GFLG_GADGHIMAGE,
  RELVERIFY|GADGIMMEDIATE,
  PROPGADGET,
  (APTR)&CCPreImage2,
  NULL,
  &CCPreIText2,
  NULL,
  (APTR)&CCPreCCPreGadget2SInfo,
  OUTPUT_CC_GADGET,
  NULL
};

/* Value gadget. */
struct PropInfo CCPreCCPreGadget3SInfo = {
  AUTOKNOB+FREEHORIZ,
  -16384, -1,
  16384, -1,
};

struct Image CCPreImage3 = {
  0,0,
  102,6,
  0,
  NULL,
  0x0000,0x0000,
  NULL
};

struct IntuiText CCPreIText3 = {
  2,0,JAM1,
  -96, 1,
  NULL,
  "Value:",
  NULL
};

struct Gadget CCPreGadget3 = {
  &CCPreGadget2,
  103,38,
  100,10,
  GFLG_GADGHBOX|GFLG_GADGHIMAGE,
  RELVERIFY|GADGIMMEDIATE,
  PROPGADGET,
  (APTR)&CCPreImage3,
  NULL,
  &CCPreIText3,
  NULL,
  (APTR)&CCPreCCPreGadget3SInfo,
  OUTPUT_VALUE_GADGET,
  NULL
};

/* Delay gadget. */
struct PropInfo CCPreCCPreGadget4SInfo = {
  AUTOKNOB+FREEHORIZ,
  -16384, -1,
  16384, -1,
};

struct Image CCPreImage4 = {
  0,0,
  102,6,
  0,
  NULL,
  0x0000,0x0000,
  NULL
};

struct IntuiText CCPreIText4 = {
  2,0,JAM1,
  -96, 1,
  NULL,
  "Delay:",
  NULL
};

struct Gadget CCPreGadget4 = {
  &CCPreGadget3,
  103,50,
  100,10,
  GFLG_GADGHBOX|GFLG_GADGHIMAGE,
  RELVERIFY|GADGIMMEDIATE,
  PROPGADGET,
  (APTR)&CCPreImage4,
  NULL,
  &CCPreIText4,
  NULL,
  (APTR)&CCPreCCPreGadget4SInfo,
  DELAY_GADGET,
  NULL
};

struct NewWindow CCPreNewWindowStructure1 = {
  75,85,
  248, 64,
  0,6,
  GADGETDOWN+GADGETUP+CLOSEWINDOW,
  WINDOWDRAG+WINDOWDEPTH+WINDOWCLOSE+ACTIVATE+NOCAREREFRESH,
  &CCPreGadget4,
  NULL,
  "CC Prefix",
  NULL,
  NULL,
  5,5,
  -1,-1,
  CUSTOMSCREEN
};

/* Writes the value of a proportional slider. Thank goodness
   all the sliders are identical in range and behaviour! */
__geta4		/* Callback. */
long display_routine(struct Window *window,	/* Window where the gadget lives. */
		     struct Gadget *gadget,	/* The gadget itself. */
		     unsigned long value)	/* The value to display. */
{
  char text[10];
  struct RastPort *rp = window->RPort;
  Move(rp, 215, 21 + ((gadget->GadgetID - 1) / 3) * 12);
  SetAPen(rp, 1);
  SetBPen(rp, 0);
  SetDrMd(rp, JAM2);
  sprintf(text, "%3ld", value);
  Text(rp, text, 3);
  return value;
}

static long
drag_embossed_prop(struct Window *window,
		   short id,
		   UBYTE min)
{
  long value = (*functions->DragEmbossedProp)(window, id);
  if (value < min)
    {
      value = min;
      (*functions->ModifyEmbossedProp)(window, id,
				       value, 0, 0, 0, 0, 0);
    }
  (*functions->DrawEmbossedProp)(window, id);

  return value;
}

static long
shift_embossed_prop(struct Window *window,
		    short id,
		    short shift,
		    UBYTE min)
{
  long value = (*functions->ShiftEmbossedProp)(window, id, shift, 0);
  if (value < min)
    {
      value = min;
      (*functions->ModifyEmbossedProp)(window, id,
				       value, 0, 0, 0, 0, 0);
    }
  (*functions->DrawEmbossedProp)(window, id);

  return value;
}

__geta4
void
edittoolcode(struct CCPreTool *tool)
{
  struct IntuiMessage *message;
  struct Window *window;
  long class, code;
  struct Gadget *gadget;
  struct NewWindow *newwindow;
  CCPreNewWindowStructure1.Screen = functions->screen;

  if (tool->tool.touched & TOUCH_EDIT)
    {
      CCPreNewWindowStructure1.LeftEdge = tool->tool.left;
      CCPreNewWindowStructure1.TopEdge = tool->tool.top;
      CCPreNewWindowStructure1.Width = tool->tool.width;
      CCPreNewWindowStructure1.Height = tool->tool.height;
    }
  
  if (!tool->tool.touched)
    {
      ccpre_tool_init(tool);
    }

  newwindow = (struct NewWindow *)
    (*functions->DupeNewWindow)(&CCPreNewWindowStructure1);
  if (!newwindow)
    {
      return;
    }
  newwindow->Title = 0;
  newwindow->Flags |= BORDERLESS;
  newwindow->Flags &= ~0xF;
  newwindow->BlockPen = 0;
  newwindow->DetailPen = 0;

  window = (struct Window *) (*functions->FlashyOpenWindow)(newwindow);
  if (!window)
    {
      return;
    }

  tool->tool.window = window;
  (*functions->EmbossWindowOn)(window,WINDOWCLOSE|WINDOWDEPTH|WINDOWDRAG,
			       CCPRE_NAME,(short)-1,(short)-1,0,0);


  for (short i = INPUT_CC_GADGET;
       i <= OUTPUT_VALUE_GADGET;
       i += 3)
    {
      (*functions->FatEmbossedPropOn)(window,i, i + 1, i + 2,
				      (no_prototype) display_routine,
				      128, 1);
    }
  
  (*functions->FatEmbossedPropOn)(window,
				  DELAY_GADGET,
				  DELAYDOWN_GADGET,
				  DELAYUP_GADGET,
				  (no_prototype) display_routine,
				  9, 1);
  
  (*functions->ModifyEmbossedProp)(window, INPUT_CC_GADGET,
				   tool->input_cc, 0, 0, 0, 0, 0);
  (*functions->ModifyEmbossedProp)(window, OUTPUT_CC_GADGET,
				   tool->output_cc, 0, 0, 0, 0, 0);
  (*functions->ModifyEmbossedProp)(window, OUTPUT_VALUE_GADGET,
				   tool->output_value, 0, 0, 0, 0, 0);
  (*functions->ModifyEmbossedProp)(window, DELAY_GADGET,
				   tool->output_delay, 0, 0, 0, 0, 0);

  for (short i = INPUT_CC_GADGET;
       i <= DELAY_GADGET;
       i += 3)
    {
      (*functions->DrawEmbossedProp)(window, i);
    }

  for (;;)
    {
      message = (struct IntuiMessage *) (*functions->GetIntuiMessage)(window);
      class = message->Class;
      code = message->Code;
      gadget = (struct Gadget *) message->IAddress;
      class = (*functions->SystemGadgets)(window,class,gadget,code);

      ReplyMsg((struct Message *)message);
      if (class == CLOSEWINDOW)
	{
	  break;
	}
      else if (class == GADGETDOWN)
	{
	  const short id = gadget->GadgetID;

	  switch (id)
	    {
	    case INPUT_CC_GADGET:
	      tool->input_cc = (UBYTE) drag_embossed_prop(window,
							  INPUT_CC_GADGET, 0);
	      break;
	    case INPUT_CCDOWN_GADGET:
	      tool->input_cc = (UBYTE) shift_embossed_prop(window,
							   INPUT_CC_GADGET, -1, 0);
	      break;
	    case INPUT_CCUP_GADGET:
	      tool->input_cc = (UBYTE) shift_embossed_prop(window,
							   INPUT_CC_GADGET, 1, 0);
	      break;
	    case OUTPUT_CC_GADGET:
	      tool->output_cc = (UBYTE) drag_embossed_prop(window,
							   OUTPUT_CC_GADGET, 0);
	      break;
	    case OUTPUT_CCDOWN_GADGET:
	      tool->output_cc = (UBYTE) shift_embossed_prop(window,
							    OUTPUT_CC_GADGET, -1, 0);
	      break;
	    case OUTPUT_CCUP_GADGET:
	      tool->output_cc = (UBYTE) shift_embossed_prop(window,
						      OUTPUT_CC_GADGET, 1, 0);
	      break;
	    case OUTPUT_VALUE_GADGET:
	      tool->output_value = (UBYTE) drag_embossed_prop(window,
							      OUTPUT_VALUE_GADGET, 0);
	      break;
	    case OUTPUT_VALUEDOWN_GADGET:
	      tool->output_value = (UBYTE) shift_embossed_prop(window,
							       OUTPUT_VALUE_GADGET,
							       -1, 0);
	      break;
	    case OUTPUT_VALUEUP_GADGET:
	      tool->output_value = (UBYTE) shift_embossed_prop(window,
							       OUTPUT_VALUE_GADGET, 1, 0);
	      break;
	    case DELAY_GADGET:
	      tool->output_delay = (UBYTE) drag_embossed_prop(window, DELAY_GADGET, 1);
	      break;
	    case DELAYDOWN_GADGET:
	      tool->output_delay = (UBYTE) shift_embossed_prop(window, DELAY_GADGET, -1, 1);
	      break;
	    case DELAYUP_GADGET:
	      tool->output_delay = (UBYTE) shift_embossed_prop(window, DELAY_GADGET, 1, 1);
	      break;
	    }
	}
      else if (class == GADGETUP)
	{
	  /* Nothing. */
	}
    }
  tool->tool.window = 0;
  tool->tool.left = window->LeftEdge;
  tool->tool.top = window->TopEdge;
  tool->tool.width = window->Width;
  tool->tool.height = window->Height;
  tool->tool.touched = TOUCH_INIT | TOUCH_EDIT;

  for (short i = INPUT_CC_GADGET;
       i <= DELAY_GADGET;
       i += 3)
    {
      (*functions->FatEmbossedPropOff)(window,i, i + 1, i + 2);
    }

  (*functions->EmbossWindowOff)(window);
  (*functions->FlashyCloseWindow)(window);
  (*functions->DeleteNewWindow)(newwindow);
}

struct ToolMaster *
inittoolmaster()
{
  memset((char *)&master,0,sizeof(struct ToolMaster));
  master.toolid = CCPRE_ID;
  master.image = &ccpre_image;
  strcpy(master.name, CCPRE_NAME);
  master.edittool = (void_prototype) edittoolcode;
  master.processevent = (event_prototype) processeventcode;
  master.tooltype = CCPRE_TYPE;
  master.toolsize = sizeof(struct CCPreTool);
  return(&master);
}
