/* :ts=8 */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#include "general.h"
#include "globals.h"
#include "intui.h"
#include "spl_gfx.h"
#include "spl_util.h"
#include "timer.h"
#include "file.h"
#include "windows.h"
#include "commands.h"
#include "config.h"
#include "knotinfo.h"
#include "rotate_p.h"
#include "pan.h"
#include "center.h"
#include "group.h"
#include "scale_g.h"
#include "rotate_g.h"
#include "move.h"
#include "origin.h"
#include "render.h"

Window_Info_T	Windows[Max_Nbr_Windows];

#ifdef NTSC

	/* Positions of the four small views on the screen: */
View_T	Views_Small[4] = {
	341, 108, 633, 196, 0, 0,	/* 0 = V_X : Y - Z */
	 25, 108, 317, 196, 0, 0,	/* 1 = V_Y : X - Z */
	 25,  12, 317, 104, 0, 0,	/* 2 = V_Z : X - Y */
	341,  12, 633, 104, 0, 0	/* 3 = V_P : Persp */
};
	/* Positions of the four big views on the screen:   */
View_T	Views_Big[4] = {
	21,  12, 633, 196, 0, 0,	/* 0 = V_X : Y - Z */
	21,  12, 633, 196, 0, 0,	/* 1 = V_Y : X - Z */
	21,  12, 633, 196, 0, 0,	/* 2 = V_Z : X - Y */
	21,  12, 633, 196, 0, 0		/* 3 = V_P : Persp */
};

#else

	/* Positions of the four small views on the screen: */
View_T	Views_Small[4] = {
	341, 131, 633, 247, 0, 0,	/* 0 = V_X : Y - Z */
	 25, 131, 317, 247, 0, 0,	/* 1 = V_Y : X - Z */
	 25,  12, 317, 128, 0, 0,	/* 2 = V_Z : X - Y */
	341,  12, 633, 128, 0, 0	/* 3 = V_P : Persp */
};
	/* Positions of the four big views on the screen:   */
View_T	Views_Big[4] = {
	21,  12, 633, 247, 0, 0,	/* 0 = V_X : Y - Z */
	21,  12, 633, 247, 0, 0,	/* 1 = V_Y : X - Z */
	21,  12, 633, 247, 0, 0,	/* 2 = V_Z : X - Y */
	21,  12, 633, 247, 0, 0		/* 3 = V_P : Persp */
};

#endif
	
	/* The currently used views:	*/
View_T 			*Views 			= Views_Small;
Window_Id_T		Id_Active_Window 	= W_Main;

	/* Size of IDCMP mouse queue. If this is 0, then redraw	will be	*/
	/* done when there's no more messages waiting, otherwise redraw	*/
	/* will be done whenever a mouse move event happens.		*/

unsigned long		MQ_Size_KnotInfo	= 2;
unsigned long		MQ_Size_Rotate_P	= 2;
unsigned long		MQ_Size_Rotate_G	= 2;
unsigned long		MQ_Size_Scale_G		= 2;
unsigned long		MQ_Size_Move_G		= 2;
unsigned long		MQ_Size_Move		= 0;

	/* TRUE when redrawing for each mouse move event */
Boolean_T		Redraw_Always;

struct MsgPort		*User_Port		= NULL;
static USHORT 		*Mouse_Image		= NULL;
static USHORT 		*Busy_Image		= NULL;

struct IntuitionBase	*IntuitionBase;
struct GfxBase		*GfxBase;
struct Library		*GadToolsBase;
struct Library		*AslBase;

#define Mouse_Image_Size	100
#define Busy_Image_Size		200
	
	/* Global text buffer for error messages	*/
char	Error_Msg[Buffer_Length+1];

/* Window title, this MUST be 80 characters long */
char 	Window_Title[] = 
"ICoons                                                                       ";

void Set_Window_Title(char *Title)
/************************************************************************/
/*                                                                      */
/* Set the window title (first 40 chars) in the title bar.		*/
/* The remaining characters are reserved for messages.			*/
/*                                                                      */
/* if 'Title' is NULL, reset the title to 'ICoons'.			*/
/*                                                                      */
/************************************************************************/
{
    int	 i;
    char *Cp;
    char Buffer[Buffer_Length+1];

    if (Title == NULL) {
	if (Select_Spline != NULL) {
            sprintf(Buffer, "ICoons   Id:%d %s %s", 
		Select_Point_Id,
		Group_Mode ? "Group" : "",
		Points_Hidden ? "H" : "");
	} else {
            sprintf(Buffer, "ICoons   Id:NONE %s %s", 
		Group_Mode ? "Group" : "",
		Points_Hidden ? "H" : "");
	}
	Title = Buffer;
    }

    for (i = 0, Cp = Title; i < 40 && *Cp != '\0'; i++,Cp++)
				Window_Title[i] = *Cp;
    while (i < 40) Window_Title[i++] = ' ';

    SetWindowTitles(Windows[Id_Active_Window].Window, 
					Window_Title, (UBYTE *) -1);
} /* Set_Window_Title */

