/* Copyright (C) 2000-2003 Damir Zucic */

/*=============================================================================

				motion_notify.c

Purpose:
	Handle MotionNotify events.

Input:
	(1) Pointer to MolComplexS structure, with macromol. complexes.
	(2) Number of macromolecular complexes.
	(3) Pointer to RuntimeS structure, with some runtime data.
	(4) Pointer to ConfigS structure, with configuration data.
	(5) Pointer to GUIS structure, with GUI data.
	(6) Pointer to NearestAtomS structure.
	(7) The number of pixels in the main window free area.
	(8) Pointer to refreshI.
	(9) Pointer to XMotionEvent structure.

Output:
	(1) Some data related to the atom under  the pointer written to
	    output window.
	(2) Sequence neighborhood  of the residue under the pointer may
	    be written to the main window (if requested).
	(3) Return value.

Return value:
	(1) Positive on success.
	(2) Zero if event is ignored.
	(3) Negative on failure.

Notes:
	(1) Indentation is exceptionally 4 spaces.

========includes:============================================================*/

#include <stdio.h>

#include <string.h>
#include <math.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>

#include "defines.h"
#include "typedefs.h"

/*======function prototypes:=================================================*/

int		RamaMouse_ (MolComplexS *, int, ConfigS *, GUIS *,
			    NearestAtomS *, size_t, unsigned int,
			    XMotionEvent *);
int		PlotMouse_ (RuntimeS *, GUIS *,
			    NearestAtomS *, size_t, unsigned int,
			    XMotionEvent *);
int		ComparisonMouse_ (RuntimeS *, GUIS *,
				  NearestAtomS *, size_t,
				  XMotionEvent *);
int		ExtractN_ (VectorS *, AtomS *, size_t, size_t);
int		ExtractC_ (VectorS *, AtomS *, size_t, size_t);

/*======handle MotionNotify events:==========================================*/

