/*
 *	Program: DXFRIEND
 *	Filename:DISPLAY.C
 *
 *	Code to display various objects on the screen.
 */

#include <stdio.h>
#include "midi.h"
#include "dx7.h"

static struct dx7state state;

/*
 *	We have several possible displays, which update dynamically when
 *	things change if they are being displayed.
 */

#define V_NONE	0	/* nothing at all */
#define V_KEYS	1	/* map of keys held down + control settings */
#define V_NAMES	2	/* list of voice names in buffer */
#define V_MON	3	/* monitor scrolling on whole display, errors inline */

static int view = V_NONE;

/*
 *	Routines come in threes. eg:
 *	view_*	switches to the appropriate screen display
 *	show_*	displays the object appropriately on the screen
 *	disp_*	updates an object, and displays it *if it is the current view*
 */
view_names();		/* switch to the buffer voice name display view */
static view_key();	/* switch to the dynamic keyboard display, refresh the
			 *	whole keyboard and control display panel */
static view_control();	/* refresh the control panel part of the above */
static show_names();
static show_key();
static show_control();
static show_program();
static show_after();
static show_pb();

/*
 *	Display the names of the voices in the current 4096-byte buffer
 */
view_names(buf)
char *buf;
{
	view = V_NAMES;
	v_begin();
	show_names(buf);
	v_end();
}

disp_names(buf)
char *buf;
{
	if (view == V_NAMES) {
		v_begin();
		show_names(buf);
		v_end();
	} else {
		/* Voice buffer changes can occur over the midi.
		 * Tell them what to do. */
		m_str("Press N to see the voice names");
	}
}

static
show_names(buf)
char *buf;		/* pointer to the voice buffer */
{
	int i, j;

	view = V_NAMES;

	/*
	 * Until we display voice name characters properly, control chars
	 * screw the display up.
	 */
	v_clear();

	for (i=0; i<32; i++) {
		v_moveto((i&15)+2, i&16 ? 20 : 0);
		v_printf("%2d ", i+1); fflush(stdout);
		/* 128 is size of one voice,
		 * 118 is the offset of the name in each 128-byte block.
		 * The voice name is 10 characters long.
		 */
		v_strn(&buf[i*128 + 118], 10);
	}
}

view_state()
{
	view = V_STATE;
	v_begin();
	v_clear();
	view_key();
	view_control();
	v_end();
}
	
/* Routines called from midifunc.c. to show changes in midi parameters */

#define V_KEY_R	2		/* top row (from 0) */
#define V_KEY_C 1		/* leftmost column */
#define V_KEY_NUMWID 4		/* width of number part */
#define V_KEY_OCTWID 25		/* width of an octave display */
#define V_KEY_GUTTER 2		/* number of blanks between columns */

/* Do the whole keyboard display, according to state of keys */
static
view_key()
{
	register int keyno;
	register int octave;

	for (octave=0; octave <= (127/12); octave++) {
		v_moveto(V_KEY_R + 15 - octave/2*3 + 1,
			 ((octave & 1)
			   ? V_KEY_C + V_KEY_NUMWID + V_KEY_OCTWID
				     + V_KEY_GUTTER
			   : V_KEY_C));
		v_printf("%2d", octave - 1);
	}
		
	for (keyno=0; keyno<=127; keyno++) {
		show_key(keyno, state.key[keyno]);
	}

	view_control();
}

disp_key(keyno, vel)
{
	state.key[keyno] = vel;
	if (view == V_STATE) {
		v_begin();
		show_key(keyno, vel);
		v_end();
	}
}

/* update one key in the keyboard display.  This shows one octave on 3 lines,
 * with a hex digit pair for the velocity. */