void Display_Status(char *Message)
/************************************************************************/
/*                                                                      */
/* Show the text 'Message' in the last 40 characters of the title bar.	*/
/* if 'Message' is NULL, then remove any message.			*/
/*                                                                      */
/************************************************************************/
{
    int  i;
    char *Cp;

    if (Message == NULL) {

	for (i = 40; i < 80; i++) Window_Title[i] = ' ';

    } else {

        for (Cp = Message, i = 40; i < 80 && *Cp != '\0'; i++)
					Window_Title[i] = *Cp++;
	while (i < 80) Window_Title[i++] = ' ';

    }
    SetWindowTitles(Windows[Id_Active_Window].Window, 
					Window_Title, (UBYTE *) -1);
 
} /* Display_Status */

void Display_Message(char *Message)
/************************************************************************/
/*                                                                      */
/* Show the text 'Message' to the user, and wait until he clicks a	*/
/* 'Continue' button.							*/
/* 'Message' may contain \n do denote new lines.			*/
/*                                                                      */
/************************************************************************/
{
    struct EasyStruct Requester;

    Requester.es_StructSize = sizeof(struct EasyStruct);
    Requester.es_Flags		= 0;
    Requester.es_Title		= "ICoons";
    Requester.es_TextFormat	= Message;
    Requester.es_GadgetFormat	= "Continue";

    (void) EasyRequest(Windows[Id_Active_Window].Window, &Requester, NULL);

} /* Display_Message */

void Display_Error_Message(char *Message)
/************************************************************************/
/*                                                                      */
/* Show the text 'Message' to the user in an ALERT, and wait until he 	*/
/* clicks a button.							*/
/* Only the first 60 characters of the message is shown.		*/
/*                                                                      */
/************************************************************************/
{
    UBYTE Buffer[Buffer_Length+1];
    int	  Length;
    int	  Pos;

    Length = strlen(Message);
    if (Length > 60) Length = 60;

    Pos = 0;
 
    Buffer[Pos++] = 0x00;
    Buffer[Pos++] = 0x30;	/* 16 bit X coordinate in pixels */

    Buffer[Pos++] = 0x14;	/*  8 bit Y coordinate in pixels */

    strncpy(&Buffer[Pos], Message, Length); Pos += Length;

    Buffer[Pos++] = '\0';	/* End of string  */

    Buffer[Pos] = '\0';	/* End of message */
   
    (void) DisplayAlert(RECOVERY_ALERT, Buffer, /* Heigth = */ 42); 

} /* Display_Message */

int Ask_Question(char *Question, char *Choices)
/************************************************************************/
/*                                                                      */
/* Ask the question 'Question' and let him choose between the choices	*/
/* in 'Choices'.							*/
/* 'Question' may contain \n do denote new lines.			*/
/* The choices are separated with vertical bars (ex: "Ok|Cancel").	*/
/*                                                                      */
/* The function will return a number identifying the response:		*/
/* 1 for the leftmost button, 2 for the next button etc. AND		*/
/* 0 for the rightmost button (!!!!)					*/
/*                                                                      */
/* Example: Ask_Question("Quit now?", "Yes|Maybe|No")			*/
/* 	will return 1 for YES 2 for MAYBE and 0 for NO.			*/
/*                                                                      */
/************************************************************************/
{
    struct EasyStruct Requester;
    int   	      Status;

    Requester.es_StructSize = sizeof(struct EasyStruct);
    Requester.es_Flags		= 0;
    Requester.es_Title		= "ICoons";
    Requester.es_TextFormat	= Question;
    Requester.es_GadgetFormat	= Choices;

    Status = EasyRequest(Windows[Id_Active_Window].Window, &Requester, NULL);

    return(Status);

} /* Ask_Question */

static 
void Windows_Init()
/************************************************************************/
/*                                                                      */
/* Initialize the Windows array.					*/
/*                                                                      */
/************************************************************************/
{
    int			i;
    static Boolean_T	Init_Done = FALSE;

    if (Init_Done) return;

    Init_Done = TRUE;

    for (i = 0; i < Max_Nbr_Windows; i++) {
    	Windows[i].Window    = NULL;
    	Windows[i].ViewPort  = NULL;
    	Windows[i].RastPort  = NULL;
    } /* for */

    Views = Views_Small;

    for (i = 0; i < 4; i++) {

	Views_Small[i].X_Center = (Views_Small[i].X_Min + 
				   Views_Small[i].X_Max) / 2;
	Views_Small[i].Y_Center = (Views_Small[i].Y_Min + 
				   Views_Small[i].Y_Max) / 2;

	Views_Big[i].X_Center = (Views_Big[i].X_Min + 
				 Views_Big[i].X_Max) / 2;
	Views_Big[i].Y_Center = (Views_Big[i].Y_Min + 
				 Views_Big[i].Y_Max) / 2;

    } /* for */

} /* Windows_Init */

