#define WAVEGRPH
#include <stdio.h>
#include <conio.h>
#include <graphics.h>
#include <stdlib.h>
#include <alloc.h>
#include <dos.h>

typedef enum { FALSE, TRUE } tftype;
typedef enum { APPROX, DETAIL, INPUT, OUTPUT } signaltype;
typedef enum { DECOMP, RECON } wavetype;
typedef enum { NONE, HELP1, HELP2 } helptype;

#include "wavegrph.h"

double *ViewExDA, *ViewExDD, *ViewExRA, *ViewExRD;
int *ViewIndex, *ViewLevel;
int mulY, divY, frameH, frameHm1, rowsremain;
int maxY, csize, csizep1, csizem1, framemod;
int arrow1, arrow2, arrow3, arrow4;

int GraphSetup(void)
{
	int driver = DETECT;
	int mode = 0;
	int error;

	detectgraph(&driver, &mode);
	initgraph(&driver, &mode, "");
	error = graphresult();
	if (!error)
	{
		maxY = getmaxy();

		 /* display-mode dependent constants for character sizing,
				bargraph size and postioning, bit-mapped font line and
				character postions, and arrow drawing */
		switch(driver)
			{
				case CGA:	/* CGA mono graphics 640 X 200 */
					mulY = 1;  divY = 1;  viewlines = 23;
					frameH = 66;  frameHm1 = 65;  framemod = 21;
					rowsremain = 2;
					trow0 = -1;  trow1 = 7;  trow2 = 15;  trow3 = 23;  trow4 = 31;
					trow5 = 39;  trow6 = 47;  trow7 = 55;  trow8 = 63;
					csize = 8;  csizem1 = 7;  csizep1 = 9;
					arrow1 = 1;  arrow2 = 3;  arrow3 = 4;  arrow4 = 6;
					break;
				case EGAMONO:	/* EGA graphics 640 X 350 */
					mulY = 175;  divY = 100;  viewlines = 41;
					frameH = 116;  frameHm1 = 115;  framemod = 33;
					rowsremain = 2;
					trow0 = -1;  trow1 = 13;  trow2 = 27;  trow3 = 41;  trow4 = 55;
					trow5 = 69;  trow6 = 83;  trow7 = 97;  trow8 = 111;
					csize = 14;  csizem1 = 13;  csizep1 = 15;
					arrow1 = 3;  arrow2 = 6;  arrow3 = 7;  arrow4 = 10;
					break;
				case VGA:	/* VGA mono graphics 640 X 480 */
					mulY = 24;  divY = 10;  viewlines = 58;
					frameH = 160;  frameHm1 = 159;  framemod = 43;
					rowsremain = 0;
					trow0 = -1;  trow1 = 18;  trow2 = 37;  trow3 = 56;  trow4 = 76;
					trow5 = 95;  trow6 = 115;  trow7 = 134;  trow8 = 152;
					csize = 19;  csizem1 = 18;  csizep1 = 20;
					arrow1 = 5;  arrow2 = 8;  arrow3 = 9;  arrow4 = 13;
					break;
				case HERCMONO:	/* Hercules mono graphics 720 X 348 */
					mulY = 174;  divY = 100;  viewlines = 41;
					frameH = 116;  frameHm1 = 115;  framemod = 33;
					rowsremain = 0;
					trow0 = -1;  trow1 = 13;  trow2 = 27;  trow3 = 41;  trow4 = 55;
					trow5 = 69;  trow6 = 83;  trow7 = 97;  trow8 = 111;
					csize = 14;  csizem1 = 13;  csizep1 = 15;
					arrow1 = 3;  arrow2 = 6;  arrow3 = 7;  arrow4 = 10;
					setviewport(40, 0, 679, 347, 1);
					break;
			}

		settextstyle(SMALL_FONT, HORIZ_DIR, 0);
		setusercharsize(7, 6, mulY, divY);
	}
	else
		printf("\n%s\n", grapherrormsg(error));

	return error;
}


void GraphExit(void)
{
	closegraph();
}