static
show_key(keyno, vel)
{
	/* offset of digit pair for each note from top left of octave block */
	/*			   C C#  D D#  E   F  F#   G  Ab   A  Bb   B */
	static int xoffset[12] = { 0, 2, 4, 6, 8, 11, 13, 15, 17, 19, 21, 23 };
	static int yoffset[12] = { 1, 0, 1, 0, 1,  1,  0,  1,  0,  1,  0,  1 };
	register int octave = keyno/12;
	register int note   = keyno%12;		/* 0 = C, 11 = B */

	v_moveto(V_KEY_R + 15 - octave/2 * 3 + yoffset[note],
		 ((octave & 1)
		    ? V_KEY_C + V_KEY_NUMWID + V_KEY_OCTWID
			      + V_KEY_GUTTER + V_KEY_NUMWID
		    : V_KEY_C + V_KEY_NUMWID) + xoffset[note]);
	if (vel == 0) v_str("__");	/* show unpressed keys as __ */
	else v_2hex(vel);		/* and pressed ones as hex velocity */
}

/* initialise control panel display - called from view_key */
#define V_CON_R 2	/* row that control panel starts on */
#define V_CON_C	63
#define V_CON_NAMWID 12	/* horiz offset of numbers from names, "portamento"+2 */

static
view_control()
{
	static struct control {
		int value;
		char *name;
	} control[] = {
		{ MODWHEEL,	"Mod wheel" },
		{ BREATH,	"Breath" },
		{ FOOT,		"Foot" } ,
		{ DATAENTRY,	"Data entry"} ,
		{ SUSTAIN,	"Sustain" },
		{ PORTAMENTO,	"Portamento" },
		/* These are not controls, but are convenient here for printing */
		{ 0,	"" },		/* Blank line */
		{ 0,	"Program" },
		{ 0,	"Aftertouch" },
		{ 0,	"Pitch bend" },
		{ 0,	NULL }
	};
	register int i;
	struct control *cp;

	cp = control;
	for (i=0; cp->name != NULL; i++) {
		v_moveto(V_CON_R+i, V_CON_C);
		v_str(cp->name);
		cp++;
	}

	cp = control;
	for (i=0; cp->value != 0; i++) {
		v_moveto(V_CON_R+i, V_CON_C+V_CON_NAMWID);
		v_2hex(state.control[cp->value]);
		cp++;
	}

	show_program(state.program);
	show_after(state.after);
	show_pb(state.pb_lsb, state.pb_msb);
}

disp_control(number, value)
{
	register int column = V_CON_R;

	state.control[number] = value;
	if (view == V_STATE) {
		v_begin();
		show_control(number, value);
		v_end();
	}
}

static
show_control(number, value)
{
	register int row;

	switch (number) {
	case MODWHEEL:	row = 0;	break;
	case BREATH:	row = 1;	break;
	case FOOT:	row = 2;	break;
	case DATAENTRY:	row = 3;	break;
	case SUSTAIN:	row = 4;	break;
	case PORTAMENTO:row = 5;	break;
	case DATAPLUS:	m_str("Data entry +1");	break;
	case DATAMINUS:	m_str("Data entry -1");	break;
	default:
		m_printf("CONTROL\t%d, %d", number, value);
		return;
	}

	v_moveto(V_CON_R+row, V_CON_C+V_CON_NAMWID);
	v_2hex(value);
}

disp_program(number)
{
	state.program = number;
	if (view == V_STATE) {
		v_begin();
		show_program(number);
		v_end();
	}
}

static
show_program(number)
{
	v_moveto(V_CON_R + 7, V_CON_C + V_CON_NAMWID);
	v_2hex(number);
}

disp_after(value)
{
	state.after = value;
	if (view == V_STATE) {
		v_begin();
		show_after(value);
		v_end();
	}
}

static
show_after(value)
{
	v_moveto(V_CON_R + 8, V_CON_C + V_CON_NAMWID);
	v_2hex(value);
}

disp_pb(lsb, msb)
{
	state.pb_lsb = lsb;
	state.pb_msb = msb;
	if (view == V_STATE) {
		v_begin();
		show_pb(lsb, msb);
		v_end();
	}
}

static
show_pb(lsb, msb)
{
	v_moveto(V_CON_R + 9, V_CON_C + V_CON_NAMWID);
	v_printf("%02x %02x", msb, lsb);
}

/* Display an error message */
error(fmt, arg1, arg2, arg3, arg4)
char *fmt;
{
	/* m_printf does v_begin stuff */
	m_printf(fmt, arg1, arg2, arg3, arg4);
}