static 
void Set_Normal_Pointer(Window_Id_T Id)
/************************************************************************/
/*                                                                      */
/* Define a custom pointer for the window defined by Id.		*/
/*                                                                      */
/************************************************************************/
{
    USHORT *M;

    if (Windows[Id].Window == NULL) return;

    if (Mouse_Image == NULL) {

        Mouse_Image = (USHORT *) AllocMem(Mouse_Image_Size, MEMF_CHIP);
	if (Mouse_Image == NULL) {
	    Display_Error_Message("Couldn't allocate memory");
	    CloseStuff();
	    exit(1);
	}

	M = Mouse_Image;

    	*M++ = 0x0000, *M++ = 0x0000;

	/* plane 1,	plane 2	*/
	*M++ = 0x0400; *M++ = 0x0400;	/* Line 1 	*/
	*M++ = 0x0400; *M++ = 0x0400;	/* Line 2 	*/
	*M++ = 0x0400; *M++ = 0x0400;	/* Line 3 	*/
	*M++ = 0x0400; *M++ = 0x0400;	/* Line 4 	*/

      	*M++ = 0x0000; *M++ = 0x0000;	/* Line 5 	*/
	*M++ = 0xf5f0; *M++ = 0xf5f0;	/* Line 6 	*/
	*M++ = 0x0000; *M++ = 0x0000;	/* Line 7 	*/

	*M++ = 0x0400; *M++ = 0x0400;	/* Line 8 	*/
	*M++ = 0x0400; *M++ = 0x0400;	/* Line 9 	*/
	*M++ = 0x0400; *M++ = 0x0400;	/* Line 10 	*/
	*M++ = 0x0400; *M++ = 0x0400;	/* Line 11 	*/

	*M++ = 0x0000; *M++ = 0x0000;
    }

    switch (Id) {

        case W_Main:
        case W_ExpandedView:
	    SetPointer(Windows[Id].Window, Mouse_Image, 11, 11, -5, -5);
	    break;

	default: 			/* Normal intuition pointer */
	    ClearPointer(Windows[Id].Window);
	    break;

    } /* switch */

} /* Set_Normal_Pointer */

static 
void Set_Busy_Pointer(Window_Id_T Id)
/************************************************************************/
/*                                                                      */
/* Define a custom pointer for the window defined by Id.		*/
/*                                                                      */
/************************************************************************/
{
    USHORT *M;

    if (Windows[Id].Window == NULL) return;

    if (Busy_Image == NULL) {

        Busy_Image = (USHORT *) AllocMem(Busy_Image_Size, MEMF_CHIP);
	if (Busy_Image == NULL) {
	    Display_Error_Message("Couldn't allocate memory");
	    CloseStuff();
	    exit(1);
	}

	M = Busy_Image;

    	*M++ = 0x0000, *M++ = 0x0000;

	/* plane 1,	plane 2	*/
	*M++ = 0x0400; *M++ = 0x07c0;	/* Line 1 	*/
	*M++ = 0x0000; *M++ = 0x07c0;	/* Line 2 	*/
	*M++ = 0x0100; *M++ = 0x0380;	/* Line 3 	*/
	*M++ = 0x0000; *M++ = 0x07e0;	/* Line 4 	*/
      	*M++ = 0x07c0; *M++ = 0x1ff8;	/* Line 5 	*/
	*M++ = 0x1ff0; *M++ = 0x3fec;	/* Line 6 	*/
	*M++ = 0x3ff8; *M++ = 0x7fde;	/* Line 7 	*/
	*M++ = 0x3ff8; *M++ = 0x7fbe;	/* Line 8 	*/
	*M++ = 0x7ffc; *M++ = 0xff7f;	/* Line 9 	*/
	*M++ = 0x7efc; *M++ = 0xffff;	/* Line 10 	*/
	*M++ = 0x7ffc; *M++ = 0xffff;	/* Line 11 	*/
	*M++ = 0x3ff8; *M++ = 0x7ffe;	/* Line 12 	*/
	*M++ = 0x3ff8; *M++ = 0x7ffe;	/* Line 13 	*/
	*M++ = 0x1ff0; *M++ = 0x3ffc;	/* Line 14 	*/
	*M++ = 0x07c0; *M++ = 0x1ff8;	/* Line 15 	*/
	*M++ = 0x0000; *M++ = 0x07e0;	/* Line 16 	*/

	*M++ = 0x0000; *M++ = 0x0000;
    }

    SetPointer(Windows[Id].Window, Busy_Image, 16, 16, -6, 0);

} /* Set_Busy_Pointer */

void Set_Pointer(Boolean_T Busy)
/************************************************************************/
/*                                                                      */
/* Set pointer to busy pointer in all windows if 'Busy' is TRUE, other-	*/
/* set default pointer.							*/
/*                                                                      */
/************************************************************************/
{
    if (Busy) {

    	Set_Busy_Pointer(W_Config);
    	Set_Busy_Pointer(W_ExpandedView);
    	Set_Busy_Pointer(W_Move_Group);
    	Set_Busy_Pointer(W_Rotate_Group);
    	Set_Busy_Pointer(W_Scale_Group);
    	Set_Busy_Pointer(W_KnotInfo);
    	Set_Busy_Pointer(W_Main);

    } else {

    	Set_Normal_Pointer(W_Config);
    	Set_Normal_Pointer(W_ExpandedView);
    	Set_Normal_Pointer(W_Move_Group);
    	Set_Normal_Pointer(W_Rotate_Group);
    	Set_Normal_Pointer(W_Scale_Group);
    	Set_Normal_Pointer(W_KnotInfo);
    	Set_Normal_Pointer(W_Main);

    } /* if .. else .. */

} /* Set_Pointer */