int MotionNotify_ (MolComplexS *mol_complexSP, int mol_complexesN,
		   RuntimeS *runtimeSP, ConfigS *configSP, GUIS *guiSP,
		   NearestAtomS *nearest_atomSP, size_t pixelsN,
		   unsigned int refreshI,
		   XMotionEvent *motion_eventSP)
{
size_t			pixelI;
NearestAtomS		*pixelSP;
MolComplexS		*curr_mol_complexSP;
AtomS			*curr_atomSP;
RawAtomS		*raw_atomSP;
static char		stringA[STRINGSIZE];
int			text_line_height;
int			screen_x0, screen_y0;
double			tf;
double			x, y, z;
ResidueS		*curr_residueSP;
double			phi, psi, omega, chi1, chi2, chi3;
int			central_residueI, first_residueI, last_residueI;
int			space_width, space_half_width;
int			residueI;
AtomS			*first_atomSP;
char			*residue_nameP;
char			residue_nameA[RESNAMESIZE];
char			serial_and_chainA[SHORTSTRINGSIZE];
int			residue_name_length, string_length;
int			text_width;
int			line_x0 = 0, line_x1 = 0;
int			width, height;
ResidueS		*central_residueSP;
size_t			first_atomI, last_atomI;
int			octantI;
int			n;
VectorS			N_vectorS, C_vectorS;
double			delta_x, delta_y;
double			rho, cos_angle, sin_angle, angle;
int			top_left_x, top_left_y;

/*------Ramachandran plot:---------------------------------------------------*/

/* If main window contains Ramachandran plot: */
if (guiSP->main_window_modeI == 1)
    {
    return RamaMouse_ (mol_complexSP, mol_complexesN,
		       configSP, guiSP,
		       nearest_atomSP, pixelsN,
		       refreshI,
		       motion_eventSP);
    }

/*------hydrophobicity plots:------------------------------------------------*/

/* If main window contains hydrophobicity plots: */
else if (guiSP->main_window_modeI == 4)
    {
    return PlotMouse_ (runtimeSP, guiSP,
		       nearest_atomSP, pixelsN, refreshI,
		       motion_eventSP);
    }

/*------sequence comparison plot:--------------------------------------------*/

/* If main window contains sequence comparison: */
else if (guiSP->main_window_modeI == 5)
    {
    return ComparisonMouse_ (runtimeSP, guiSP,
			     nearest_atomSP, pixelsN,
			     motion_eventSP);
    }

/*---------------------------------------------------------------------------*/

/* If the event came from child window, return zero: */
if (motion_eventSP->subwindow != None) return 0;

/*------output window:-------------------------------------------------------*/

/* Pixel index: */
pixelI = guiSP->main_win_free_area_width * motion_eventSP->y +
	 motion_eventSP->x;

/* Check the pixel index: */
if (pixelI >= pixelsN) return -1;

/* Pointer to NearestAtomS structure assigned to this pixel: */
pixelSP = nearest_atomSP + pixelI;

/* Check the refreshI associated with this pixel: */
if (pixelSP->last_refreshI != refreshI) return 0;

/* Check the style index associated with this pixel; */
/* if only plane  was drawn to this pixel,  there is */
/* no atomic information  associated with the pixel: */
if (pixelSP->styleI == PLANE_STYLE) return 0;

/* Refresh the output window: */
XSetForeground (guiSP->displaySP, guiSP->theGCA[0],
		guiSP->output_winS.bg_colorID);
XFillRectangle (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
		0, 0, guiSP->output_winS.width, guiSP->output_winS.height);

/* Pointer to the current macromolecular complex: */
curr_mol_complexSP = mol_complexSP + pixelSP->mol_complexI;

/* Pointer to the current atom: */
curr_atomSP = curr_mol_complexSP->atomSP + pixelSP->atomI;

/* Pointer to raw atomic data: */
raw_atomSP = &curr_atomSP->raw_atomS;

/* Pointer to the current residue: */
curr_residueSP = curr_mol_complexSP->residueSP + curr_atomSP->residue_arrayI;

/* Prepare the text foreground color: */
XSetForeground (guiSP->displaySP, guiSP->theGCA[0],
		guiSP->output_winS.fg_colorID);

/* Text line height: */
text_line_height = guiSP->output_winS.font_height;

/* The initial drawing position: */
screen_x0 = TEXTMARGIN;
screen_y0 = guiSP->output_winS.font_height + 1;

/* Draw the unique PDB identifier: */
sprintf (stringA, "(%d) %s",
	 curr_mol_complexSP->mol_complexID,
	 curr_mol_complexSP->unique_PDB_codeA);
stringA[STRINGSIZE - 1] = '\0';
XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
	     screen_x0, screen_y0, stringA, strlen (stringA));

/* Draw residue data: */
screen_y0 += text_line_height;
sprintf (stringA, "%s %d %c",
	 raw_atomSP->residue_nameA,
	 raw_atomSP->residue_sequenceI,
	 raw_atomSP->chainID);
stringA[STRINGSIZE - 1] = '\0';
XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
	     screen_x0, screen_y0, stringA, strlen (stringA));

/* Draw atom name,  alternate location  indicator, */
/* atom serial number and chemical element symbol: */
screen_y0 += text_line_height;
sprintf (stringA, "%s%c %d %s",
	 raw_atomSP->pure_atom_nameA,
	 raw_atomSP->alt_location,
	 raw_atomSP->serialI,
	 raw_atomSP->chemical_symbolA);
stringA[STRINGSIZE - 1] = '\0';
XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
	     screen_x0, screen_y0, stringA, strlen (stringA));

/* Temperature factor: */
screen_y0 += text_line_height;
tf = raw_atomSP->temperature_factor;
if (tf < 99.99) sprintf (stringA, "t.f.: %5.2f", tf);
else sprintf (stringA, "t.f. :%.2f", tf);
stringA[STRINGSIZE - 1] = '\0';
XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
	     screen_x0, screen_y0, stringA, strlen (stringA));