int ScrollGraph(int scrollup)
{
	int Dtop, Stop, Sbot, i, displacement;
	unsigned size;
	void far *buffer;

	/* set move parameters according to direction of scroll */
	if (scrollup) /* true value move the block down the screen */
	{
		Dtop = frameH * 2;
		displacement = frameH;
	}
	else /* false value move the block up the screen */
	{
		Dtop = 0;
		displacement = -frameH;
	}

	Stop = frameH;
	Sbot = (frameH * 2) - 1;

	/* create temporary buffer to hold displaced screen image */
	size = imagesize(0, 1, 639, frameH);
	buffer = farmalloc(size);
	if (buffer)
	{
		for (i = 0; i < 2; i++)
		{
			getimage(0, Stop, 639, Sbot, buffer);
			setfillstyle(EMPTY_FILL, BLACK);
			bar(0, Stop, 639, Sbot);
			setfillstyle(SOLID_FILL, WHITE);
			putimage(0, Dtop, buffer, COPY_PUT);
			Stop -= displacement;
			Sbot -= displacement;
			Dtop -= displacement;
		}

		farfree(buffer);
	}
	else
		return 0;

	return 1;
}


void DrawBarGraph(int left, int top, int right, int bot, double *data,
																									int length, int barsize)
{
	double range, newval, maxval;
	int i, j, index, width, center, offset, Ltop, Lbot;
	char str[20];

	width = right - left - 1;
	center = (bot + top) / 2;
	range = 0.95 * (double) ((bot - top - framemod) / 2);
	if ((length * barsize) > width)
		length = width / barsize;

	if (data[0] < 0) /* initial seed for largest magnitude of data set */
		maxval = -data[0];
	else
		maxval = data[0];

	for (i = 1; i < length; i++)
	{                /* determine largest magnitude of data set */
		if (data[i] < 0)
			newval = -data[i];
		else
			newval = data[i];

		if (newval > maxval)
			maxval = newval;
	}

	if (maxval == 0.0) /* default value if  all data points are zero */
		maxval = 1.0;

	setfillstyle(EMPTY_FILL, BLACK); /* draw the frame of the bar graph */
	bar(left, top, right, bot);
	rectangle(left, top + csizep1, right, bot - csizep1);
	line(left, center, right, center);
	setfillstyle(SOLID_FILL, WHITE);

	for(i = 0, index = left + 1; i < length; i++)
	{  /* draw the bars of the graph */
		offset = (int) ((data[i] / maxval) * range);
		if (offset > 0) /* properly format excursion in Y direction */
		{
			Ltop = center - offset;
			Lbot = center;
		}
		else
		{
			Ltop = center;
			Lbot = center - offset;
		}

		for (j = 0; j < barsize; j++) /* draw bar to desired width */
		{
			line(index, Ltop, index, Lbot);
			index++;
		}
	}

	sprintf(str, "% 10.4e", maxval); /* label max and min vertical range */
	outtextxy(left, top - 1, str);
	sprintf(str, "% 10.4e", -maxval);
	outtextxy(left, bot - csize, str);
}


void DrawLevel(int left, int top, int right, int bot, double *data,
								int length, int res, int barsize, signaltype heading)
{
	char lvlstr[3];

	DrawBarGraph(left, top, right, bot, data, length, barsize);
	settextjustify(RIGHT_TEXT, TOP_TEXT);
	switch (heading)
	{
		case DETAIL:
			itoa(DispWaveLevels - res, lvlstr, 10);
			outtextxy(right - 23, bot - csize, lvlstr);
			outtextxy(right - 39, bot - csize, "DETAIL LEVEL");
			setfillstyle(EMPTY_FILL, BLACK);
			bar(313, top, 324, top + csizem1);
			setfillstyle(SOLID_FILL, WHITE);
			line(315, top, 322, top + csizem1);
			if (WaveShown == DECOMP)  /* draw the proper arrow */
			{
				line(322, top + csizem1, 324, top + arrow3);
				line(322, top + csizem1, 315, top + arrow4);
			}
			else
			{
				line(315, top, 322, top + arrow1);
				line(315, top, 313, top + arrow2);
			}
			break;
		case APPROX:
			if ((DispWaveLevels - res) == 0)
				outtextxy(right - 23, bot - csize, "APPROXIMATION LEVEL 0");
			else
				outtextxy(right - 23, bot - csize, "INTERMEDIATE APPROXIMATION");

			line(300, top, 300, top + csizem1);
			if (WaveShown == DECOMP)  /* draw the proper arrow */
			{
				line(300, top + csizem1, 305, top + arrow3);
				line(300, top + csizem1, 295, top + arrow3);
			}
			else
			{
				line(300, top, 305, top + arrow2);
				line(300, top, 295, top + arrow2);
			}
			break;
		case INPUT:
			outtextxy(right - 23, bot - csize, "INPUT");
			break;
		case OUTPUT:
			outtextxy(right - 23, bot - csize, "OUTPUT");
			break;
	}

	settextjustify(LEFT_TEXT, TOP_TEXT);
}


