/*	Quantize.c

	© 1989 Blue Ribbon Bakery

*/

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

#define ID_QANT	0x51414E54

static UWORD quantize[] = { 
/*------ plane # 0: --------*/

  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0, 
  0x0,   0x0,
 /*------ plane # 1: --------*/

  0x0,   0x0, 
  0x3ff0,   0x0, 
  0x3ef0,   0x0, 
  0x3ef0,   0x0, 
  0xf8f0,   0x300, 
  0xfff0,   0x300, 
  0xc00f,   0xff00, 
  0xc00f,   0xbf00, 
  0xf,   0xbc00, 
  0xe,   0x3c00, 
  0xf,   0xfc00, 
  0x0,   0x0,
 /*------ plane # 2: --------*/

  0x0,   0x0, 
  0x3fff,   0xfc00, 
  0x3eff,   0xbc00, 
  0x3eff,   0xbc00, 
  0x38fe,   0x3c00, 
  0x3fff,   0xfc00, 
  0x3fff,   0xfc00, 
  0x3eff,   0xbc00, 
  0x3eff,   0xbc00, 
  0x38fe,   0x3c00, 
  0x3fff,   0xfc00, 
  0x0,   0x0};
 struct  Image quantizeimage =
 {
 0,0,
 24 , 12 , 3 ,
 &quantize[0],
 0x1f,0x00,
 NULL,
 };
 
struct QuantizeTool {
    struct Tool tool;
    long tightness;
    short offset;
    short resolution;
    short noteindex;
    long capture;
    short range;
    char on;
    char off;
    char triplet;
};
 

extern struct Functions *functions;

extern printf();

struct Event *processeventcode(event)

struct NoteEvent *event;

{
    struct QuantizeTool *tool = (struct QuantizeTool *) event->tool;
    long time;
    short res;
    short half;
    long range;
    struct Track *track;
    track = tool->tool.track;
    event->tool = tool->tool.next;
    if (track) {
	if (!functions->running) return((struct Event *)event);
	if (!tool->tool.intool && (track->mode & TRACK_RECORD) && functions->recording)
	    return((struct Event *)event);
    }
    if (!tool->tool.touched) {
	tool->on = 1;
	tool->noteindex = 3;
	tool->resolution = 96;
	tool->tool.touched = TOUCH_INIT;
	tool->capture = 65535;
	tool->range = 48;
	tool->tightness = 65535;
    }
    res = tool->resolution;
    half = res >> 1;
    range = tool->range;
    if (event->status == MIDI_NOTEON) {
	if (!event->velocity) event->status = MIDI_NOTEOFF;
	if (tool->on) {
	    time = event->time + half;
	    time = IDivU(time,res);
	    time = IMulU(time,res);
	    time += tool->offset;
	    time = time - event->time;
	    if ((time >= -range) && (time <= range)) {
		time = time*tool->tightness;
		time = time >> 16;
		event->time += time;
		if (event->time < 0) event->time = 0;
	    }
	}
	if (event->type & EVENT_PADEDIT) {
	    if (tool->off) {
		time = event->time + event->duration + half;
		time = IDivU(time,res);
		time = IMulU(time,res);
		time += tool->offset;
		time = time - (event->time + event->duration);
		if ((time >= -range) && (time <= range)) {
		    time = time*tool->tightness;
		    time = time >> 16;
		    event->duration += time;
		    if (event->duration > 60000) event->duration = 1;
		}
	    }
	}
    }
    if (event->status == MIDI_NOTEOFF) {
	if (tool->off) {
	    time = event->time + half;
	    time = IDivU(time,res);
	    time = IMulU(time,res);
	    time += tool->offset;
	    time = time - event->time;
	    if ((time >= -range) && (time <= range)) {
		time = time*tool->tightness;
		time = time >> 16;
		event->time += time;
		if (event->time < 0) event->time = 0;
	    }
	}
    }
    return((struct Event *)event);
}

extern struct NewWindow quantizeNewWindowStructure1;

/*	The sliding proportional gadgets are actaully defined simply
	as Boolean gadgets.  When a gadget is clicked, DragSlider() is
	called, and one of the parameters passed to it is a routine
	that displays the number in the center of the knob.
	DragSlider is called with 5 parameters:

	result = DragSlider(window,gadget,width,routine,flag);

	Window:  This window.
	Gadget:  Pointer to the sliding gadget.
	Width:  Width, in pixels of the knob.
	Routine:  Routine to display the data in the knob.
	Flag:  If set, this drags up and down, instead of left to right.

	The final result (which is returned by the routine you
	provide) is returned by DragSlider.

	Your draw routine receives four parameters:

	routine(RastPort,X,Y,Position)

	RastPort:  RastPort to draw in.
	X,Y:  X and Y coordinates to draw to.
	Position:  Number between 0 and 65535, representing the
		position of the slider.
*/

static tightroutine(rp,x,y,position)

struct RastPort *rp;
unsigned short x,y,position;

{
    char text[20];
    long result;
    Move(rp,x+4,y + 8);
    SetAPen(rp,0);
    SetDrMd(rp,JAM1);
    result = position;
    position = position / 656;
    sprintf(text,"%ld%%",position);
    Text(rp,text,strlen(text));
    return((long)result);
}

static offsetroutine(rp,x,y,position)

