/*****************************************************************************
*   "Irit" - the 3d polygonal solid modeller.				     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
*   General routines to	handle the graphic calls.			     *
* currently supported devices:						     *
* Input: Keyboard, mouse.						     *
* Output: Hercules, EGA/VGA.						     *
*****************************************************************************/

#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <dir.h>
#include <dos.h>
#include <math.h>
#include <ctype.h>
#include <graphics.h>
#include <stdarg.h>
#include <io.h>
#include <fcntl.h>
#include "program.h"
#include "graphgng.h"
#include "allocatg.h"
#include "graphgnl.h"
#include "mousedrv.h"

#define  SINGLEPAGE

extern int MouseExists;			       /* Responsibility of program! */
extern int GraphDriver;						       /* "" */

int    GraphMode;				 /* The Graphics mode value. */
double AspectRatio;		   /* Aspect ratio of a pixel on the screen. */
int    ScreenMaxX, ScreenMaxY;	    /* The maximum resolution of the screen. */
int    ScreenMaxY2;				 	  /* ScreenMaxY / 2. */
int    ScreenMaxColors;		       /* The maximum # of colors available. */
int    ScreenMaxPages;			/* The maximum # of pages available. */
int    ScreenErrorCode;			     /* Reports any graphics errors. */
int    CurrentCursorX, CurrentCursorY;		 /* Cursor current position. */
int    ScreenGraphicMode = FALSE;     /* TRUE if the screen in graphic mode. */

static double WndwAspectRatio, GlblAspectRatio;
static struct palettetype palette;	       /* Used to read palette info. */
static char *CursorImageBuffer;				    /* Cursor shape. */
static int LocalMoveToX, LocalMoveToY;	      /* Save last position we move. */
static char LastGetChar;	    /* Last char recieved in UpdateGetPoint. */
static double MouseXRatio, MouseYRatio;
static int DefaultFontSize;		        /* Depend on graphic driver. */
static int ScreenCursorColor;	    /* Graphic and text (in graphic screen). */
static int ViewPortX, ViewPortY;    /* Viewport offset from top left (0, 0). */
static int OldStdOut;	  /* To recover stdout when we done with graph mode. */
static int CrntWindowName;		 /* Unique index for current window. */

/* #define NOGRAPHICS	  /* If no graphics to perform (only routine calls). */
/* #define DEBUG		   /* Print graphics driver info and dies... */

/****************************************************************************
* Routine to move to a normalized point	between	-1..1 on both axes :	    *
****************************************************************************/
void GGMyMove(double x, double y)
{
#ifndef	NOGRAPHICS
    /* Note I use ScreenMaxY2 as X>ScreenMaxY2 left for	text (menus). */
    LocalMoveToX = (int) ((x * WndwAspectRatio * ScreenMaxY2 + ScreenMaxY2) *
							GlblAspectRatio);
    LocalMoveToY = (int) (-y * ScreenMaxY2 + ScreenMaxY2);
    moveto(LocalMoveToX - ViewPortX, LocalMoveToY - ViewPortY);
#endif /* NOGRAPHICS */
}

/****************************************************************************
* Routine to draw to a normalized point	between	-1..1 on both axes :	    *
****************************************************************************/
void GGMyDraw(double x, double y)
{
#ifndef	NOGRAPHICS
    int	NewX, NewY;

    /* Note I use ScreenMaxY2 as X>ScreenMaxY2 left for	text (menus). */
    NewX = (int) ((x * WndwAspectRatio * ScreenMaxY2 + ScreenMaxY2) *
							GlblAspectRatio);
    NewY = (int) (-y * ScreenMaxY2 + ScreenMaxY2);
    line(LocalMoveToX - ViewPortX, LocalMoveToY - ViewPortY,
					NewX - ViewPortX, NewY - ViewPortY);
    LocalMoveToX = NewX;
    LocalMoveToY = NewY;
#endif /* NOGRAPHICS */
}

/****************************************************************************
* Routine to draw to a normelized point	between	-1..1 on both axes :	    *
****************************************************************************/
void GGMySetColor(int color)
{
#ifndef	NOGRAPHICS
    if (color >= ScreenMaxColors) color = color % (ScreenMaxColors-1) + 1;
    setcolor(color);
#endif /* NOGRAPHICS */
}

