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

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

				backbone.c

Purpose:
	Draw backbone.

Input:
	(1) Pointer to MolComplexS structure.
	(2) Number of macromolecular complexes.
	(3) Pointer to ConfigS structure, with configuration data.
	(4) Pointer to GUIS structure.
	(5) Pointer to NearestAtomS structure, with information about the
	    atom occupying the given pixel.
	(6) The number of pixels in the main window free area.
	(7) The refreshI, used to check the  NearestAtomS associated with
	    a given pixel.

Output:
	(1) Bonds drawn to the hidden pixmap.
	(2) Return value.

Return value:
	(1) The number of bonds drawn.

Notes:
	(1) Indentation is exceptionally 4 spaces.

	(2) This function includes all drawing styles.

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

#include <stdio.h>
#include <stdlib.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:=================================================*/

unsigned long	InterpolateColor_ (unsigned long, unsigned long, GUIS *);
void		AddTwoColors_ (unsigned long, unsigned long, GUIS *,
			       unsigned long *, unsigned long *);
void		AddThreeColors_ (unsigned long, unsigned long, GUIS *,
				 unsigned long *,
				 unsigned long *,
				 unsigned long *);
int		BondStyle1Quadrant1_ (Aux1S *, int);
int		BondStyle1Quadrant2_ (Aux1S *, int);
int		BondStyle1Quadrant3_ (Aux1S *, int);
int		BondStyle1Quadrant4_ (Aux1S *, int);
int		BondStyle2Quadrant1_ (Aux1S *, int);
int		BondStyle2Quadrant2_ (Aux1S *, int);
int		BondStyle2Quadrant3_ (Aux1S *, int);
int		BondStyle2Quadrant4_ (Aux1S *, int);
int		BondStyle3Quadrant1_ (Aux1S *, int);
int		BondStyle3Quadrant2_ (Aux1S *, int);
int		BondStyle3Quadrant3_ (Aux1S *, int);
int		BondStyle3Quadrant4_ (Aux1S *, int);
int		BondStyle4Quadrant1_ (Aux1S *, int);
int		BondStyle4Quadrant2_ (Aux1S *, int);
int		BondStyle4Quadrant3_ (Aux1S *, int);
int		BondStyle4Quadrant4_ (Aux1S *, int);
int		BondStyle5Quadrant1_ (Aux1S *, int);
int		BondStyle5Quadrant2_ (Aux1S *, int);
int		BondStyle5Quadrant3_ (Aux1S *, int);
int		BondStyle5Quadrant4_ (Aux1S *, int);

/*======draw backbone:=======================================================*/