void Set_RMBTrap(Boolean_T On)
/************************************************************************/
/*                                                                      */
/* Activate or deactivate RMBTrap.					*/
/*                                                                      */
/* If RMBTrap is active, then the menu is disabled, and the application	*/
/* can receive MENUDOWN and MENUUP events.				*/
/*                                                                      */
/************************************************************************/
{
    int i;

    Forbid(); /* Disable multitasking while doing this (RKMLib p. 110) */

    for (i = 0; i < Max_Nbr_Windows; i++) {

	if (Windows[i].Window != NULL) {

	    if (On) Windows[i].Window->Flags |= WFLG_RMBTRAP;
	    else    Windows[i].Window->Flags &= ~WFLG_RMBTRAP;
		
        } /* if */
    } /* for */

    Permit();


} /* Set_RMBTrap */

void StripIntuiMessages(struct MsgPort *mp, struct Window *win)
/************************************************************************/
/*                                                                      */
/* Function to remove and reply all IntuiMessages on a port that have	*/
/* been sent to a particular window.					*/
/* Taken from RKM:Libraries p. 255					*/
/*                                                                      */
/************************************************************************/
{
    struct IntuiMessage *msg;
    struct Node		*succ;

    msg = (struct IntuiMessage *)mp->mp_MsgList.lh_Head;

    while (succ = msg->ExecMessage.mn_Node.ln_Succ) {

	if (msg->IDCMPWindow == win) {	

		/* Intuition is about to free this message
		** Make sure that we have politely sent it back.
        	*/

	    Remove( (struct Node *) msg);

	    ReplyMsg( (struct Message *) msg);

	} /* if */

	msg = (struct IntuiMessage *) succ;
	
    } /* while */
  
} /* StripIntuiMessages */

void RemovePort(struct Window *win, Boolean_T Do_Free)
/************************************************************************/
/*                                                                      */
/* Remove the userport from the window 'win'.				*/
/* If Do_Free is TRUE the port will be deleted.				*/
/*                                                                      */
/************************************************************************/
{
    /* We forbid here to keep out of race conditions with Intuition */

    Forbid();

    /* Send back any messages for this window that have not yet been
    ** processed.
    */

    StripIntuiMessages(win->UserPort, win);

	/* Clear UserPort so Intuition will not free it */

    if (!Do_Free) win->UserPort = NULL;

    ModifyIDCMP(win, 0L);

    /* Turn multitasking back on */
    Permit();

} /* RemovePort */

void Window_Close(Window_Id_T Id)
/************************************************************************/
/*                                                                      */
/*                                                                      */
/************************************************************************/
{

    if (Windows[Id].Window == NULL) return;

    switch (Id) {

    case W_Main:
	CloseMainWindow();
	break;

    case W_KnotInfo:
	RemovePort(Windows[Id].Window, FALSE);
	ClearMenuStrip(Windows[Id].Window);
	CloseKnotInfoWindow();
	break;

    case W_Scale_Group:
	RemovePort(Windows[Id].Window, FALSE);
	ClearMenuStrip(Windows[Id].Window);
	CloseScale_GroupWindow();
	break;

    case W_Rotate_Group:
	RemovePort(Windows[Id].Window, FALSE);
	ClearMenuStrip(Windows[Id].Window);
	CloseRotate_GroupWindow();
	break;

    case W_Move_Group:
	RemovePort(Windows[Id].Window, FALSE);
	ClearMenuStrip(Windows[Id].Window);
	CloseMove_GroupWindow();
	break;

    case W_ExpandedView:
	RemovePort(Windows[Id].Window, FALSE);
	ClearMenuStrip(Windows[Id].Window);
	CloseExpandedViewWindow();
	Id_Active_Window = W_Main;
	break;

    case W_Config:
	RemovePort(Windows[Id].Window, FALSE);
	ClearMenuStrip(Windows[Id].Window);
	CloseConfigWindow();
	break;

    default: 
        sprintf(Error_Msg, "Unknown window id %d",  Id);
	Display_Error_Message(Error_Msg);
        CloseStuff();

    } /* switch */	

    Windows[ Id].Window = NULL;

} /* Window_Close */