/****************************************************************************
* Routine to reset all the system to starting condition	:		    *
****************************************************************************/
void GGInitGraph(void)
{
#ifndef	NOGRAPHICS
    int	xasp, yasp,			   /* Used to read the aspect ratio. */
	i, j;

    if (registerbgidriver(Herc_driver) < 0) MyExit(1);
    if (registerbgidriver(EGAVGA_driver) < 0) MyExit(1);

    /* For some wierd reason, some machines waits much more than expected on */
    /* the first delay. Lets do it now, so people will consider it part of   */
    /* the initialization (make it a feature...)...                          */
    delay(1);

    if (GraphDriver == 0) {
	/* Use autodetect feature of graphic library to see what we have. */
	detectgraph(&GraphDriver, &GraphMode);
	if (GraphDriver < 0) {
	    fprintf(stderr, "Auto detect: No graphics device detected\n");
	    MyExit(1);
	}
    }

    /* Put in the following any graphic driver specific setup: */
    switch (GraphDriver) {
	case EGA:
	    GraphMode = EGAHI;
	    break;
	case EGA64:
	    GraphMode = EGA64HI;
	    break;
	case EGAMONO:
	    GraphMode = EGAMONOHI;
	    break;
	case HERCMONO:
	    GraphMode = HERCMONOHI;
	    break;
	case VGA:
	    GraphMode = VGAHI;
	    break;
	default:
	    fprintf(stderr, "Requested graphic device (%d - see .cfg file) is not supported\n",
		GraphDriver);
	    MyExit(1);
	    break;
    }

    initgraph(&GraphDriver, &GraphMode, "");
    ScreenErrorCode = graphresult();	   /* Read result of initialization. */
    if (ScreenErrorCode != grOk) {	       /* Error occured during init. */
	fprintf(stderr, " Graphics System Error: %s\n",
				grapherrormsg(ScreenErrorCode));
	MyExit(1);
    }

    getpalette(&palette);		     /* Read the palette from board. */
    ScreenMaxColors = getmaxcolor() + 1;   /* Read maximum number of colors. */
    ScreenCursorColor = (ScreenMaxColors > 1 ? ScreenMaxColors - 1 :
					       ScreenMaxColors);

    ScreenMaxX = getmaxx();			     /* Read size of screen. */
    ScreenMaxY = getmaxy();
    ScreenMaxY2 = ScreenMaxY / 2;
    CurrentCursorX = ScreenMaxX / 2;
    CurrentCursorY = ScreenMaxY2;
    getaspectratio(&xasp, &yasp);		/* Read the hardware aspect. */
    /* Get correction factor: */
    AspectRatio = (double) xasp / (double) yasp;
    GlblAspectRatio = 1.0 / AspectRatio;
    WndwAspectRatio = 1.0;

    /* Put in the following any graphic driver specific setup: */
    switch (GraphDriver) {
	case HERCMONO:
	    DefaultFontSize = 1;			/* Fixed width font. */
	    ScreenMaxPages = 2;
	    break;

	case EGA:
	    DefaultFontSize = 1;			/* Fixed width font. */
	    ScreenMaxPages = 2;

	    GlblAspectRatio = 1.2; /* I dont like it either, but its simple. */
	    break;

	case EGA64:
	    DefaultFontSize = 1;			/* Fixed width font. */
	    ScreenMaxPages = 1;

	    GlblAspectRatio = 1.2; /* I dont like it either, but its simple. */
	    break;

	case EGAMONO:
	    DefaultFontSize = 1;			/* Fixed width font. */
	    ScreenMaxPages = 2;
	    ScreenMaxColors = 2;

	    GlblAspectRatio = 1.2; /* I dont like it either, but its simple. */
	    break;

	case VGA:
	    DefaultFontSize = 1;			/* Fixed width font. */
	    ScreenMaxPages = 2;

	    GlblAspectRatio = 0.88;/* I dont like it either, but its simple. */
	    break;
    }

#   ifdef SINGLEPAGE
	ScreenMaxPages = 1;
#   endif /* SINGLEPAGE */

#   ifdef DEBUG
	GGCloseGraph();
	fprintf(stderr, "ScreenMaxX = %d, ScreenMaxY = %d, MaxColors = %d\n",
	    ScreenMaxX, ScreenMaxY, ScreenMaxColors);
	fprintf(stderr, "AspectRatio = %lf, ViewPortX = %d, ViewPortY = %d\n",
	    AspectRatio, ViewPortX, ViewPortY);
	MyExit(1);
#   endif /* DEBUG */

    /* Prepare the cursor (Arrow) image : */
    cleardevice();
    GGMySetColor(ScreenCursorColor);
    setlinestyle(SOLID_LINE, 0,	NORM_WIDTH);
    line(0, 0, CURSOR_IMAGE_X, CURSOR_IMAGE_Y);
    j = CURSOR_IMAGE_X / 3;
    for	(i=1; i<=7; i++) line(0, 0, j, j + i);	     /* Draw the arrow head. */
    j = CURSOR_IMAGE_Y / 3;
    for	(i=1; i<=7; i++) line(0, 0, j + i, j);

    CursorImageBuffer = MyMalloc(imagesize(0, 0, CURSOR_IMAGE_X,
						 CURSOR_IMAGE_Y), OTHER_TYPE);
    getimage(0,	0, CURSOR_IMAGE_X, CURSOR_IMAGE_Y, CursorImageBuffer);

    settextstyle(DEFAULT_FONT, HORIZ_DIR, DefaultFontSize);

    if (MouseExists) {
	switch (GraphDriver) {
	    case EGA:
	    case EGA64:
	    case EGAMONO:
	    case VGA:
		MSMouseXmax = 1000;			/* Set Column Range. */
		MSMouseYmax = 1000;			   /* Set Row range. */
		break;
	    case HERCMONO:
		/* Compensate on the text mode the mouse knows about as      */
		/* hercules is not supported by the mouse driver!            */
		MSMouseXmax = 8000;			/* Set Column Range. */
		MSMouseYmax = 8000;			   /* Set Row range. */
		break;
	}
	MouseXRatio = ((double) ScreenMaxX) / MSMouseXmax;
	MouseYRatio = ((double) ScreenMaxY) / MSMouseYmax;
    }

    ScreenGraphicMode = TRUE;
    i = open("nul", O_WRONLY);	  /* Redirect the stdout to nul: (no ^C...). */
    fflush(stdout);
    OldStdOut = dup(1);
    dup2(i, 1);
    close(i);
#endif /* NOGRAPHICS */
}