void DrawPanel(void)
{
	char str[30];

	setfillstyle(EMPTY_FILL, BLACK);
	bar(328, 0, 639, frameH);
	setfillstyle(SOLID_FILL, WHITE);
	outtextxy(128, trow0,"WAVELET TRANSFORM DEMO");
	switch (HelpToggle)
	{
		case HELP1:
			outtextxy(328, trow0, "SIGNAL I/O - (I)NPUT READ  (O)UTPUT WRITE");
			outtextxy(328, trow1, "");
			outtextxy(328, trow2, "");
			outtextxy(328, trow3, "TRANSFORM OPERATIONS - SET: (L)EVELS");
			outtextxy(328, trow4, "(T)OGGLE DECOMP/RECON  (E)XECUTE");
			outtextxy(328, trow5, "");
			outtextxy(328, trow6, "F1 - HELP TOGGLE   (M)ORE HELP...");
			outtextxy(328, trow7, "PGUP/PGDN - SCROLL   ESC - EXIT PROGRAM");
			break;
		case HELP2:
			outtextxy(328, trow0, "WAVELET COEFFICIENTS - SET: (A)LPHA  (B)ETA");
			outtextxy(328, trow1, "a - f: WAVELET COEFFICIENTS");
			outtextxy(328, trow2, "");
			outtextxy(328, trow3, "EXPANSION COEFFICIENTS - (C)OPY");
			outtextxy(328, trow4, "(P)RUNE  (R)EAD  (V)IEW  (W)RITE (Z)ERO");
			outtextxy(328, trow5, "");
			outtextxy(328, trow6, "F1 - HELP TOGGLE   (M)ORE HELP...");
			outtextxy(328, trow7, "PGUP/PGDN - SCROLL   ESC - EXIT PROGRAM");
			break;
		case NONE:
			outtextxy(328, trow2, "LENGTH:");  outtextxy(328, trow4, "ALPHA:");
			outtextxy(328, trow5, "BETA:");  outtextxy(328, trow6, "LEVELS:");
			outtextxy(512, trow0, "a:");  outtextxy(512, trow1, "b:");
			outtextxy(512, trow2, "c:");  outtextxy(512, trow3, "d:");
			outtextxy(512, trow4, "e:");  outtextxy(512, trow5, "f:");
			outtextxy(424 ,trow6, "SIGNAL MSE:");
			itoa(DispLength, str, 10);  outtextxy(384, trow2, str);
			gcvt(*DispAlpha, 15, str);  outtextxy(376, trow4, str);
			gcvt(*DispBeta, 15, str);  outtextxy(368, trow5, str);
			itoa(DispWaveLevels, str, 10);  outtextxy(384, trow6, str);
			gcvt(DispWaveCoeff[0], 10, str);  outtextxy(528, trow0, str);
			gcvt(DispWaveCoeff[1], 10, str);  outtextxy(528, trow1, str);
			gcvt(DispWaveCoeff[2], 10, str);  outtextxy(528, trow2, str);
			gcvt(DispWaveCoeff[3], 10, str);  outtextxy(528, trow3, str);
			gcvt(DispWaveCoeff[4], 10, str);  outtextxy(528, trow4, str);
			gcvt(DispWaveCoeff[5], 10, str);  outtextxy(528, trow5, str);
			gcvt(DispMSE, 10, str);  outtextxy(504, trow6, str);
			if (WaveShown == DECOMP)
			{
				outtextxy(328, trow0, "DECOMPOSITION");
				outtextxy(328, trow1, "SIG. IN:");
				outtextxy(384, trow1, DispSignalname);
				outtextxy(328, trow3, "COF. OUT:");
				outtextxy(392, trow3, DispCoeffname);
			}
			else
			{
				outtextxy(328, trow0, "RECONSTRUCTION");
				outtextxy(328, trow1, "SIG. OUT:");
				outtextxy(392, trow2, DispSignalname);
				outtextxy(328, trow3, "COF. IN:");
				outtextxy(384, trow3, DispCoeffname);
			}
	}
}


void DrawViewedLevels(void)
{
	int i, j, top, bot;

	clearviewport();
	for (i = TopResShown, j = 0; i <= DispWaveLevels && j < 3; i++, j++)
	{
		top = frameH * j;
		bot = top + frameHm1;
		if (i == 0)
		{
			if (WaveShown == DECOMP)
				DrawLevel(0, 0, 313, bot, DispData, DispLength, 0, 1, INPUT);
			else
				DrawLevel(0, 0, 313, bot, DispData, DispLength, 0, 1, OUTPUT);

			DrawPanel();
		}
		else if (i <= DispWaveLevels)
		{
			DrawLevel(0, top, 313, bot, DispExCoeff[2 * (i - 1)],
												(DispLength >> i), i, 1 << i, APPROX);
			DrawLevel(326, top, 639, bot, DispExCoeff[(2 * (i - 1)) + 1],
																(DispLength >> i), i, 1 << i, DETAIL);
		}
	}
}