void Window_Open(Window_Id_T Id)
/************************************************************************/
/*                                                                      */
/* Open a window. The main window should always be opened first, as 	*/
/* its UserPort is used for other windows.				*/
/*                                                                      */
/************************************************************************/
{
    long	Status;
    ULONG 	IDCMP_Flags;

    if (Windows[Id].Window != NULL) return;

    if (Id == W_Main) {

	Status = OpenMainWindow();
        if (Status != NULL) {
	    sprintf(Error_Msg, "Couldn't open window %d, Status = %d", 
						 Id, Status);
	    Display_Error_Message(Error_Msg);
            CloseStuff();
            exit(1);     
    	} 
    	Windows[Id].Window = MainWnd;
        User_Port = Windows[Id].Window->UserPort;

    } else {

        if (Windows[W_Main].Window == NULL) {
	    Display_Error_Message("Main window should be opened first.");
            CloseStuff();
            exit(1);     
        } /* if */

        switch (Id) {

        case W_KnotInfo:
	    Status = OpenKnotInfoWindow();
    	    Windows[Id].Window = KnotInfoWnd;
	    break;

        case W_Scale_Group:
	    Status = OpenScale_GroupWindow();
    	    Windows[Id].Window = Scale_GroupWnd;
	    break;

        case W_Rotate_Group:
	    Status = OpenRotate_GroupWindow();
    	    Windows[Id].Window = Rotate_GroupWnd;
	    break;

        case W_Move_Group:
	    Status = OpenMove_GroupWindow();
    	    Windows[Id].Window = Move_GroupWnd;
	    break;

        case W_ExpandedView:
	    Status = OpenExpandedViewWindow();
    	    Windows[Id].Window = ExpandedViewWnd;
	    break;

        case W_Config:
	    Status = OpenConfigWindow();
    	    Windows[Id].Window = ConfigWnd;
	    break;

    	default: 
            sprintf(Error_Msg, "Unknown window id %d",  Id);
	    Display_Error_Message(Error_Msg);
            CloseStuff();
	    exit(1);

    	} /* switch */	

        if (Status != NULL) {
	    sprintf(Error_Msg, "Couldn't open window %d, Status = %d", 
						 Id, Status);
	    Display_Error_Message(Error_Msg);
            CloseStuff();
            exit(1);     

        } /* if */


	IDCMP_Flags = Windows[Id].Window->IDCMPFlags;

		/* Remove and delete the old port:	*/
	RemovePort(Windows[Id].Window, TRUE);

		/* User same port as main window:	*/
	Windows[Id].Window->UserPort = User_Port;
        if (ModifyIDCMP(Windows[Id].Window, IDCMP_Flags) == NULL) {

	    Display_Error_Message("Couldn't modify IDCMP port\n");
	    CloseStuff();
	    exit(1);

	} /* if */

		/* Use same menu as on the main window for all windows */
	SetMenuStrip(Windows[Id].Window, MainMenus);

    } /* if .. else .. */

    Windows[Id].RastPort = Windows[Id].Window->RPort;
    Windows[Id].ViewPort = ViewPortAddress(Windows[Id].Window);

    Set_Normal_Pointer(Id);

} /* Window_Open */



void CloseStuff()
/************************************************************************/
/*                                                                      */
/* Close libraries, windows, screens etc.				*/
/*                                                                      */
/* Free allocated memory.						*/
/*                                                                      */
/************************************************************************/
{
    Window_Close(W_Config);
    Window_Close(W_ExpandedView);
    Window_Close(W_Move_Group);
    Window_Close(W_Rotate_Group);
    Window_Close(W_Scale_Group);
    Window_Close(W_KnotInfo);
    Window_Close(W_Main);	/* Should always be closed last! */

    if (Mouse_Image != NULL) FreeMem(Mouse_Image, Mouse_Image_Size);
    if (Busy_Image != NULL)  FreeMem(Busy_Image, Busy_Image_Size);
    
    CloseDownScreen();
    if (GfxBase)        CloseLibrary((struct Library *) GfxBase);
    if (AslBase)   CloseLibrary((struct Library *) AslBase);
    if (GadToolsBase)   CloseLibrary((struct Library *) GadToolsBase);
    if (IntuitionBase)  CloseLibrary((struct Library *) IntuitionBase);

    Splines_Init();
    Render_Cleanup();

} /* CloseStuff */

void OpenStuff()
/************************************************************************/
/*                                                                      */
/*                                                                      */
/************************************************************************/
{
    long	Status;

    Windows_Init();

    IntuitionBase = 
  		(struct IntuitionBase*)OpenLibrary("intuition.library",37L);
    if (IntuitionBase == NULL) {
	Display_Error_Message("Couldn't open intuition.library");
        CloseStuff();
        exit(1);     
    } /* if */

    GadToolsBase = (struct Library *)OpenLibrary("gadtools.library", 37L);
    if (GadToolsBase == NULL) {
	Display_Error_Message("Couldn't open gadtools.library");
        CloseStuff();
        exit(1);     
    } /* if */

    AslBase = (struct Library *)OpenLibrary("asl.library", 37L);
    if (AslBase == NULL) {
	Display_Error_Message("Couldn't open asl.library");
        CloseStuff();
        exit(1);     
    } /* if */

    GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0L);
    if (GfxBase == NULL) {
	Display_Error_Message("Couldn't open graphics.library");
        CloseStuff();
        exit(1);     
    } /* if */

    Status = SetupScreen();
    if (Status != NULL) {
    	sprintf(Error_Msg, "Couldn't open screen, Status = %d", Status);
	Display_Error_Message(Error_Msg);
	CloseStuff();
	exit(1);
    }
    ShowTitle(Scr, 0L);		/* Put screen title behind backdrop */

    Window_Open(W_Main);	/* Should always be opened first ! */

    Id_Active_Window = W_Main;

} /* OpenStuff */