size_t DrawBackbone_ (MolComplexS *mol_complexSP, int mol_complexesN,
		      ConfigS *configSP, GUIS *guiSP,
		      NearestAtomS *nearest_atomSP, size_t pixelsN,
		      unsigned int refreshI)
{
size_t			bonds_drawnN = 0;
int			imageI, imagesN;
Aux1S			aux1S;
int			mol_complexI;
MolComplexS		*curr_mol_complexSP;
size_t			c_alphaI, c_alphaN;
BackboneS		*curr_backboneSP;
AtomS			*curr_atomSP, *partner_atomSP;
unsigned long		color1ID, color2ID, color3ID;
unsigned long		color4ID, color5ID, color6ID;
double			z_shift, delta_z_sign;
int			screen_abs_delta_x, screen_abs_delta_y;
int			regionI;
int			styleI;
int			bondI;

/* Number of images: */
if (configSP->stereoF) imagesN = 2;
else imagesN = 1;

/* Prepare the auxiliary  Aux1S  structure,  used to */
/* reduce the number of arguments in function calls: */
aux1S.configSP = configSP;
aux1S.guiSP = guiSP;
aux1S.nearest_atomSP = nearest_atomSP;
aux1S.pixelsN = pixelsN;
aux1S.refreshI = refreshI;

/* Initialize  the bond  index.  The negative value */
/* means that backbone is not a true chemical bond: */
bondI = -1;

/* Scan each macromolecular complex: */
for (mol_complexI = 0; mol_complexI < mol_complexesN; mol_complexI++)
    {
    /* Pointer to current macromolecular complex: */
    curr_mol_complexSP = mol_complexSP + mol_complexI;

    /* Check the number of atoms (the complex may be discarded): */
    if (curr_mol_complexSP->atomsN == 0) continue;

    /* Prepare and check the number of CA atoms: */
    c_alphaN = curr_mol_complexSP->c_alphaN;
    if (c_alphaN == 0) continue;

    /* Copy the macromol. complex index into aux1S: */
    aux1S.mol_complexI = mol_complexI;

    /* Draw backbone for each CA atoms: */
    for (c_alphaI = 0; c_alphaI < c_alphaN; c_alphaI++)
	{
	/* Pointer to the current BackboneS structure: */
	curr_backboneSP = curr_mol_complexSP->backboneSP + c_alphaI;

	/* The current backbone element may be hidden: */
	if (curr_backboneSP->hiddenF) continue;

	/* Pointer to the current CA atom: */
	curr_atomSP = curr_mol_complexSP->atomSP + curr_backboneSP->c_alphaI;

	/* Check is atom inside slab: */
	if (!curr_atomSP->inside_slabF) continue;

	/* Check is atom inside window: */
	if (!curr_atomSP->inside_windowF) continue;

	/* Copy the style index: */
	styleI = curr_backboneSP->backbone_styleI;

	/* Prepare colors: */
	switch (styleI)
	    {
	    case 1:
		XSetForeground (guiSP->displaySP, guiSP->theGCA[0],
				curr_atomSP->left_colorID);
		aux1S.colorIDA[0] = curr_atomSP->left_colorID;
		break;

	    case 2:
		XSetForeground (guiSP->displaySP, guiSP->theGCA[0],
				curr_atomSP->left_colorID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[1],
				curr_atomSP->middle_colorID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[2],
				curr_atomSP->right_colorID);
		aux1S.colorIDA[0] = curr_atomSP->left_colorID;
		aux1S.colorIDA[1] = curr_atomSP->middle_colorID;
		aux1S.colorIDA[2] = curr_atomSP->right_colorID;
		break;

	    case 3:
		color1ID = InterpolateColor_ (curr_atomSP->left_colorID,
					      curr_atomSP->middle_colorID,
					      guiSP);
		color2ID = InterpolateColor_ (curr_atomSP->middle_colorID,
					      curr_atomSP->right_colorID,
					      guiSP);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[0],
				curr_atomSP->left_colorID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[1],
				color1ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[2],
				curr_atomSP->middle_colorID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[3],
				color2ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[4],
				curr_atomSP->right_colorID);
		aux1S.colorIDA[0] = curr_atomSP->left_colorID;
		aux1S.colorIDA[1] = color1ID;
		aux1S.colorIDA[2] = curr_atomSP->middle_colorID;
		aux1S.colorIDA[3] = color2ID;
		aux1S.colorIDA[4] = curr_atomSP->right_colorID;
		break;

	    case 4:
		AddTwoColors_ (curr_atomSP->left_colorID,
			       curr_atomSP->middle_colorID,
			       guiSP,
			       &color1ID, &color2ID);
		AddTwoColors_ (curr_atomSP->middle_colorID,
			       curr_atomSP->right_colorID,
			       guiSP,
			       &color3ID, &color4ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[0],
				curr_atomSP->left_colorID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[1],
				color1ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[2],
				color2ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[3],
				curr_atomSP->middle_colorID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[4],
				color3ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[5],
				color4ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[6],
				curr_atomSP->right_colorID);
		aux1S.colorIDA[0] = curr_atomSP->left_colorID;
		aux1S.colorIDA[1] = color1ID;
		aux1S.colorIDA[2] = color2ID;
		aux1S.colorIDA[3] = curr_atomSP->middle_colorID;
		aux1S.colorIDA[4] = color3ID;
		aux1S.colorIDA[5] = color4ID;
		aux1S.colorIDA[6] = curr_atomSP->right_colorID;
		break;

	    case 5:
		AddThreeColors_ (curr_atomSP->left_colorID,
				 curr_atomSP->middle_colorID,
				 guiSP,
				 &color1ID, &color2ID, &color3ID);
		AddThreeColors_ (curr_atomSP->middle_colorID,
				 curr_atomSP->right_colorID,
				 guiSP,
				 &color4ID, &color5ID, &color6ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[0],
				curr_atomSP->left_colorID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[1],
				color1ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[2],
				color2ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[3],
				color3ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[4],
				curr_atomSP->middle_colorID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[5],
				color4ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[6],
				color5ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[7],
				color6ID);
		XSetForeground (guiSP->displaySP, guiSP->theGCA[8],
				curr_atomSP->right_colorID);
		aux1S.colorIDA[0] = curr_atomSP->left_colorID;
		aux1S.colorIDA[1] = color1ID;
		aux1S.colorIDA[2] = color2ID;
		aux1S.colorIDA[3] = color3ID;
		aux1S.colorIDA[4] = curr_atomSP->middle_colorID;
		aux1S.colorIDA[5] = color4ID;
		aux1S.colorIDA[6] = color5ID;
		aux1S.colorIDA[7] = color6ID;
		aux1S.colorIDA[8] = curr_atomSP->right_colorID;
		break;
	    }

	/* Copy the atomic index into aux1S: */
	aux1S.atomI = curr_backboneSP->c_alphaI;

	/* Draw bond to the previous CA atom: */
	if (curr_backboneSP->previous_c_alphaF)
	    {
	    /* Pointer to the bond partner: */
	    partner_atomSP = curr_mol_complexSP->atomSP +
			     curr_backboneSP->previous_c_alphaI;

	    /* Draw one (mono) or two bonds (stereo): */
	    for (imageI = 0; imageI < imagesN; imageI++)
		{
		/* Copy imageI to aux1S: */
		aux1S.imageI = imageI;

		/* Prepare screen coordinates of */
		/* both atoms  involved in bond: */
		aux1S.screen_x0 = curr_atomSP->raw_atomS.screen_x[imageI];
		aux1S.screen_y0 = curr_atomSP->raw_atomS.screen_y;
		aux1S.screen_x1 = partner_atomSP->raw_atomS.screen_x[imageI];
		aux1S.screen_y1 = partner_atomSP->raw_atomS.screen_y;
		aux1S.atomic_z0 = curr_atomSP->raw_atomS.z[imageI];
		aux1S.atomic_z1 = partner_atomSP->raw_atomS.z[imageI];

		/* Prepare differences: */
		aux1S.screen_delta_x = aux1S.screen_x1 - aux1S.screen_x0;
		aux1S.screen_delta_y = aux1S.screen_y1 - aux1S.screen_y0;
		aux1S.atomic_delta_z = aux1S.atomic_z1 - aux1S.atomic_z0;

		/* Shift and tilt bond to ensure */
		/* the proper stacking of bonds: */
		delta_z_sign = 1.0;
		if (aux1S.atomic_delta_z < 0) delta_z_sign = -1.0;
		z_shift = delta_z_sign * Z_SHIFT;
		aux1S.atomic_z0 += z_shift;
		aux1S.atomic_z1 -= z_shift;
		aux1S.atomic_delta_z -= 3 * z_shift;

		/* Some absolute differences: */
		screen_abs_delta_x = abs (aux1S.screen_delta_x);
		screen_abs_delta_y = abs (aux1S.screen_delta_y);

		/* Find the region: */
		if (screen_abs_delta_x >= screen_abs_delta_y)
		    {
		    if (aux1S.screen_delta_x >= 0) regionI = 1;
		    else regionI = 3;
		    }
		else
		    {
		    if (aux1S.screen_delta_y >= 0) regionI = 2;
		    else regionI = 4;
		    }
		if ((aux1S.screen_delta_x == 0) && (aux1S.screen_delta_y == 0))
		    {
		    regionI = 0;
		    }

		/* Each region has its own drawing procedure: */
		switch (regionI)
		    {
		    /* Both atoms are  projected to  the same  point, */
		    /* i.e. this bond is perpendicular to the screen: */
		    case 0:
			/* Do nothing! */
			break;

		    /* Right quadrant: */
		    case 1:
			switch (styleI)
			    {
			    case 1:
				BondStyle1Quadrant1_ (&aux1S, bondI);
				break;
			    case 2:
				BondStyle2Quadrant1_ (&aux1S, bondI);
				break;
			    case 3:
				BondStyle3Quadrant1_ (&aux1S, bondI);
				break;
			    case 4:
				BondStyle4Quadrant1_ (&aux1S, bondI);
				break;
			    case 5:
				BondStyle5Quadrant1_ (&aux1S, bondI);
				break;
			    default:
				;
			    }
			break;

		    /* Bottom quadrant (check Note 3!): */
		    case 2:
			switch (styleI)
			    {
			    case 1:
				BondStyle1Quadrant2_ (&aux1S, bondI);
				break;
			    case 2:
				BondStyle2Quadrant2_ (&aux1S, bondI);
				break;
			    case 3:
				BondStyle3Quadrant2_ (&aux1S, bondI);
				break;
			    case 4:
				BondStyle4Quadrant2_ (&aux1S, bondI);
				break;
			    case 5:
				BondStyle5Quadrant2_ (&aux1S, bondI);
				break;
			    default:
				;
			    }
			break;

		    /* Left quadrant: */
		    case 3:
			switch (styleI)
			    {
			    case 1:
				BondStyle1Quadrant3_ (&aux1S, bondI);
				break;
			    case 2:
				BondStyle2Quadrant3_ (&aux1S, bondI);
				break;
			    case 3:
				BondStyle3Quadrant3_ (&aux1S, bondI);
				break;
			    case 4:
				BondStyle4Quadrant3_ (&aux1S, bondI);
				break;
			    case 5:
				BondStyle5Quadrant3_ (&aux1S, bondI);
				break;
			    default:
				;
			    }
			break;

		    /* Top quadrant (check Note 3!): */
		    case 4:
			switch (styleI)
			    {
			    case 1:
				BondStyle1Quadrant4_ (&aux1S, bondI);
				break;
			    case 2:
				BondStyle2Quadrant4_ (&aux1S, bondI);
				break;
			    case 3:
				BondStyle3Quadrant4_ (&aux1S, bondI);
				break;
			    case 4:
				BondStyle4Quadrant4_ (&aux1S, bondI);
				break;
			    case 5:
				BondStyle5Quadrant4_ (&aux1S, bondI);
				break;
			    default:
				;
			    }
			break;

		    /* This should be the impossible case: */
		    default:
			;
		    }

		/* Update the counter: */
		bonds_drawnN++;
		}	/* imageI loop */
	    }

	/* Draw bond to the next CA atom: */
	if (curr_backboneSP->next_c_alphaF)
	    {
	    /* Pointer to the bond partner: */
	    partner_atomSP = curr_mol_complexSP->atomSP +
			     curr_backboneSP->next_c_alphaI;

	    /* Draw one (mono) or two bonds (stereo): */
	    for (imageI = 0; imageI < imagesN; imageI++)
		{
		/* Copy imageI to aux1S: */
		aux1S.imageI = imageI;

		/* Prepare screen coordinates of */
		/* both atoms  involved in bond: */
		aux1S.screen_x0 = curr_atomSP->raw_atomS.screen_x[imageI];
		aux1S.screen_y0 = curr_atomSP->raw_atomS.screen_y;
		aux1S.screen_x1 = partner_atomSP->raw_atomS.screen_x[imageI];
		aux1S.screen_y1 = partner_atomSP->raw_atomS.screen_y;
		aux1S.atomic_z0 = curr_atomSP->raw_atomS.z[imageI];
		aux1S.atomic_z1 = partner_atomSP->raw_atomS.z[imageI];

		/* Prepare differences: */
		aux1S.screen_delta_x = aux1S.screen_x1 - aux1S.screen_x0;
		aux1S.screen_delta_y = aux1S.screen_y1 - aux1S.screen_y0;
		aux1S.atomic_delta_z = aux1S.atomic_z1 - aux1S.atomic_z0;

		/* Shift and tilt bond to ensure */
		/* the proper stacking of bonds: */
		delta_z_sign = 1.0;
		if (aux1S.atomic_delta_z < 0) delta_z_sign = -1.0;
		z_shift = delta_z_sign * Z_SHIFT;
		aux1S.atomic_z0 += z_shift;
		aux1S.atomic_z1 -= z_shift;
		aux1S.atomic_delta_z -= 3 * z_shift;

		/* Some absolute differences: */
		screen_abs_delta_x = abs (aux1S.screen_delta_x);
		screen_abs_delta_y = abs (aux1S.screen_delta_y);

		/* Find the region: */
		if (screen_abs_delta_x >= screen_abs_delta_y)
		    {
		    if (aux1S.screen_delta_x >= 0) regionI = 1;
		    else regionI = 3;
		    }
		else
		    {
		    if (aux1S.screen_delta_y >= 0) regionI = 2;
		    else regionI = 4;
		    }
		if ((aux1S.screen_delta_x == 0) && (aux1S.screen_delta_y == 0))
		    {
		    regionI = 0;
		    }

		/* Each region has its own drawing procedure: */
		switch (regionI)
		    {
		    /* Both atoms are  projected to  the same  point, */
		    /* i.e. this bond is perpendicular to the screen: */
		    case 0:
			/* Do nothing! */
			break;

		    /* Right quadrant: */
		    case 1:
			switch (styleI)
			    {
			    case 1:
				BondStyle1Quadrant1_ (&aux1S, bondI);
				break;
			    case 2:
				BondStyle2Quadrant1_ (&aux1S, bondI);
				break;
			    case 3:
				BondStyle3Quadrant1_ (&aux1S, bondI);
				break;
			    case 4:
				BondStyle4Quadrant1_ (&aux1S, bondI);
				break;
			    case 5:
				BondStyle5Quadrant1_ (&aux1S, bondI);
				break;
			    default:
				;
			    }
			break;

		    /* Bottom quadrant (check Note 3!): */
		    case 2:
			switch (styleI)
			    {
			    case 1:
				BondStyle1Quadrant2_ (&aux1S, bondI);
				break;
			    case 2:
				BondStyle2Quadrant2_ (&aux1S, bondI);
				break;
			    case 3:
				BondStyle3Quadrant2_ (&aux1S, bondI);
				break;
			    case 4:
				BondStyle4Quadrant2_ (&aux1S, bondI);
				break;
			    case 5:
				BondStyle5Quadrant2_ (&aux1S, bondI);
				break;
			    default:
				;
			    }
			break;

		    /* Left quadrant: */
		    case 3:
			switch (styleI)
			    {
			    case 1:
				BondStyle1Quadrant3_ (&aux1S, bondI);
				break;
			    case 2:
				BondStyle2Quadrant3_ (&aux1S, bondI);
				break;
			    case 3:
				BondStyle3Quadrant3_ (&aux1S, bondI);
				break;
			    case 4:
				BondStyle4Quadrant3_ (&aux1S, bondI);
				break;
			    case 5:
				BondStyle5Quadrant3_ (&aux1S, bondI);
				break;
			    default:
				;
			    }
			break;

		    /* Top quadrant (check Note 3!): */
		    case 4:
			switch (styleI)
			    {
			    case 1:
				BondStyle1Quadrant4_ (&aux1S, bondI);
				break;
			    case 2:
				BondStyle2Quadrant4_ (&aux1S, bondI);
				break;
			    case 3:
				BondStyle3Quadrant4_ (&aux1S, bondI);
				break;
			    case 4:
				BondStyle4Quadrant4_ (&aux1S, bondI);
				break;
			    case 5:
				BondStyle5Quadrant4_ (&aux1S, bondI);
				break;
			    default:
				;
			    }
			break;

		    /* This should be the impossible case: */
		    default:
			;
		    }

		/* Update the counter: */
		bonds_drawnN++;
		}	/* imageI loop */
	    }
	}	/* c_alphaI loop */
    }		/* mol_complexI loop */

return bonds_drawnN;
}

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