/* Hydrophobicity: */
screen_y0 += text_line_height;
sprintf (stringA, "phob:%6.2f", raw_atomSP->hydrophobicity);
stringA[STRINGSIZE - 1] = '\0';
XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
	     screen_x0, screen_y0, stringA, strlen (stringA));

/* Selection status: */
screen_y0 += text_line_height;
if (curr_atomSP->selectedF) strcpy (stringA, "selected");
else strcpy (stringA, "not selected");
XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
	     screen_x0, screen_y0, stringA, strlen (stringA));

/* The angle phi (see dihedral_angles.c for definition): */
screen_y0 += text_line_height;
phi = curr_residueSP->phi;
if (phi == BADDIHEDANGLE)
    {
    strcpy (stringA, "phi: missing");
    XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
		 screen_x0, screen_y0, stringA, strlen (stringA));
    }
else
    {
    phi *= RAD_TO_DEG;
    sprintf (stringA, "phi: %7.2f", phi);
    stringA[STRINGSIZE - 1] = '\0';
    XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
		 screen_x0, screen_y0, stringA, strlen (stringA));
    }

/* The angle psi (see dihedral_angles.c for definition): */
screen_y0 += text_line_height;
psi = curr_residueSP->psi;
if (psi == BADDIHEDANGLE)
    {
    strcpy (stringA, "psi: missing");
    XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
		 screen_x0, screen_y0, stringA, strlen (stringA));
    }
else
    {
    psi *= RAD_TO_DEG;
    sprintf (stringA, "psi: %7.2f", psi);
    stringA[STRINGSIZE - 1] = '\0';
    XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
		 screen_x0, screen_y0, stringA, strlen (stringA));
    }

/* The angle omega (see dihedral_angles.c for definition): */
screen_y0 += text_line_height;
omega = curr_residueSP->omega;
if (omega == BADDIHEDANGLE)
    {
    strcpy (stringA, "ome: missing");
    XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
		 screen_x0, screen_y0, stringA, strlen (stringA));
    }
else
    {
    omega *= RAD_TO_DEG;
    sprintf (stringA, "ome: %7.2f", omega);
    stringA[STRINGSIZE - 1] = '\0';
    XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
		 screen_x0, screen_y0, stringA, strlen (stringA));
    }

/* The angle chi1 (see dihedral_angles.c for definition): */
screen_y0 += text_line_height;
chi1 = curr_residueSP->chi1;
if (chi1 == BADDIHEDANGLE)
    {
    strcpy (stringA, "chi1:    -");
    XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
		 screen_x0, screen_y0, stringA, strlen (stringA));
    }
else
    {
    chi1 *= RAD_TO_DEG;
    sprintf (stringA, "chi1:%7.2f", chi1);
    stringA[STRINGSIZE - 1] = '\0';
    XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
    screen_x0, screen_y0, stringA, strlen (stringA));
    }

/* The angle chi2 (see dihedral_angles.c for definition): */
screen_y0 += text_line_height;
chi2 = curr_residueSP->chi2;
if (chi2 == BADDIHEDANGLE)
    {
    strcpy (stringA, "chi2:    -");
    XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
		 screen_x0, screen_y0, stringA, strlen (stringA));
    }
else
    {
    chi2 *= RAD_TO_DEG;
    sprintf (stringA, "chi2:%7.2f", chi2);
    stringA[STRINGSIZE - 1] = '\0';
    XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
		 screen_x0, screen_y0, stringA, strlen (stringA));
    }

/* The angle chi3 (see dihedral_angles.c for definition): */
screen_y0 += text_line_height;
chi3 = curr_residueSP->chi3;
if (chi3 == BADDIHEDANGLE)
    {
    strcpy (stringA, "chi3:    -");
    XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
		 screen_x0, screen_y0, stringA, strlen (stringA));
    }