/****************************************************************************
* Routine to close and shutdown	graphic	mode :				    *
****************************************************************************/
void GGCloseGraph(void)
{
#ifndef	NOGRAPHICS
    closegraph();			  /* Return the system to text mode. */
    ScreenGraphicMode = FALSE;
    dup2(OldStdOut, 1);		    /* Recover stdout to its regular status. */
    close(OldStdOut);
#endif /* NOGRAPHICS */
}

/****************************************************************************
* Routine to update x, y coordinates according to current x, y and key	    *
* pressed. Returns TRUE only if <Enter> or alpha char is pressed.	    *
****************************************************************************/
static int GGUpdateGetPointKbd(int *x, int *y)
{
    int	BrkPressed = FALSE;

    switch (LastGetChar	= getch()) {
	case 0:
	    switch(getch()) {			      /* extended character: */
		case 16:
		    MyExit(0);			  /* Alt - Q, async. exit... */
		case 71:
		    *x -= 1;		      /* Arrowes - move 1 at a time. */
		    *y -= 1;
		    break;
		case 72:
		    *y -= 1;
		    break;
		case 73:
		    *x += 1;
		    *y -= 1;
		    break;
		case 75:
		    *x -= 1;
		    break;
		case 77:
		    *x += 1;
		    break;
		case 79:
		    *x -= 1;
		    *y += 1;
		    break;
		case 80:
		    *y += 1;
		    break;
		case 81:
		    *x += 1;
		    *y += 1;
		    break;
	    }
	    break;
	case 10 :
	case 13 :
	    BrkPressed = TRUE;
	    break;
	case '1':
	    *x -= 10;		      /* Shifted arrows - move 10 at a time. */
	    *y += 10;
	    break;
	case '2':
	    *y += 10;
	    break;
	case '3':
	    *x += 10;
	    *y += 10;
	    break;
	case '4':
	    *x -= 10;
	    break;
	case '6':
	    *x += 10;
	    break;
	case '7':
	    *x -= 10;
	    *y -= 10;
	    break;
	case '8':
	    *y -= 10;
	    break;
	case '9':
	    *x += 10;
	    *y -= 10;
	    break;
	/* If the key pressed is an alpha - exit. */
	default:
	    if isalpha(LastGetChar) BrkPressed = TRUE;
	    break;
    }

    return BrkPressed;
}