void Finish()
/************************************************************************/
/*                                                                      */
/*                                                                      */
/************************************************************************/
{
    struct IntuiMessage *Msg;

    while ((Msg = GT_GetIMsg(User_Port)) != NULL) {

      GT_ReplyIMsg(Msg);

    } /* while */

  CloseStuff();

} /* Finish */

void Clear_All(long What)
/************************************************************************/
/*                                                                      */
/* Clear the drawing area.						*/
/*                                                                      */
/************************************************************************/
{
    SetAPen(Windows[Id_Active_Window].RastPort, Pen_Background);
    SetDrMd(Windows[Id_Active_Window].RastPort, JAM2);
    Windows[Id_Active_Window].RastPort->Mask    = 0xff;

    What &= What_Mask;
    if (What & What_X) { 
  	RectFill(Windows[Id_Active_Window].RastPort, 
		Views[V_X].X_Min, Views[V_X].Y_Min,
	    	Views[V_X].X_Max, Views[V_X].Y_Max);
    }

    if (What & What_Y) { 
  	RectFill(Windows[Id_Active_Window].RastPort, 
		Views[V_Y].X_Min, Views[V_Y].Y_Min,
	    	Views[V_Y].X_Max, Views[V_Y].Y_Max);
    }

    if (What & What_Z) { 
   	RectFill(Windows[Id_Active_Window].RastPort, 
		Views[V_Z].X_Min, Views[V_Z].Y_Min,
	    	Views[V_Z].X_Max, Views[V_Z].Y_Max);
    }

    if (What & What_P) { 
   	RectFill(Windows[Id_Active_Window].RastPort, 
		Views[V_P].X_Min, Views[V_P].Y_Min,
	    	Views[V_P].X_Max, Views[V_P].Y_Max);
    }

} /* Clear_All */

void Clear_Plane(int Plane, long What)
{
    SetAPen(Windows[Id_Active_Window].RastPort, Pen_Background);
    Windows[Id_Active_Window].RastPort->Mask    = 1 << Plane;
    SetDrMd(Windows[Id_Active_Window].RastPort, JAM2);

    What &= What_Mask;

    if (What & What_X) { 
  	RectFill(Windows[Id_Active_Window].RastPort, 
		Views[V_X].X_Min, Views[V_X].Y_Min,
	    	Views[V_X].X_Max, Views[V_X].Y_Max);
    }

    if (What & What_Y) { 
  	RectFill(Windows[Id_Active_Window].RastPort, 
		Views[V_Y].X_Min, Views[V_Y].Y_Min,
	    	Views[V_Y].X_Max, Views[V_Y].Y_Max);
    }

    if (What & What_Z) { 
   	RectFill(Windows[Id_Active_Window].RastPort, 
		Views[V_Z].X_Min, Views[V_Z].Y_Min,
	    	Views[V_Z].X_Max, Views[V_Z].Y_Max);
    }

    if (What & What_P) { 
   	RectFill(Windows[Id_Active_Window].RastPort, 
		Views[V_P].X_Min, Views[V_P].Y_Min,
	    	Views[V_P].X_Max, Views[V_P].Y_Max);
    }

} /* Clear_Plane */


void Handle_Gadget_Event(struct IntuiMessage *Msg)
/************************************************************************/
/*                                                                      */
/* Dispatcher for gadget events.					*/
/*                                                                      */
/************************************************************************/
{
    struct Gadget *Gad = (struct Gadget *) Msg->IAddress;

    if (Msg->Code == 0x09) {			/* TAB */	
	return;
    }

    else if (Msg->Code == 0x5f) {		/* HELP */
	return;
    }

    switch (Gad->GadgetID) {

    case GD_G_X_Expand:
	Command_X_Expand();
	break;

    case GD_G_Y_Expand:
	Command_Y_Expand();
	break;

    case GD_G_Z_Expand:
	Command_Z_Expand();
	break;

    case GD_G_Persp_Expand:
	Command_Persp_Expand();
	break;

    case GD_G_Persp_Rotate:
    case GD_G_Persp_Rotate_E:
  	Set_Mode_Rotate_P(); 
	break;

    case GD_G_Unexpand:
	Command_Unexpand();
        Window_Close(W_ExpandedView);
	break;

	/* KnotInfo window:	*/
    case GD_G_Tension:
	Handle_G_Tension();
	break;

    case GD_G_Tension_Value:
	Handle_G_Tension_Value();
	break;

    case GD_G_Bias:
	Handle_G_Bias();
	break;

    case GD_G_Bias_Value:
	Handle_G_Bias_Value();
	break;

    case GD_G_Continuity:
	Handle_G_Continuity();
	break;

    case GD_G_Continuity_Value:
	Handle_G_Continuity_Value();
	break;

	/* Scale_Group window:	*/
    case GD_Scale_Group_Factor:
	Handle_Scale_Group_Factor();
	break;

    case GD_Scale_Group_Factor_Value:
	Handle_Scale_Group_Factor_Value();
	break;

	/* Rotate_Group window:	*/
    case GD_Rotate_Group_Axis:
	Handle_Rotate_Group_Axis(Msg->Code);
	break;

    case GD_Rotate_Group_Angle:
	Handle_Rotate_Group_Angle();
	break;

    case GD_Rotate_Group_Angle_Value:
	Handle_Rotate_Group_Angle_Value();
	break;

	/* Move_Group window:	*/
    case GD_Move_Group_X_Value:
	Handle_Move_Group_X_Value();
	break;

    case GD_Move_Group_Y_Value:
	Handle_Move_Group_Y_Value();
	break;

    case GD_Move_Group_Z_Value:
	Handle_Move_Group_Z_Value();
	break;

    case GD_Move_Group_Move:
	Handle_Move_Group_Move();
	break;


    } /* switch */

} /* Handle_Gadget_Event */


