/*****************************************************************************
*   "Irit" - the 3d polygonal solid modeller.				     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
* Module to handle the windows used by the solid modeller.		     *
*****************************************************************************/

#ifdef __MSDOS__
#include <graphics.h>
#include <alloc.h>
#include <conio.h>
#endif /* __MSDOS__ */

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "program.h"
#include "graphgng.h"
#include "windowsl.h"
#include "windowsg.h"
#include "viewobjg.h"

#ifndef __MSDOS__
#include "xgraphic.h"
#endif /* __MSDOS__ */

#ifdef __MSDOS__

InputWindowStruct InputWindow =				    /* Input window. */
{   (int) INPUT_WINDOW_NAME,
    IW_MIN_X, IW_MAX_X, IW_MIN_Y, IW_MAX_Y,
    IW_FRAME_COLOR, IW_LINE_COLOR, IW_TEXT_COLOR,
    IW_NUM_OF_LINES,
    IW_FIRST_LINE_X, IW_FIRST_LINE_Y, IW_DIFF_Y_LINE
};

StatusWindowStruct StatusWindow =			   /* Status window. */
{   (int) STATUS_WINDOW_NAME,
    SW_MIN_X, SW_MAX_X, SW_MIN_Y, SW_MAX_Y,
    SW_FRACE_COLOR, SW_LINE_COLOR, SW_TEXT_COLOR,
    SW_NUM_OF_LINES,
    SW_FIRST_LINE_X, SW_FIRST_LINE_Y, SW_DIFF_Y_LINE
};

ViewWindowStruct ViewWindow =				     /* View window. */
{   (int) VIEW_WINDOW_NAME,
    VW_MIN_X, VW_MAX_X, VW_MIN_Y, VW_MAX_Y,
    VW_FRACE_COLOR, VW_LINE_COLOR, VW_TEXT_COLOR,
    VW_NUM_OF_LINES,
    VW_FIRST_LINE_X, VW_FIRST_LINE_Y, VW_DIFF_Y_LINE
};

struct GenerWindowStruct *GlblInputWindow, *GlblStatusWindow, *GlblViewWindow;

/* Set it to FALSE if the status window is used for other purpose (such as */
/* for interactive transformation (INTERACT command).			   */
static int UpdateStatusWindow = FALSE;

#else
#define IRIT_PROMPT	"Irit> "
#endif /* __MSDOS__ */

static void WndwDrawWindowFrame(GenerWindowStruct *Window);

/*****************************************************************************
*  Routine to setup all the window to zero state, assuming graphic mode.     *
*****************************************************************************/
void WndwSetUpAllWndws(void)
{
    int i;
    char Line[LINE_LEN_LONG];

    GGClearAllScreen();

    WndwDrawAllWndwFrames();			 /* Draw the windows frames. */

#ifdef __MSDOS__
    for (i=0; i<InputWindow.NumOfLines; i++)	  /* Clear all text in them. */
	strcpy(InputWindow.Lines[i], "");
    for (i=0; i<StatusWindow.NumOfLines; i++)
	strcpy(StatusWindow.Lines[i], "");
    for (i=0; i<ViewWindow.NumOfLines; i++)
	strcpy(ViewWindow.Lines[i], "");

    strcpy(StatusWindow.Lines[0], "Irit");
    strcpy(StatusWindow.Lines[2], VERSION);
    strcpy(StatusWindow.Lines[5], "Core Left");

    /* Display the entry messages on the view window: */
    for (i=0; i<(78 - strlen(PrgmHeader)) / 2; i++) Line[i] = ' '; Line[i] = 0;
    strcat(Line, PrgmHeader);
    WndwInputWindowPutStr(Line, NO_COLOR);
    WndwInputWindowPutStr("", NO_COLOR);

    for (i=0; i<(78 - strlen(CopyRight)) / 2; i++) Line[i] = ' '; Line[i] = 0;
    strcat(Line, CopyRight);
    WndwInputWindowPutStr(Line, NO_COLOR);
    WndwInputWindowPutStr("", NO_COLOR);

    for (i=0; i<(78 - strlen(AuthorName)) / 2; i++) Line[i] = ' '; Line[i] = 0;
    strcat(Line, AuthorName);
    WndwInputWindowPutStr(Line, NO_COLOR);

    UpdateStatusWindow = TRUE;
    WndwStatusWindowDraw();  /* Update the status window - beginning status. */

    GlblInputWindow =  (GenerWindowStruct *) &InputWindow;
    GlblStatusWindow = (GenerWindowStruct *) &StatusWindow;
    GlblViewWindow =   (GenerWindowStruct *) &ViewWindow;
#endif /* __MSDOS__ */
}