/****************************************************************************
* Routine to get one x, y selected point in normelized form (-1..1) :	    *
****************************************************************************/
void GGGetPoint(double *x, double *y)
{
#ifndef	NOGRAPHICS
    int	Xtemp, Ytemp, Buttons, Xscreen, Yscreen, Quit, GetInput;
    struct viewporttype view;

    getviewsettings(&view);
    setviewport(0, 0, getmaxx(), getmaxy(), FALSE);

    LastGetChar = 0x001;	     /* Make in impossible if mouse input... */

    Xtemp = CurrentCursorX;
    Ytemp = CurrentCursorY;
    putimage(CurrentCursorX,	     /* Draw the cursor - starting position. */
	     CurrentCursorY, CursorImageBuffer,	XOR_PUT);

    if (MouseExists)			/* Update mouse on current position. */
	MouseSetPosition((int) (CurrentCursorX / MouseXRatio),
			 (int) (CurrentCursorY / MouseYRatio));

    Quit = FALSE;
    do {
	GetInput = FALSE;
	do {
	    /* Wait for input from one of the devices: Mouse/Keyboard. */
	    if (MouseExists && MouseQueryBuffer()) {
		MouseGetBuffer(&Xscreen, &Yscreen, &Buttons);
		GetInput = TRUE;
		CurrentCursorX = (int) (Xscreen * MouseXRatio);
		CurrentCursorY = (int) (Yscreen * MouseYRatio);
		Quit = Buttons & 0x01;
	    }
	    if (kbhit()) {
		Quit = GGUpdateGetPointKbd(&CurrentCursorX, &CurrentCursorY);
		if (MouseExists)        /* Update mouse on the new position. */
		    MouseSetPosition((int) (CurrentCursorX / MouseXRatio),
                                      (int) (CurrentCursorY / MouseYRatio));
		GetInput = TRUE;
	    }

	}
	while (!GetInput);
	if (CurrentCursorX < 0) CurrentCursorX = 0;
	if (CurrentCursorY < 0) CurrentCursorY = 0;
	if (CurrentCursorX > ScreenMaxX - CURSOR_IMAGE_X)
	    CurrentCursorX = ScreenMaxX	- CURSOR_IMAGE_X;
	if (CurrentCursorY > ScreenMaxY	- CURSOR_IMAGE_Y / 2)
	    CurrentCursorY = ScreenMaxY - CURSOR_IMAGE_Y / 2;

	putimage(Xtemp, Ytemp, CursorImageBuffer, XOR_PUT);/* Erase old crsr!*/
	putimage(CurrentCursorX, CurrentCursorY, CursorImageBuffer,
						 XOR_PUT); /* Draw new crsr! */
	Xtemp = CurrentCursorX;	    /* Save them so we could erase it later. */
	Ytemp = CurrentCursorY;
    }
    while (!Quit);

    putimage(Xtemp,		    /* Erase last old cursor before quiting. */
	     Ytemp, CursorImageBuffer, XOR_PUT);

    /* See GGMyMove for ScreenMaxY2. */
    *x = ((double)(CurrentCursorX) / GlblAspectRatio - ScreenMaxY2) /
								ScreenMaxY2;
    *y = ((double)(CurrentCursorY) - ScreenMaxY2) / (-ScreenMaxY2);

    setviewport(view.left, view.top, view.right, view.bottom, view.clip);

#endif /* NOGRAPHICS */
}

/****************************************************************************
* Routine to draw a point on screen as marker +	with a title :		    *
****************************************************************************/
void GGDrawPoint(double p[], char title[], int PointColor)
{
#ifndef	NOGRAPHICS
    GGMySetColor(PointColor);
    GGMyMove(p[1] + POINT_SIZE, p[2]);
    GGMyDraw(p[1] - POINT_SIZE, p[2]);
    GGMyMove(p[1], p[2] + POINT_SIZE);
    GGMyDraw(p[1], p[2] - POINT_SIZE);

    GGXYPutStr(p[1] + POINT_TITLE, p[2] + POINT_TITLE, title);
#endif /* NOGRAPHICS */
}