void Handle_Menu_Event(struct IntuiMessage *Msg)
/************************************************************************/
/*                                                                      */
/* Dispatcher for menu events.						*/
/*                                                                      */
/************************************************************************/
{
    UWORD MenuNumber;
    UWORD ItemNumber;
    UWORD SubNumber;

    if (Msg->Code == MENUNULL) return;

    MenuNumber = MENUNUM(Msg->Code);
    ItemNumber = ITEMNUM(Msg->Code);
    SubNumber  = SUBNUM(Msg->Code);

    switch (MenuNumber) {

    case 0:					/* PROJECT	*/
	switch (ItemNumber) {

	case 0:					/* About	*/
  	    Command_About();
	    break;

	case 1:					/* New		*/
  	    Command_New(); 
	    break;

	case 2:					/* Load		*/
	    switch (SubNumber) {
	    case 0:				/* Icoons obj.	*/
  	        Command_Load_Object(); 
	        break;
	    case 1:				/* JMan  obj.	*/
  	        Command_Load_JMan_Object(); 
	        break;
	    } /* switch (SubNumber) */
	    break;

	case 3:					/* Save		*/
  	    Command_Save_Object(); 
	    break;

	case 4:					/* Generate	*/
	    switch (SubNumber) { 
	    case 0:				/* TTDDD	*/
  	        Command_Generate_TTDDD_File(); 
		break;

	    } /* switch */
	    break;

	case 5:					/* Configuration*/
	    switch (SubNumber) {

	    case 0:				/* Load		*/
		Command_Load_Config();
		break;

	    case 1:				/* Save		*/
		Command_Save_Config();
		break;

	    case 2:				/* Edit		*/
		Edit_Configuration();
		break;

	    } /* switch (SubNumber) */
	    break;

	case 6:					/* Quit		*/
  	    Command_Quit(); 
	    break;

	} /* switch (ItemNumber) */
	break;


    case 1:					/* DISPLAY 	*/
	switch (ItemNumber) {

	case 0:					/* Zoom in	*/
  	    Command_ZoomIn(); 
	    break;

	case 1:					/* Zoom out	*/
  	    Command_ZoomOut(); 
	    break;

	case 2:					/* Center	*/
  	    Set_Mode_Center(); 
	    break;

	case 3:					/* Rotate P.	*/
  	    Set_Mode_Rotate_P(); 
	    break;

	case 4:					/* Pan		*/
  	    Set_Mode_Pan(); 
	    break;

	case 5:					/* Grid 	*/
  	    Grid_Active = !Grid_Active;
            Redraw_Mask |= What_All;
	    break;

	case 6:					/* Grid snap	*/
  	    Grid_Snap_Active = !Grid_Snap_Active;
	    break;

    	case 7:					/* Resolution	*/
	    switch (SubNumber) {
	    case 0:				/* Up		*/
		Command_Resolution_Up();
		break;

	    case 1:				/* Down		*/
		Command_Resolution_Down();
		break;

	    } /* switch */
	    break;

	case 8:					/* Redraw	*/
            Redraw_Mask |= What_All;
	    break;

	} /* switch (ItemNumber) */
	break;

    case 2:					/* WINDOWS 	*/
	switch (ItemNumber) {

	case 0:					/* Knot  	*/
  	    Command_Show_KnotInfo(); 
	    break;

	case 1:					/* Scale G  	*/
  	    Command_Scale_Group(); 
	    break;

	case 2:					/* Rotate G  	*/
  	    Command_Rotate_Group(); 
	    break;

	case 3:					/* Move G 	*/
  	    Command_Move_Group(); 
	    break;

	} /* switch (ItemNumber) */
	break;

    case 3:					/* RENDER	*/
	switch (ItemNumber) {

	case 0:					/* Mode		*/
	    switch (SubNumber) {
	
	    case 0:				/* Line		*/
		Rendering_Mode = RM_Line;
		break;

	    case 1:				/* Flat		*/
		Rendering_Mode = RM_Flat;
		break;

	    case 2:				/* Gouraud	*/
		Rendering_Mode = RM_Gouraud;
		break;

	    case 3:				/* Phong	*/
		Rendering_Mode = RM_Phong;
		break;

	    } /* switch */
	    break;

	case 1:					/* Render	*/
	    Render_Tesselate_Object();	
	    break;

	case 2:					/* Re-render	*/
	    Render_Object();	
	    break;

	} /* switch (ItemNumber) */
	break;

    case 4:					/* Select	*/
	switch (ItemNumber) {
	case 0:					/* Toggle Group	*/
	    Set_Mode_Group();
	    break;

	case 1:					/* Toggle Spline*/
	    Command_Toggle_Select_Spline();
	    break;

	case 2:					/* Toggle All	*/
	    Command_Toggle_Select_All();
	    break;

	case 3:					/* Knot		*/
	    if (Select_Point_Id >= 0) {
		Group_Mode = TRUE;
		Set_Window_Title(NULL);
	    } /* if */
	    break;

	case 4:					/* Next		*/
	    Command_Select_Next();
	    break;

	case 5:					/* Deselect all	*/
	    Command_Deselect_All();
	    break;

	} /* switch (ItemNumber) */
	break;

    case 5:					/* Commands	*/
	switch (ItemNumber) {

	case 0:					/* Add		*/
	    Command_Add();
	    break;

	case 1:					/* Combine	*/
	    Command_Combine();
	    break;

	case 2:					/* Cut		*/
	    Command_Cut();
	    break;

	case 3:					/* Delete	*/
	    Command_Delete();
	    break;

	case 4:					/* Disconnect	*/
	    Command_Disconnect();
	    break;

	case 5:					/* Hide		*/
	    Command_Hide();
	    break;

	case 6:					/* Move origin	*/
	    Set_Mode_Origin();
	    break;

	} /* switch (ItemNumber) */
	break;

    } /* switch (MenuNum) */


} /* Handle_Menu_Event */

