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

#include "general.h"
#include "globals.h"
#include "intui.h"
#include "timer.h"
#include "spl_math.h"
#include "spl_util.h"
#include "spl_gfx.h"
#include "move.h"

/* Get Stringinfo buffer for the Move gadget with id 'G':	*/
#define Get_Gadget_String(G) (((struct StringInfo *) \
				(Move_GroupGadgets[G]->SpecialInfo))->Buffer)

	/* Set string to 'S' in string gadget 'G':	*/
#define Set_Gadget_String(G, S) \
		GT_SetGadgetAttrs(Move_GroupGadgets[G], \
			Windows[W_Move_Group].Window, NULL, \
			GTST_String, S)

static Boolean_T	Move_Group_Active;
static short		Old_X;
static short		Old_Y;
static Vector_T		Orig_Pos;
static Vector_T		Box_Min, Box_Max;

void Set_Move_Group_Position(Vector_T Pos)
/************************************************************************/
/*                                                                      */
/* Set move position gadgets to 'Pos'.					*/
/*                                                                      */
/************************************************************************/
{
    char Buffer[Buffer_Length+1];

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

    sprintf(Buffer, " %.2lf", Pos[0]);
    Set_Gadget_String(GDX_Move_Group_X_Value, Buffer);
    sprintf(Buffer, " %.2lf", Pos[1]);
    Set_Gadget_String(GDX_Move_Group_Y_Value, Buffer);
    sprintf(Buffer, " %.2lf", Pos[2]);
    Set_Gadget_String(GDX_Move_Group_Z_Value, Buffer);

} /* Set_Move_Group_Position */

static
void Move_Timeout()
/************************************************************************/
/*                                                                      */
/* Function called when timer expires: Draw all splines.		*/
/*                                                                      */
/************************************************************************/
{    
    Vector_T	Distance;

    if (Group_Mode) {

        Distance[0] = Current_Pos[0] - Orig_Pos[0];
        Distance[1] = Current_Pos[1] - Orig_Pos[1];
        Distance[2] = Current_Pos[2] - Orig_Pos[2];

        Points_Move(Distance);
    }
    Compute_Splines();
    Clear_All(What_All);
    Draw_All(What_All);

    Redraw_Mask = 0;

    Stop_Timer();

} /* Move_Timeout */

static 
void Move_Redraw(long Mask)
/************************************************************************/
/*                                                                      */
/* Function called to redraw screen while moving points.		*/
/*                                                                      */
/************************************************************************/
{    
    Vector_T B_Min, B_Max;

    if (Select_Point_Id >= 0) {

        Set_Move_Group_Position(Points[Select_Point_Id].Pos);
	KnotInfo_Show_Pos(Points[Select_Point_Id].Pos);

    } /* if */

    if (Group_Mode) {

        B_Min[0] = Box_Min[0] + Current_Pos[0] - Orig_Pos[0];
        B_Min[1] = Box_Min[1] + Current_Pos[1] - Orig_Pos[1];
        B_Min[2] = Box_Min[2] + Current_Pos[2] - Orig_Pos[2];
        B_Max[0] = Box_Max[0] + Current_Pos[0] - Orig_Pos[0];
        B_Max[1] = Box_Max[1] + Current_Pos[1] - Orig_Pos[1];
        B_Max[2] = Box_Max[2] + Current_Pos[2] - Orig_Pos[2];

        Clear_Plane(3, What_All);
        Draw_Box(B_Min, B_Max, NULL, DM_Plane, What_All);

        Start_Timer(Delay_Draw_Seconds, Delay_Draw_Micros);

    } else {
	
	Point_Move(Select_Point_Id, Current_Pos);

	Compute_Marked_Segments();
	Clear_Plane(3, What_All);
	Draw_Marked_Segments(DM_Plane, What_All);

    } /* if .. else .. */

    Redraw_Mask = 0;

} /* Move_Redraw */