void ScrollInLevel(int res, int maxlevels, int scrollup)
{
	int error, Gtop, Gbot;

	error = ScrollGraph(scrollup); /* scroll display in desired direction */
	if (!error)
	{     /* error is usually not enough memory for scrolling in VGA mode */
		closegraph();
		printf("Memory allocation failure during ScrollGraph\n");
		exit(1);
	}

	if (scrollup) /* TRUE value places new graph at top of the screen */
	{
		Gtop = 0;
		Gbot = frameHm1;
	}
	else /* place new graph at bottom of screen if it exist */
	{
		Gtop = maxY - (frameHm1 + rowsremain);
		Gbot = maxY - rowsremain;
	}

	if (res == 0)
	{
		setfillstyle(EMPTY_FILL, BLACK);
		bar(0, 0, 639, frameHm1);
		setfillstyle(SOLID_FILL, WHITE);
		if (WaveShown == DECOMP)
			DrawLevel(0, 0, 313, frameHm1, DispData, DispLength, 0, 1, INPUT);
		else
			DrawLevel(0, 0, 313, frameHm1, DispData, DispLength, 0, 1, OUTPUT);

		DrawPanel();
	}
	else if (res <= maxlevels)
	{
		DrawLevel(0, Gtop, 313, Gbot, DispExCoeff[2 * (res - 1)],
				(DispLength >> res), res, (1 << res), APPROX);
		DrawLevel(326, Gtop, 639, Gbot, DispExCoeff[(2 * (res - 1)) + 1],
				(DispLength >> res), res, (1 << res), DETAIL);
	}
}


unsigned char GetGraphicInput(int left, int top, char *in_str, int length)
{
	unsigned char char_count;
	char c[2];

	outtextxy(328, trow7, "PRESS ESC TO CANCEL ENTRY");
	setfillstyle(EMPTY_FILL, BLACK); /* blank out the field */
	bar(left, top + 1, left + (length * 8) - 1, top + csize);
	char_count = 0;
	c[1] = '\0';
	do
	{
		line(left + (char_count * 8), top + csize, /* draw graphic cursor */
				left + (char_count * 8) + 6, top + csize);
		c[0] = (char) getch();
		if ((c[0] >= ' ') && (c[0] <= '~') && (char_count < length))
		{
			if ((c[0] >= 'a') && (c[0] <= 'z'))
				c[0] &= 0xdf;

				/* replace current cursor position with typed character */
			bar(left + char_count * 8, top + 1,
					left + char_count * 8 + 7, top + csizep1);
			outtextxy(left + char_count * 8, top, c);
			in_str[char_count++] = c[0];
		}
		else if ((c[0] == 0x08) && char_count)
		{								/* backspace - rubout last char */
			bar(left + (char_count - 1) * 8, top + 1,
					left + char_count * 8 + 7, top + csizep1);
			in_str[char_count--] = '\0';
		}
		else if (c[0] == 0x0d) /* ENTER characer - complete the entry */
			in_str[char_count] = '\0';

		else if (c[0] == 0x1B) /* <ESC> character - abort entry */
		{
			in_str[0] = '\0';
			char_count = 0;
		}
		else
			beep();

	}
	while ((c[0] != 0x0d) && (c[0] != 0x1b));

	setfillstyle(SOLID_FILL, WHITE);
	return char_count;
}



void GraphMessage(int left, int top, char *message_str)
{
	int length;

	length = -1;
	do  /* determine length of the given string */
	{
		length++;
	} while (message_str[length] != '\0');
	setfillstyle(EMPTY_FILL, BLACK); /* blank out the field */
	bar(left, top + 1, left + (length * 8) - 1, top + csize);
	outtextxy(left, top, message_str);
	setfillstyle(SOLID_FILL, WHITE);
}