/*****************************************************************************
*    Routine to set up the view port.					     *
*****************************************************************************/
void GGWindowViewPort(double XMin, double YMin,
		      double XMax, double YMax, int WindowName)
{
#ifndef	NOGRAPHICS
    CrntWindowName = WindowName;
    switch (WindowName) {
	case NO_WINDOW_NAME:
	    break;
	case INPUT_WINDOW_NAME:
	    WndwAspectRatio = 1.0;
	    break;
	case STATUS_WINDOW_NAME:
	    WndwAspectRatio = 1.0;
	    break;
	case VIEW_WINDOW_NAME:
	    WndwAspectRatio = 1.2;
	    break;
    }

    setviewport(
	(ViewPortX = (int) ((XMin * ScreenMaxY2 + ScreenMaxY2) *
							GlblAspectRatio)),
	(ViewPortY = (int) (-YMax * ScreenMaxY2 + ScreenMaxY2)),
	(int) ((XMax * ScreenMaxY2 + ScreenMaxY2) * GlblAspectRatio),
	(int) (-YMin * ScreenMaxY2 + ScreenMaxY2),
	TRUE);
#endif /* NOGRAPHICS */
}

/*****************************************************************************
*   Routine to clear given window area.					     *
* Note the viewport on exit is the window only!				     *
*****************************************************************************/
void GGClearWindow(double XMin, double YMin,
		   double XMax, double YMax, int WindowName)
{
#ifndef	NOGRAPHICS
    GGWindowViewPort(XMin, YMin, XMax, YMax, WindowName);
    clearviewport();
#endif /* NOGRAPHICS */
}

/*****************************************************************************
*   Routine to clear all the screen.					     *
*****************************************************************************/
void GGClearAllScreen(void)
{
#ifndef	NOGRAPHICS
    cleardevice();
    ViewPortX = ViewPortY = 0;
#endif /* NOGRAPHICS */
}

/*****************************************************************************
* Routine to put one char in current position with given color:		     *
* The position of the cursor is updated horizontally (only).		     *
* If the char is BS (Back Space), the position is updated backward with no   *
* printing. Note it is assume no over/underflow of horizontal position.	     *
*****************************************************************************/
void GGXYPutChar(char c, int Color)
{
#ifndef	NOGRAPHICS
    char c1[] = " ";			       /* One char in length string. */

    if (c == BSPACE) {
	LocalMoveToX -= textwidth(" ");	      /* Must be non justified font! */
	moveto(LocalMoveToX - ViewPortX, LocalMoveToY - ViewPortY);
    }
    else {
	c1[0] = c;
	GGMySetColor(Color);
	outtext(c1);
	LocalMoveToX += textwidth(" ");	      /* Must be non justified font! */
	moveto(LocalMoveToX - ViewPortX, LocalMoveToY - ViewPortY);
    }
#endif /* NOGRAPHICS */
}

/*****************************************************************************
* Routine to print an message on the given location:			     *
*****************************************************************************/
void GGXYPutStr(double x, double y, char *s)
{
#ifndef	NOGRAPHICS
    GGMyMove(x, y);
    outtext(s);
#endif /* NOGRAPHICS */
}