struct RastPort *rp;
unsigned short x,y,position;

{
    char text[20];
    long result;
    Move(rp,x+4,y + 8);
    SetAPen(rp,0);
    SetDrMd(rp,JAM1);
    result = position - 32768;
    if (result <= 0) {
	result = (result - 328) / 655;
	sprintf(text,"%ld",result);
    }
    else {
	result = (result + 328) / 655;
	if (!result) sprintf(text,"%ld",result);
	else sprintf(text,"+%ld",result);
    }
    Text(rp,text,strlen(text));
    return((long)result);
}

void edittoolcode(tool)

struct QuantizeTool *tool;

{
    struct IntuiMessage *message;
    struct Window *window;
    long class, code;
    char refresh = 1;
    static struct IntuiText itext = { 6,0,JAM2,0,0,NULL,0,0 };
    static char *convert[] = 
	{"1   ","1/2 ","1/4 ","1/8 ","1/16","1/32", "1/64"};
    struct Gadget *gadget;
    struct NewWindow *newwindow;
    quantizeNewWindowStructure1.Screen = functions->screen;
    if (tool->tool.touched & TOUCH_EDIT) {
	quantizeNewWindowStructure1.LeftEdge = tool->tool.left;
	quantizeNewWindowStructure1.TopEdge = tool->tool.top;
	quantizeNewWindowStructure1.Width = tool->tool.width;
	quantizeNewWindowStructure1.Height = tool->tool.height;
    }
    if (!tool->tool.touched) {
	tool->on = 1;
	tool->noteindex = 3;
	tool->resolution = 96;
	tool->tool.touched = TOUCH_INIT;
	tool->capture = 65535;
	tool->range = 48;
	tool->tightness = 65535;
    }
    newwindow = (struct NewWindow *) 
	(*functions->DupeNewWindow)(&quantizeNewWindowStructure1);
    if (!newwindow) return;
    window = (struct Window *)
	(*functions->FlashyOpenWindow)(newwindow);
    if (!window) return;
    tool->tool.window = window;
    for (;;) {
	if (refresh) {
	    code = tool->noteindex;
	    code = 768 / (1 << code);
	    if (tool->triplet) code = (code << 1) / 3;
	    tool->resolution = code;
	    code = code >> 1;
	    code = code * tool->capture;
	    code = code >> 16;
	    tool->range = code + 1;
	    itext.IText = convert[tool->noteindex];	    
	    PrintIText(window->RPort,&itext,140,78);
	    (*functions->DrawSlider)(window,
		(*functions->GetGadget)(window,1),tool->tightness,32,tightroutine,0);
	    code = tool->offset * 655;
	    code += 32768;
	    (*functions->DrawSlider)(window,
		(*functions->GetGadget)(window,2),code,32,offsetroutine,0);
	    (*functions->DrawSlider)(window,
		(*functions->GetGadget)(window,7),tool->capture,32,tightroutine,0);
	    (*functions->SelectGadget)(window,4,tool->on);
	    (*functions->SelectGadget)(window,5,tool->off);
	    (*functions->SelectGadget)(window,6,tool->triplet);
	}
	refresh = 0;
	message = (struct IntuiMessage *) (*functions->GetIntuiMessage)(window);
	class = message->Class;
	code = message->Code;
	gadget = (struct Gadget *) message->IAddress;
	ReplyMsg((struct Message *)message);
	if (class == CLOSEWINDOW) break;
	else if (class == GADGETDOWN) {
	    class = gadget->GadgetID;
	    switch (class) {
		case 1 :
		    tool->tightness = (*functions->DragSlider)
			(window,gadget,32,tightroutine,0);
		    refresh = 1;
		    break;
		case 2 :
		    tool->offset = (*functions->DragSlider)
			(window,gadget,32,offsetroutine,0);
		    refresh = 1;
		    break;
		case 3 :
		    code = (*functions->PopUpMenu)(window,(struct Menu *)
			gadget->SpecialInfo);
		    code = ITEMNUM(code);
		    if (code < 7) tool->noteindex = code;
		    refresh = 1;
		    break;
		case 7 :
		    tool->capture = (*functions->DragSlider)
			(window,gadget,32,tightroutine,0);
		    refresh = 1;
		    break;
	    }
	}
	else if (class == GADGETUP) {
	    class = gadget->GadgetID;
	    switch (class) {
		case 4 :
		    tool->on = !tool->on;
		    refresh = 1;
		    break;
		case 5 :
		    tool->off = !tool->off;
		    refresh = 1;
		    break;
		case 6 :
		    tool->triplet = !tool->triplet;
		    refresh = 1;
		    break;
	    }
	}
    }
    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_EDIT | TOUCH_INIT;
    (*functions->FlashyCloseWindow)(window);
    (*functions->DeleteNewWindow)(newwindow);
}

static struct ToolMaster master;

struct ToolMaster *inittoolmaster()

{
    memset((char *)&master,0,sizeof(struct ToolMaster));
    master.toolid = ID_QANT;
    master.image = &quantizeimage;
    strcpy(master.name,"Quantize");
    master.edittool = edittoolcode;
    master.processevent = processeventcode;
    master.tooltype = TOOL_NORMAL;
    master.toolsize = sizeof(struct QuantizeTool);
    return(&master);
}