else
    {
    chi3 *= RAD_TO_DEG;
    sprintf (stringA, "chi3:%7.2f", chi3);
    stringA[STRINGSIZE - 1] = '\0';
    XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
		 screen_x0, screen_y0, stringA, strlen (stringA));
    }

/* x coordinate: */
screen_y0 += text_line_height;
x = raw_atomSP->x[0];
if (x < 9999.999) sprintf (stringA, "x:  %8.3f", x);
else sprintf (stringA, "x:  %.3f", x);
stringA[STRINGSIZE - 1] = '\0';
XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
	     screen_x0, screen_y0, stringA, strlen (stringA));

/* y coordinate: */
screen_y0 += text_line_height;
y = raw_atomSP->y;
if (y < 9999.999) sprintf (stringA, "y:  %8.3f", y);
else sprintf (stringA, "y:  %.3f", y);
stringA[STRINGSIZE - 1] = '\0';
XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
	     screen_x0, screen_y0, stringA, strlen (stringA));

/* z coordinate: */
screen_y0 += text_line_height;
z = raw_atomSP->z[0];
if (z < 9999.999) sprintf (stringA, "z:  %8.3f", z);
else sprintf (stringA, "z:  %.3f", z);
stringA[STRINGSIZE - 1] = '\0';
XDrawString (guiSP->displaySP, guiSP->output_winS.ID, guiSP->theGCA[0],
	     screen_x0, screen_y0, stringA, strlen (stringA));

/*------sequence neighborhood:-----------------------------------------------*/