/*****************************************************************************
* Routine to read one line terminated by <Enter> and visualized	on graphic   *
* screen.								     *
* Full line editing is supported which includes:			     *
* 1. Right and left arrow to move along the line.			     *
* 2. Delete to delete current character.				     *
* 3. Insert to toggle Insert/Overwrite mode.				     *
* 4. Backspace to delete character before current one.			     *
* 5. Home/End to move to beginning/End of string respectively.		     *
* 6. Return to accept current line.					     *
* 7. Esc to clear current line.						     *
* If s is not empty it is used as starting string.			     *
* Line is drawn	starting from position x, y on screen (normalized -1..1).    *
*****************************************************************************/
void GGGetGraphicLine(double x, double y, char s[], int Length, int Color)
{
#ifndef	NOGRAPHICS
    static int Insert = TRUE;
    int	NextKey, Xstep, CursorPos = 0, FirstVisible = 0, Len = strlen(s),
	EndOfString = FALSE, FirstTime = s[0];
    char *Cursor;

    /* Force the string to be empty. Note we saved s[0] in FirstTime, so if  */
    /* the string is empty we change nothing, otherwise we can recover it.   */
    s[0] = 0;

    Xstep = textwidth(" ");	 /* Get the width is pixels of current font. */

    while (!EndOfString) {
	if (Len >= Length) {
	    s[Length-1] = 0;
	    return;
	}

	/* Set the first character in string to be displayed: */
	FirstVisible = CursorPos > 70 ? CursorPos - 10 : 0;
	GGMySetColor(Color);		  /* Draw the string and the cursor: */
	GGXYPutStr(x, y, &s[FirstVisible]);
	Cursor = (Insert ? "_" : "-");
	GGMySetColor(ScreenCursorColor);
	GGMyMove(x, y);
	moverel((CursorPos - FirstVisible) * Xstep, 0);
	outtext(Cursor);

	NextKey = GGGetKey();

	GGMySetColor(BLACK);		 /* Erase the string and the cursor: */
	GGMyMove(x, y);
	moverel((CursorPos - FirstVisible) * Xstep, 0);
	outtext(Cursor);
	GGXYPutStr(x, y, &s[FirstVisible]);

	switch (NextKey) {
	    case KEY_BSPACE:
		if (CursorPos == 0) {
		    GGTone(1000, 100);			 /* Do some noise... */
		    break;
		}
		movmem(&s[CursorPos], &s[CursorPos - 1], Len - CursorPos + 1);
		CursorPos--;
		Len--;
		break;
	    case KEY_DELETE:
		if (CursorPos >= Len) {
		    GGTone(1000, 100);			 /* Do some noise... */
		    break;
		}
		movmem(&s[CursorPos + 1], &s[CursorPos], Len - CursorPos);
		Len--;
		break;
	    case KEY_RIGHT:
		if (CursorPos >= Len) {
		    GGTone(1000, 100);			 /* Do some noise... */
		    break;
		}
		CursorPos++;
		break;
	    case KEY_LEFT:
		if (CursorPos <= 0) {
		    GGTone(1000, 100);			 /* Do some noise... */
		    break;
		}
		CursorPos--;
		break;
	    case KEY_UP:
		if (FirstTime) s[0] = FirstTime;
		break;
	    case KEY_RETURN:
		EndOfString = TRUE;
		break;
	    case KEY_ESC:
		Len = 0;				/* Clear everything. */
		CursorPos = 0;
		s[0] = 0;
		break;
	    case KEY_HOME:
		CursorPos = 0;
		break;
	    case KEY_END:
		CursorPos = Len;
		break;
	    case KEY_INSERT:
		Insert = !Insert;
		break;
	    default:				      /* Regular ascii char. */
		if (Insert) {
		    movmem(&s[CursorPos], &s[CursorPos + 1],
							Len - CursorPos + 1);
		    Len++;
		}
		else if (CursorPos == Len) Len++;/* We are on last character.*/
		s[CursorPos++] = NextKey;
		if (CursorPos == Len) s[CursorPos] = 0;
		break;
	}
	FirstTime = FALSE;
    }
#endif /* NOGRAPHICS */
}

/*****************************************************************************
* Get a key from keyboard, and translating operational keys into special     *
* codes (>255).								     *
*****************************************************************************/
static int GGGetKey(void)
{
    char c;

    while (TRUE) switch (c = getch()) {
	case 0:		      /* Extended code - get the next extended char. */
	    switch (getch()) {
		case 75: return KEY_LEFT;
		case 77: return KEY_RIGHT;
		case 71: return KEY_HOME;
		case 72: return KEY_UP;
		case 79: return KEY_END;
		case 83: return KEY_DELETE;
		case 82: return KEY_INSERT;
	    }
	    break;
	case 8:
	    return KEY_BSPACE;
	case 10:
	case 13:
	    return KEY_RETURN;
	case 27:
	    return KEY_ESC;
	default:
	    if isprint(c) return c;
    }

    return KEY_RETURN;				    /* Make warnings silent. */
}

/*****************************************************************************
* Routine to make some sound with given Frequency, Time milliseconds:	     *
*****************************************************************************/
void GGTone(int Frequency, int Time)
{
    sound(Frequency);
    delay(Time);
    nosound();
}