static
void Move_Select_Up(short X, short Y)
/************************************************************************/
/*                                                                      */
/* X, Y are the actual coordinates in the active window.		*/
/*                                                                      */
/************************************************************************/
{
    if (Group_Mode) {

	/* If the timer hasn't expired, then call the timeout handler to*/
	/* compute and draw the splines.			 	*/

        if (!Check_Timer()) Move_Timeout();

    } else {

	if (Grid_Snap_Active) {

	    Current_Pos[0] = Points[Select_Point_Id].Pos[0];
	    Current_Pos[1] = Points[Select_Point_Id].Pos[1];
	    Current_Pos[2] = Points[Select_Point_Id].Pos[2];

	    Snap_To_Grid(Current_Pos);


	} /* if */
	Point_Move(Select_Point_Id, Current_Pos);

	Compute_Marked_Segments();

	Clear_Plane(3, What_All);

	Draw_Marked_Segments(DM_Normal, What_All);
	Draw_Knots(DM_Normal, What_All);

	Set_RMBTrap(FALSE);

    } /* if .. else .. */

    Set_Mode_Normal();

} /* Move_Select_Up */

static 
void Move_Select_Down(short X, short Y)
/************************************************************************/
/*                                                                      */
/* X, Y are the actual coordinates in the active window.		*/
/*                                                                      */
/************************************************************************/
{
    Select_View_Id = Screen_To_World(X, Y, Current_Pos);
    if (Select_View_Id < 0) return;

    if (Get_Select_Bounding_Box(Box_Min, Box_Max)) {
	Display_Message("No points selected");
	return;
    }
    Draw_Box(Box_Min, Box_Max, NULL, DM_Plane, What_All);


    Orig_Pos[0] = Current_Pos[0];
    Orig_Pos[1] = Current_Pos[1];
    Orig_Pos[2] = Current_Pos[2];

    Old_X = X;
    Old_Y = Y;

    Move_Group_Active = TRUE;

} /* Move_Select_Down */

static 
void  Move_Move(short X, short Y)
/************************************************************************/
/*                                                                      */
/* Function called when mouse is moved to move points.			*/
/* X, Y are the actual coordinates in the active window.		*/
/*                                                                      */
/************************************************************************/
{
    short View_Id;

    if (Group_Mode && !Move_Group_Active) return;

    View_Id = Screen_To_World(X, Y, Current_Pos);

    if (View_Id != Select_View_Id) return;

    if (Old_X == X && Old_Y == Y) return;

    Old_X = X;
    Old_Y = Y;

    if (Redraw_Always) Move_Redraw(What_All);
    else               Redraw_Mask = What_All;

} /* Move_Move */

static
Boolean_T Move_Handle_Event(struct IntuiMessage *Msg)
/************************************************************************/
/*                                                                      */
/* Event handler routine for the 'MOVE GROUP' mode.			*/
/* Events handled:							*/
/*	Select down: Start move.					*/
/*	Select up: Stop move.						*/
/*	Mouse move: Change position.					*/
/*                                                                      */
/************************************************************************/
{

    switch (Msg->Class) {


    case IDCMP_MOUSEBUTTONS:
		/* Msg->Code contain id of button pressed 		*/
		/* Msg->MouseX and Msg->MouseY contain mouse position 			*/

	switch (Msg->Code) {

	case SELECTDOWN:
	    Move_Select_Down(Msg->MouseX, Msg->MouseY);
	    return(TRUE);

	case SELECTUP:
	    Move_Select_Up(Msg->MouseX, Msg->MouseY);
	    return(TRUE);

	case MENUDOWN:
	    if (!Group_Mode) Command_Connect(); /* May return to normal mode */
	} /* switch (Msg->Code) */
	break;

    case IDCMP_MOUSEMOVE:
	Move_Move(Msg->MouseX, Msg->MouseY);
	return(TRUE);

    case IDCMP_VANILLAKEY:
	switch(Msg->Code) {

	case 'C':					/* C	*/
	    if (!Group_Mode) Command_Combine();
	    return(TRUE);

	} /* switch (Msg->Code) */
	break;

    } /* switch (Msg->Class) */

    return(FALSE);

} /* Move_Handle_Event */