/*****************************************************************************
*  Routine to draw all the frames of all the windows defined in the system:  *
*****************************************************************************/
void WndwDrawAllWndwFrames(void)
{
#ifdef __MSDOS__
    WndwDrawWindowFrame((GenerWindowStruct *) &InputWindow);
    WndwDrawWindowFrame((GenerWindowStruct *) &StatusWindow);
    WndwDrawWindowFrame((GenerWindowStruct *) &ViewWindow);
#endif /* __MSDOS__ */
}

/*****************************************************************************
*  Routine to draw a frame for the window given:			     *
*****************************************************************************/
static void WndwDrawWindowFrame(GenerWindowStruct *Window)
{
    int i;

    RealType MinX = Window -> MinX, MaxX = Window -> MaxX,
	     MinY = Window -> MinY, MaxY = Window -> MaxY,
	     Dx = FRAME_X_WIDTH / 10.0, Dy = FRAME_Y_WIDTH / 10.0;

    GGMySetColor(Window->FrameColor);

    for (i=0; i<10; i++) {
	GGMyMove(MinX, MinY);
	GGMyDraw(MinX, MaxY);
	GGMyDraw(MaxX, MaxY);
	GGMyDraw(MaxX, MinY);
	GGMyDraw(MinX, MinY);

	MinX -= Dx;
	MinY -= Dy;
	MaxX += Dx;
	MaxY += Dy;
    }
}

/*****************************************************************************
*  Routine to clear the given window interior:				     *
*****************************************************************************/
void WndwClearWindow(GenerWindowStruct *Window)
{
    GGClearWindow(Window -> MinX, Window -> MinY,
		  Window -> MaxX, Window -> MaxY, Window -> WindowName);
}

/*****************************************************************************
*  Routine to wait for user keystroke while drawing blinking cursor:	     *
* No data is returned - used as pause only.				     *
*****************************************************************************/
void WndwPause(RealType *R)
{
#ifdef __MSDOS__
    if (!APX_EQ(*R, 0.0)) while (kbhit()) getch();	     /* Flush stdin. */

    WndwInputWindowPause(InputWindow.FirstLineX,
			 InputWindow.FirstLineY - InputWindow.DiffYLine *
						  InputWindow.NumOfLines);
#else
    fprintf(stderr, "Type return to continue:");
    getchar();
#endif /* __MSDOS__ */
}

/*****************************************************************************
*  Routine to wait for user keystroke while drawing blinking cursor and	     *
* returning that keystroke.						     *
*****************************************************************************/
char WndwInputWindowPause(double X, double Y)
{
#ifdef __MSDOS__
    int i;

    GGWindowViewPort(InputWindow.MinX, InputWindow.MinY,
		     InputWindow.MaxX, InputWindow.MaxY,
		     (int) INPUT_WINDOW_NAME);

    do {
	GGMySetColor(MAGENTA);
	if (!kbhit()) GGXYPutStr(X, Y, "\x0A");
	for (i=0; i<DelayConstant; i++) if (kbhit()) break;
	GGMySetColor(BLACK);
	GGXYPutStr(X, Y, "\x0A");

	GGMySetColor(MAGENTA);
	if (!kbhit()) GGXYPutStr(X, Y, "\x09");
	for (i=0; i<DelayConstant; i++) if (kbhit()) break;
	GGMySetColor(BLACK);
	GGXYPutStr(X, Y, "\x09");
    } while (!kbhit());

    return getch();				 /* Discard the typed chars. */
#else
    fprintf(stderr, "Type return to continue:");
    getchar();
#endif /* __MSDOS__ */
}

/*****************************************************************************
*  Routine to handle the text on the Input Window. This routine calls the    *
* regular WndwInputWindowPutStr routine (below) but pauses every screen full *
* of data. if Reset is TRUE then number of lines on screen count is cleared. *
*  Note it is exactly the same as WndwInputWindowPutStr in non MSDOS system. *
*****************************************************************************/
void WndwInputWindowPutStrFS(char *str, int Color, int Reset)
{
#ifdef __MSDOS__
    static LineCount = 0;

    if (Reset) LineCount = 0;

    if (LineCount == GlblInputWindow -> NumOfLines) {		   /* Pause. */
	WndwInputWindowPause(InputWindow.MaxX - 0.04,
			     InputWindow.FirstLineY -
			     InputWindow.DiffYLine *
			     InputWindow.NumOfLines);
	LineCount = 0;
    }
    LineCount++;

#endif /* __MSDOS__ */

    WndwInputWindowPutStr(str, Color);
}