void Check_Event()
/************************************************************************/
/*                                                                      */
/* Check for an event and handle it.					*/
/*                                                                      */
/************************************************************************/
{
    struct 		IntuiMessage *Msg_Ptr;
    struct 		IntuiMessage Msg;
    struct 		Gadget *Gad;
    Boolean_T		Event_Used;

    WaitPort(User_Port);

    while ((Msg_Ptr = GT_GetIMsg(User_Port)) != NULL) {

	Msg = *Msg_Ptr;
	
        GT_ReplyIMsg(Msg_Ptr);

        switch (Msg.Class) {

	case IDCMP_INTUITICKS:
	    if (Handle_IntuiTick_Message(&Msg)) State.Timeout();
	    break;

	case IDCMP_REFRESHWINDOW:
	    GT_BeginRefresh(Msg.IDCMPWindow);
	    if (Msg.IDCMPWindow == Windows[W_Main].Window) MainRender();
	    if (Msg.IDCMPWindow == Windows[W_Config].Window) ConfigRender();
	    GT_EndRefresh(Msg.IDCMPWindow, TRUE);
	    break;

	case IDCMP_CLOSEWINDOW:
	    if (Msg.IDCMPWindow == Windows[W_Main].Window) {

		Command_Quit();

	    } else if (Msg.IDCMPWindow == Windows[W_KnotInfo].Window) {

	        Window_Close(W_KnotInfo);

	    } else if (Msg.IDCMPWindow == Windows[W_Scale_Group].Window) {

	        Window_Close(W_Scale_Group);

	    } else if (Msg.IDCMPWindow == Windows[W_Rotate_Group].Window) {

	        Window_Close(W_Rotate_Group);

	    } else if (Msg.IDCMPWindow == Windows[W_Move_Group].Window) {

	        Window_Close(W_Move_Group);

	    } else if (Msg.IDCMPWindow == Windows[W_ExpandedView].Window) {
		
		Command_Unexpand();
	        Window_Close(W_ExpandedView);

	    } else if (Msg.IDCMPWindow == Windows[W_Config].Window) {
		
	        Window_Close(W_Config);

	    } /* if */
	    break;

	case IDCMP_GADGETUP:
	case IDCMP_GADGETDOWN:
	    Handle_Gadget_Event(&Msg);
	    break;

	case IDCMP_MENUPICK:
	    Handle_Menu_Event(&Msg);
	    break;

	default:
	    Event_Used = State.Handle_Event(&Msg);

            if (Event_Used) break;
	    if (Msg.Class == IDCMP_MOUSEBUTTONS && 
	        Msg.Code == SELECTUP) Set_Mode_Normal();
	    break;

	} /* switch */

    } /* while */

    if (Redraw_Mask) {

        State.Redraw(Redraw_Mask);
        Redraw_Mask = 0;

    } /* if */

} /* Check_Event */

int Wait_For_Key()
/************************************************************************/
/*                                                                      */
/* Wait until the user presses a key, and return it.			*/
/*                                                                      */
/************************************************************************/
{
    struct 		IntuiMessage *Msg_Ptr;
    struct 		IntuiMessage Msg;

    for (;;) {

        WaitPort(User_Port);

        while ((Msg_Ptr = GT_GetIMsg(User_Port)) != NULL) {

	    Msg = *Msg_Ptr;
	
            GT_ReplyIMsg(Msg_Ptr);

            if (Msg.Class == IDCMP_VANILLAKEY) return((int) Msg.Code);

        } /* while */

    }  /* for (;;) */

} /* Wait_For_Key */