/* Write  the sequence neighborhood  of the */
/* residue under the pointer, if requested: */
if (configSP->show_sequence_neighborhoodF)
    {
    /* The central residue index: */
    central_residueI = curr_atomSP->residue_arrayI;

    /*------draw text:-------------------------------------------------------*/

    /* Space half-width: */
    space_width = XTextWidth (guiSP->main_winS.fontSP, " ", 1);
    space_half_width = space_width / 2;

    /* Indices of the first and the last residue in the neighbourhood: */
    first_residueI = central_residueI - NEIGH_HALF_WIDTH;
    if (first_residueI < 0) first_residueI = 0;
    last_residueI = central_residueI + NEIGH_HALF_WIDTH;
    if (last_residueI >= (int) curr_mol_complexSP->residuesN)
	{
	last_residueI = (int) curr_mol_complexSP->residuesN - 1;
	}

    /* Prepare the string: */
    stringA[0] = '\0';
    residue_name_length = RESNAMESIZE - 1;
    strcat (stringA, " ");
    for (residueI = first_residueI; residueI <= last_residueI; residueI++)
	{
	/* The string width, without the central residue: */
	if (residueI == central_residueI)
	    {
	    string_length = strlen (stringA);
	    line_x0 = XTextWidth (guiSP->main_winS.fontSP,
				  stringA, string_length) - space_half_width;
	    }

	/* Pointer to current residue: */
	curr_residueSP = curr_mol_complexSP->residueSP + residueI;

	/* Pointer to the first atom of the current residue: */
	first_atomSP = curr_mol_complexSP->atomSP +
		       curr_residueSP->residue_startI;

	/* Pointer to residue name: */
	residue_nameP = first_atomSP->raw_atomS.pure_residue_nameA;

	/* Add the residue name to the output string: */
	strncpy (residue_nameA, residue_nameP, residue_name_length);
	residue_nameA[residue_name_length] = '\0';
	strcat (stringA, residue_nameA);

	/* If the output should be  verbose,  i.e.  should contain */
	/* residue serial numbers and chain identifiers, add them: */
	if (configSP->show_sequence_neighborhoodF == 2)
	    {
	    /* If chain identifier is trivial (space character), ignore it: */
	    if (first_atomSP->raw_atomS.chainID == ' ')
		{
		sprintf (serial_and_chainA, "%d",
			 first_atomSP->raw_atomS.residue_sequenceI);
		serial_and_chainA[SHORTSTRINGSIZE - 1] = '\0';
		}
	    /* If chain identifier is not trivial, add it: */
	    else
		{
		sprintf (serial_and_chainA, "%d%c",
			 first_atomSP->raw_atomS.residue_sequenceI,
			 first_atomSP->raw_atomS.chainID);
		serial_and_chainA[SHORTSTRINGSIZE - 1] = '\0';
		}

	    /* Append the serial number and chain ID to residue name: */
	    strcat (stringA, serial_and_chainA);
	    }

	/* Add a single space: */
	strcat (stringA, " ");

	/* The string width, including the central residue: */
	if (residueI == central_residueI)
	    {
	    string_length = strlen (stringA);
	    line_x1 = XTextWidth (guiSP->main_winS.fontSP,
				  stringA, string_length) - space_half_width;
	    }
	}

    /* String length: */
    string_length = strlen (stringA);

    /* Text width: */
    text_width = XTextWidth (guiSP->main_winS.fontSP, stringA, string_length);

    /* Prepare text background color: */
    XSetForeground (guiSP->displaySP, guiSP->theGCA[0],
		    guiSP->main_winS.bg_colorID);

    /* Refresh the string background: */
    screen_x0 = 1;
    screen_y0 = 1;
    width = guiSP->control_winS.x0 - guiSP->main_winS.border_width - 2;
    if (width < 0) width = 0;
    height = guiSP->main_winS.font_height + 4;
    XFillRectangle (guiSP->displaySP, guiSP->main_winS.ID, guiSP->theGCA[0],
		    screen_x0, screen_y0,
		    (unsigned int) width, (unsigned int) height);

    /* Prepare text color: */
    XSetForeground (guiSP->displaySP, guiSP->theGCA[0],
    guiSP->main_winS.fg_colorID);

    /* Draw the bounding rectangle: */
    XDrawRectangle (guiSP->displaySP, guiSP->main_winS.ID, guiSP->theGCA[0],
		    screen_x0, screen_y0,
		    (unsigned int) width, (unsigned int) height);

    /* Draw the string which contains sequence neighborhood: */
    screen_x0 = guiSP->control_winS.x0 / 2 - text_width / 2;
    screen_y0 = guiSP->main_winS.font_height + 1;
    XDrawString (guiSP->displaySP, guiSP->main_winS.ID, guiSP->theGCA[0],
		 screen_x0, screen_y0, stringA, string_length);

    /* Draw rectangle bounding the central residue: */
    line_x0 += guiSP->control_winS.x0 / 2 - text_width / 2;
    line_x1 += guiSP->control_winS.x0 / 2 - text_width / 2;
    width = line_x1 - line_x0 + 1;
    if (width < 0) width = 0;
    height = guiSP->main_winS.font_height - 1;
    if (height < 0) height = 0;
    XDrawRectangle (guiSP->displaySP, guiSP->main_winS.ID, guiSP->theGCA[0],
		    line_x0, 4,
		    (unsigned int) width, (unsigned int) height);

    /*------draw the small arrow:--------------------------------------------*/

    /* Only the N and C atom of the central residue will be used. */
    central_residueSP = curr_mol_complexSP->residueSP + central_residueI;
    first_atomI = central_residueSP->residue_startI;
    last_atomI  = central_residueSP->residue_endI;

    /* Initialize the octant index: */
    octantI = 8;

    /* Formal, single pass loop: */
    do
	{
	/* Try to extract the N atom coordinates: */
	n = ExtractN_ (&N_vectorS,
		       curr_mol_complexSP->atomSP, first_atomI, last_atomI);
	if (n < 1) break;

	/* Try to extract the C atom coordinates: */
	n = ExtractC_ (&C_vectorS,
		       curr_mol_complexSP->atomSP, first_atomI, last_atomI);

	/* Now project the vector from N to C to the XY plane: */
	delta_x = C_vectorS.x - N_vectorS.x;
	delta_y = C_vectorS.y - N_vectorS.y;

	/* Prepare sine and cosine of the angle: */
	rho = sqrt (delta_x * delta_x + delta_y * delta_y);
	if (rho < 0.001) break;
	cos_angle = delta_x / rho;
	if      (cos_angle <= -0.999) angle = 3.1415927;
	else if (cos_angle >=  0.999) angle = 0.0;
	else angle = acos (cos_angle);
	sin_angle = delta_y / rho;
	if (sin_angle < 0.0) angle = 6.2831853 - angle;

	/* Identify the octant: */
	if      (angle < 0.392699087) octantI = 0;
	else if (angle < 1.178097262) octantI = 1;
	else if (angle < 1.963495437) octantI = 2;
	else if (angle < 2.748893609) octantI = 3;
	else if (angle < 3.534291783) octantI = 4;
	else if (angle < 4.319689957) octantI = 5;
	else if (angle < 5.105088131) octantI = 6;
	else if (angle < 5.890486305) octantI = 7;
	else octantI = 0;

	/* End of formal loop: */
	} while (0);

	/* Prepare the arrow position: */
	top_left_x = guiSP->control_winS.x0 / 2 - 16;
	top_left_y = 4 + height + 4;

	/* Draw the proper arrow: */
	switch (octantI)
	    {
	    case 0:
		XCopyArea (guiSP->displaySP,
			   guiSP->small_arrows_pixmapID, guiSP->main_winS.ID,
			   guiSP->theGCA[0],
			   0, 0, 32, 32, top_left_x, top_left_y);
		break;

	    case 1:
		XCopyArea (guiSP->displaySP,
			   guiSP->small_arrows_pixmapID, guiSP->main_winS.ID,
			   guiSP->theGCA[0],
			   0, 31, 32, 32, top_left_x, top_left_y);
		break;

	    case 2:
		XCopyArea (guiSP->displaySP,
			   guiSP->small_arrows_pixmapID, guiSP->main_winS.ID,
			   guiSP->theGCA[0],
			   0, 62, 32, 32, top_left_x, top_left_y);
		break;

	    case 3:
		XCopyArea (guiSP->displaySP,
			   guiSP->small_arrows_pixmapID, guiSP->main_winS.ID,
			   guiSP->theGCA[0],
			   0, 93, 32, 32, top_left_x, top_left_y);
		break;

	    case 4:
		XCopyArea (guiSP->displaySP,
			   guiSP->small_arrows_pixmapID, guiSP->main_winS.ID,
			   guiSP->theGCA[0],
			   0, 124, 32, 32, top_left_x, top_left_y);
		break;

	    case 5:
		XCopyArea (guiSP->displaySP,
			   guiSP->small_arrows_pixmapID, guiSP->main_winS.ID,
			   guiSP->theGCA[0],
			   0, 155, 32, 32, top_left_x, top_left_y);
		break;

	    case 6:
		XCopyArea (guiSP->displaySP,
			   guiSP->small_arrows_pixmapID, guiSP->main_winS.ID,
			   guiSP->theGCA[0],
			   0, 186, 32, 32, top_left_x, top_left_y);
		break;

	    case 7:
		XCopyArea (guiSP->displaySP,
			   guiSP->small_arrows_pixmapID, guiSP->main_winS.ID,
			   guiSP->theGCA[0],
			   0, 217, 32, 32, top_left_x, top_left_y);
		break;

	    default:
		XCopyArea (guiSP->displaySP,
			   guiSP->small_arrows_pixmapID, guiSP->main_winS.ID,
			   guiSP->theGCA[0],
			   0, 248, 32, 32, top_left_x, top_left_y);
	    }
    }

/*---------------------------------------------------------------------------*/

/* Return positive value: */
return 1;
}

/*===========================================================================*/