/*****************************************************************************
*  Routine to toggle the input window log file printing. If turned on, test  *
* is made if file has been opened and if not open it.			     *
*****************************************************************************/
void WndwLogPrint(RealType *Set)
{
    char s[LINE_LEN];

    if (APX_EQ(*Set, 0.0)) {
	PrintLogFile = FALSE;
	fflush(LogFile);
	return;
    }

    if (LogFile == NULL) {		      /* Not open yet - open it now: */
	if ((LogFile = fopen(LogFileName, "w")) == NULL) {
	    sprintf(s, "Failed to open log file \"%s\"", LogFileName);
	    WndwInputWindowPutStr(s, RED);
	    return;
	}
	PrintLogFile = TRUE;
    }
}

/*****************************************************************************
*  Routine to handle the text on the Input Window. This window echoes all    *
* the input steam - the input parser input. Errors or information is also    *
* displayed in this window.						     *
*****************************************************************************/
void WndwInputWindowPutStr(char *str, int Color)
{
    char c;
    int i;

    for (i=0; i<strlen(str); i++)
	if (str[i] == TAB) str[i] = ' ';		/* Strip off tabs... */
    for (i=strlen(str); isspace(str[--i]););		 /* Strip off CR/LF. */
    if (str[i+1] != 0) str[i+1] = 0;

    if (PrintLogFile) fprintf(LogFile, "%s\n", str);

#ifdef __MSDOS__
    if (kbhit())		    /* Test and handle Control S/Q protocol. */
	if ((c = getch()) == '\x13')			    /* Its Ctrl - s. */
	    while (getch() != '\x11');		       /* Wait for Ctrl - q. */
	else ungetch(c);

    for (i=1; i<InputWindow.NumOfLines; i++)
	strcpy(InputWindow.Lines[i-1], InputWindow.Lines[i]);
    strncpy(InputWindow.Lines[InputWindow.NumOfLines - 1], str, LINE_LEN-1);

    /* Now update the screen content itself: */
    WndwClearWindow((GenerWindowStruct *) &InputWindow);
    GGMySetColor(InputWindow.TextColor);
    for (i=0; i<InputWindow.NumOfLines; i++) {
	if (i == InputWindow.NumOfLines-1 && Color != NO_COLOR)
	    GGMySetColor(Color);	/* Make last line in given color */
	GGXYPutStr(InputWindow.FirstLineX,
		   InputWindow.FirstLineY - InputWindow.DiffYLine * i,
		   InputWindow.Lines[i]);
    }
#else
    printf("%s\n", str);
#endif /* __MSDOS__ */
}

/*****************************************************************************
*  Routine to handle reading one line from stdin into string Str, with max.  *
* length Length in the Input Window.					     *
*  Note Str may be non empty and can be used to fix wrong entry.	     *
*****************************************************************************/
void WndwInputWindowGetStr(char *Str, int Length)
{
#ifdef __MSDOS__
    GGWindowViewPort(InputWindow.MinX, InputWindow.MinY,
		     InputWindow.MaxX, InputWindow.MaxY,
		     (int) INPUT_WINDOW_NAME);

    GGGetGraphicLine(InputWindow.FirstLineX,
		     InputWindow.FirstLineY - InputWindow.DiffYLine *
					      InputWindow.NumOfLines,
		     Str, Length, WHITE);
#else
    printf(IRIT_PROMPT);
    fgets(Str, Length - 1, stdin);
    if (feof(stdin)) MyExit(0);    /* Eof typed on keyboard (usually CtrlD). */
    if (Str[strlen(Str) - 1] < ' ') Str[strlen(Str) - 1] = 0;   /* No CR/LF. */
    puts(Str);
#endif /* __MSDOS__ */
}

#ifdef __MSDOS__

/*****************************************************************************
*  Routine to claim/reclaim control on the status window, so that it would   *
* not be updated. Used by other modules to display data on the status window *
* temporary, such as the INTERACT command - objects transformations.	     *
*****************************************************************************/
void WndwClaimStatus(void)
{
    UpdateStatusWindow = FALSE;
}

void WndwReclaimStatus(void)
{
    UpdateStatusWindow = TRUE;
}