void InitViewData(double *InData, double *OutData,
													double **ExCoeffD, double **ExCoeffR)
{
	int datascale, siglen;
	int i, j, k;

		/* initialize storage for viewed data */
	datascale = 0x0001 << DispWaveLevels;
	ViewLength = ((datascale - 1) * DispLength) / datascale
																				+ 5 * DispWaveLevels;
	ViewExDA = (double *) calloc(DispLength + ViewLength, sizeof(double));
	ViewExDD = (double *) calloc(ViewLength, sizeof(double));
	ViewExRA = (double *) calloc(DispLength + ViewLength, sizeof(double));
	ViewExRD = (double *) calloc(ViewLength, sizeof(double));
	ViewIndex = (int *) calloc(DispLength + ViewLength, sizeof(int));
	ViewLevel = (int *) calloc(DispLength + ViewLength, sizeof(int));
	ViewLength += DispLength;

	/* align start of detail arrays with start of approximation data */
	ViewExDD -= DispLength;
	ViewExRD -= DispLength;

	k = 0;

	for (i = 0; i < DispLength; i++) /* copy in the input and output data */
	{
		ViewExDA[k] = InData[i];
		ViewExRA[k] = OutData[i];
		ViewIndex[k] = i;
		ViewLevel[k++] = -1;
	}

	siglen = DispLength;
	for (i = 0; i < DispWaveLevels; i++) /* copy in expansion coefficients */
	{
		siglen /= 2;
		for (j = 0; j < siglen + 5; j++)
		{
			ViewExDA[k] = ExCoeffD[2 * i][j];
			ViewExRA[k] = ExCoeffR[2 * i][j];
			ViewExDD[k] = ExCoeffD[(2 * i) + 1][j];
			ViewExRD[k] = ExCoeffR[(2 * i) + 1][j];
			ViewIndex[k] = j;
			ViewLevel[k++] = DispWaveLevels - i - 1;
		}
	}

		/* position starting pointer to highest level
			 currently shown on the display */
	if (TopResShown == 0)
		ViewStart = 0;
	else
	{
		datascale = 0x0001 << (TopResShown - 1);
		ViewStart = DispLength + ((datascale - 1) * DispLength) / datascale
																								+ 5 * (TopResShown - 1);
	}

	settextstyle(DEFAULT_FONT, HORIZ_DIR, 1);
	ViewingData = TRUE;
}


void ExitViewData(void)
{
	ViewExRD += DispLength; /* restore original pointer locations ... */
	ViewExDD += DispLength; /* to advoid null pointer assignment error */
	free(ViewLevel);        /* free the data display arrays */
	free(ViewIndex);
	free(ViewExRD);
	free(ViewExRA);
	free(ViewExDD);
	free(ViewExDA);
	settextstyle(SMALL_FONT, HORIZ_DIR, 0); /* return character format */
	setusercharsize(7, 6, mulY, divY);      /* to the default settings */
	ViewingData = FALSE;
}


void ShowViewData(int offset)
{
	int i, j;
	char str[20];

	clearviewport();
	ViewStart += offset;

	if (ViewStart < 0) /* align to start of data if necessary */
		ViewStart = 0;
	else if (ViewStart >= ViewLength)
		ViewStart = ViewLength - 1;

	if (ViewStart < DispLength) /* headings depend upon position in data */
	{
		outtextxy(0, 0, "LVL/ELEMT       INPUT");
		outtextxy(376, 0, "   OUTPUT");
	}
	else
	{
		outtextxy(0, 0, "LVL/ELEMT   DECOMP. APPROX.   DECOMP. DETAIL");
		outtextxy(376, 0, "RECON. APPROX.   RECON. DETAIL");
	}

	outtextxy(0, maxY - 7, "Esc - EXIT   PGUP/PGDN - PAGE UP/DOWN");
	outtextxy(320, maxY - 7, "UP/DOWN ARROW - SCROLL UP/DOWN ONE LINE");

	for(i = 0, j = 1; i < viewlines; i++, j++)
	{
		if ((ViewStart + i) < ViewLength)
		{
			if (ViewLevel[ViewStart + i] != -1)
			{					 /* show levels only for wavelet transform coefficients */
				itoa(ViewLevel[ViewStart + i], str, 10);
				outtextxy(8, 8 * j, str);
			}

				/* display input and output arrays and approximation coefficients */
			itoa(ViewIndex[ViewStart + i], str, 10);
			outtextxy(40, 8 * j, str);
			gcvt(ViewExDA[ViewStart + i], 10, str);
			outtextxy(96, 8 * j, str);
			gcvt(ViewExRA[ViewStart + i], 10, str);
			outtextxy(376, 8 * j, str);

			if ((ViewStart + i) >= DispLength)
			{		/* print detail coefficients of wavelet transform coefficients */
				gcvt(ViewExDD[ViewStart + i], 10, str);
				outtextxy(240, 8 * j, str);
				gcvt(ViewExRD[ViewStart + i], 10, str);
				outtextxy(512, 8 * j, str);
			}
		}
	}
}


void beep(void)
{
	sound(500);
	delay(50);
	nosound();
}