void Set_Mode_Move(short X, short Y)
/************************************************************************/
/*                                                                      */
/* Start moving the currently selected point, or if GROUP MODE, then	*/
/* move all selected points.						*/
/* X, Y is the current mouse position.					*/
/*                                                                      */
/************************************************************************/
{
    short	Point_Id;
    short	Tmp1, Tmp2;
    Vector_T	Current_Pos;

    if (Select_Point_Id < 0 && !Group_Mode) return;

    if (Select_Spline != NULL)
	Set_Move_Group_Position(Points[Select_Point_Id].Pos);

    KnotInfo_Show_Info(Select_Spline, Select_Knot);
    Set_Window_Title(NULL);
    Display_Status(NULL);

    if (Group_Mode) {

        Move_Group_Active	= FALSE;
        State.Handle_Event	= Move_Handle_Event;
        State.Timeout		= Move_Timeout;
        State.Redraw		= Move_Redraw;

        if (MQ_Size_Move_G > 0) {
	    SetMouseQueue(Windows[Id_Active_Window].Window, MQ_Size_Move_G); 
	    Redraw_Always = TRUE;
        } else Redraw_Always = FALSE;

        Points_Save();

        sprintf(Error_Msg, "Move group");
        Display_Status(Error_Msg);

	Move_Select_Down(X, Y);

    } else {


	State.Handle_Event	= Move_Handle_Event;
	State.Redraw		= Move_Redraw;

	Mark_Connected_Segments(Select_Point_Id);
	
	Draw_Marked_Segments(DM_Erase, What_All);
	Draw_Marked_Segments(DM_Plane, What_All);
	
	Old_X = X; Old_Y = Y;

        if (MQ_Size_Move > 0) {
	    SetMouseQueue(Windows[Id_Active_Window].Window, MQ_Size_Move); 
	    Redraw_Always = TRUE;
        } else Redraw_Always = FALSE;
	
		/* Disable menu and receive RMBTraps */
	Set_RMBTrap(TRUE);

        sprintf(Error_Msg, "Move");
        Display_Status(Error_Msg);

    } /* if .. else .. */

} /* Set_Mode_Move */

void Handle_Move_Group_Move()
/************************************************************************/
/*                                                                      */
/* Change to MOVE GROUP mode.						*/
/*                                                                      */
/************************************************************************/
{
    if (Select_Point_Id < 0) return;

    Move_Group_Active		= FALSE;
    State.Handle_Event		= Move_Handle_Event;
    State.Timeout		= Move_Timeout;
    State.Redraw		= Move_Redraw;

    sprintf(Error_Msg, "Move group");
    Display_Status(Error_Msg);

    Points_Save();

} /* Handle_Move_Group_Move */

void Handle_Move_Group_X_Value()
/************************************************************************/
/*                                                                      */
/* Handle a Move_Group_X_Value gadget event.				*/
/*                                                                      */
/************************************************************************/
{
    Vector_T Distance;

    if (Windows[W_Move_Group].Window == NULL) return;
    if (Select_Point_Id < 0) return;

    Points_Save();


    Distance[0] = atof(Get_Gadget_String(GDX_Move_Group_X_Value)) -
		  Points[Select_Point_Id].Pos[0];
    Distance[1] = 0.0;
    Distance[2] = 0.0;

    Points_Move(Distance);

    Move_Timeout(); /* Compute and draw splines */

} /* Handle_Move_Group_X_Value */

void Handle_Move_Group_Y_Value()
/************************************************************************/
/*                                                                      */
/* Handle a Move_Group_Y_Value gadget event.				*/
/*                                                                      */
/************************************************************************/
{
    Vector_T	Distance;

    if (Windows[W_Move_Group].Window == NULL) return;
    if (Select_Point_Id < 0) return;

    Points_Save();

    Distance[0] = 0.0;
    Distance[1] = atof(Get_Gadget_String(GDX_Move_Group_Y_Value)) -
		  Points[Select_Point_Id].Pos[1];
    Distance[2] = 0.0;

    Points_Move(Distance);

    Move_Timeout(); /* Compute and draw splines */

} /* Handle_Move_Group_Y_Value */

void Handle_Move_Group_Z_Value()
/************************************************************************/
/*                                                                      */
/* Handle a Move_Group_Z_Value gadget event.				*/
/*                                                                      */
/************************************************************************/
{
    Vector_T Distance;

    if (Windows[W_Move_Group].Window == NULL) return;
    if (Select_Point_Id < 0) return;

    Points_Save();

    Distance[0] = 0.0;
    Distance[1] = 0.0;
    Distance[2] = atof(Get_Gadget_String(GDX_Move_Group_Z_Value)) -
		  Points[Select_Point_Id].Pos[2];

    Points_Move(Distance);

    Move_Timeout(); /* Compute and draw splines */

} /* Handle_Move_Group_Z_Value */