/*****************************************************************************
*  Routine to handle the status window static data - clear it and print the  *
* static data. Note it calls in the end to print dynamic data also...	     *
*****************************************************************************/
void WndwStatusWindowDraw(void)
{
    int i;
    struct textsettingstype oldtext;

    if (!UpdateStatusWindow) return;

    gettextsettings(&oldtext);
    settextjustify(CENTER_TEXT, CENTER_TEXT);	   /* Draw strings centered. */

    /* Now update the screen content itself: */
    WndwClearWindow((GenerWindowStruct *) &StatusWindow);

    GGMySetColor(StatusWindow.TextColor);

    for (i=0; i<StatusWindow.NumOfLines; i++) {
	GGXYPutStr((StatusWindow.MinX + StatusWindow.MaxX) / 2.0,
		   StatusWindow.FirstLineY - StatusWindow.DiffYLine * i,
		   StatusWindow.Lines[i]);
    }

    WndwStatusWindowUpdate();

    settextjustify(oldtext.horiz, oldtext.vert);
}

/*****************************************************************************
*  Routine to handle the status window dynamic update - only reprint new.    *
*****************************************************************************/
void WndwStatusWindowUpdate(void)
{
    static char *AdvanceChar = "-\\|/";
    static int AdvanceCount = 0;
    double x, y;
    struct textsettingstype oldtext;

    if (!UpdateStatusWindow) return;

    gettextsettings(&oldtext);
    settextjustify(CENTER_TEXT, CENTER_TEXT);	   /* Draw strings centered. */

    /* Preper the new data to display: */
    AdvanceCount = (AdvanceCount + 1) & 0x03;
    sprintf(StatusWindow.Lines[7], "%c %3dk %c",
	AdvanceChar[AdvanceCount],
	(int) (coreleft()/1024L),
	AdvanceChar[AdvanceCount]);

    GGMySetColor(StatusWindow.TextColor);

    x = (StatusWindow.MinX + StatusWindow.MaxX) / 2.0;
    y = StatusWindow.FirstLineY - StatusWindow.DiffYLine * 7;

    /* Make sure the area is cleared before reprint: */
    GGClearWindow(x - CORE_SIZE_X, y - CORE_SIZE_Y,
		  x + CORE_SIZE_X, y + CORE_SIZE_Y, STATUS_WINDOW_NAME);

    GGXYPutStr(x, y, StatusWindow.Lines[7]);

    settextjustify(oldtext.horiz, oldtext.vert);
}

#endif /* __MSDOS__ */

/*****************************************************************************
*  Routine to handle the view on the View Window. This routine displays      *
* object(s) and prints its/their name also.				     *
* If ClearWindow is not zero then the window is cleared first.		     *
*****************************************************************************/
void WndwViewGeomObject(ObjectStruct *PObjList, RealType *ClearWindow)
{
    int i, ListNum = 0;
    static int NumOfObjects = 0;/* Hold number of objects currently visible. */
    static char Names[OBJ_NAME_LEN][VIEW_WNDW_MAX_OBJ_NAMES];
    char Line[LINE_LEN];
    struct ObjectStruct *PObj;

    if (!IS_OLST_OBJ(PObjList))
	FatalError("WndwViewGeomObject: Not object list object!\n");

    if (!APX_EQ(*ClearWindow, 0.0)) {
#ifdef __MSDOS__
	WndwClearWindow((GenerWindowStruct *) &ViewWindow);  /* & view port. */
#else
        GGClearAllScreen();
#endif /* __MSDOS__ */
	NumOfObjects = 0;
    }

    while ((PObj = PObjList -> U.PObjList[ListNum]) != NULL &&
	   ListNum++ < MAX_OBJ_LIST) {
	if (!IS_GEOM_OBJ(PObj)) {
	    sprintf(Line, "Cannt display none geometric object %s, ignored",
		PObj -> Name);
	    WndwInputWindowPutStr(Line, RED);
	    continue;
	}
	if (NumOfObjects < VIEW_WNDW_MAX_OBJ_NAMES) {
	    for (i=0; i<NumOfObjects; i++)
		if (strcmp(Names[i], PObj->Name) == 0) break;
	    if (i >= NumOfObjects) { /* Its a new name - add it to top list. */
		strcpy(Names[NumOfObjects++], PObj->Name);
		GGMySetColor(GET_OBJECT_COLOR(PObj));
#ifdef __MSDOS__
		GGXYPutStr(ViewWindow.MinX +
			       (ViewWindow.MaxX - ViewWindow.MinX) *
			       NumOfObjects / (VIEW_WNDW_MAX_OBJ_NAMES + 1),
			   ViewWindow.MaxY - 0.05, PObj -> Name);
#else
		GGXYPutStr(((RealType) (2 * NumOfObjects)) /
			           (VIEW_WNDW_MAX_OBJ_NAMES + 1.0) - 1.0,
			   0.9, PObj -> Name);

#endif /* _MSDOS__ */
	    }
	}
        ViewGeomObject(PObj);			  /* And finally display it. */
    }
}
