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

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

				edit_bond.c

Purpose:
	Edit  (rotate) the specified bond.  The array indices of two atoms
	which form the bond are used to specify the bond. 

Input:
	(1) Pointer to MolComplexS structure, pointing to default complex.
	    element.
	(2) The index 
	(3) Pointer to RuntimeS structure.
	(4) Pointer to ConfigS structure.
	(5) Rotation angle.

Output:
	(1) Part of  the chosen residue  rotated about the specified bond.
	(2) Return value.

Return value:
	(1) Positive on success.
	(2) Negative on failure.

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

#include <stdio.h>

#include <string.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		RotateAtom_ (AtomS *, VectorS *, VectorS *, double);
int		DihedralAngles_ (MolComplexS *, ConfigS *);

/*======edit (rotate) the specified bond:====================================*/

int EditBond_ (MolComplexS *curr_mol_complexSP, int mol_complexI,
	       RuntimeS *runtimeSP, ConfigS *configSP,
	       double rotation_angle)
{
size_t		atomsN;
size_t		atom1_arrayI, atom2_arrayI;
int		residueI;
int		start_atomI, end_atomI, atomI;
AtomS		*curr_atomSP;
int		stepI, previous_stepI;
int		atoms_in_residueN;
int		propagationF;
int		atom_reachedF;
int		bondI;
TrueBondS	*curr_bondSP;
AtomS		*curr_neighborSP;
char		*atom_nameP;
int		CA_foundF;
VectorS		atom1_vectorS, atom2_vectorS;

/*------prepare required indices:--------------------------------------------*/

/* Prepare and check the number of atoms: */
atomsN = curr_mol_complexSP->atomsN;
if (atomsN == 0) return -1;

/* Copy the array indices of two atoms which define the chosen bond: */
atom1_arrayI = runtimeSP->atom1_arrayI;
atom2_arrayI = runtimeSP->atom2_arrayI;

/* Two atoms which specify the bond should belong to the same residue: */
residueI = (curr_mol_complexSP->atomSP + atom1_arrayI)->residue_arrayI;
if ((int) (curr_mol_complexSP->atomSP + atom2_arrayI)->residue_arrayI !=
								residueI)
	{
	return -2;
	}

/* Prepare and check the atomic array indices which */
/* define the residue  to which  both atoms belong. */
start_atomI = (curr_mol_complexSP->residueSP + residueI)->residue_startI;
end_atomI   = (curr_mol_complexSP->residueSP + residueI)->residue_endI;
if (((int) atom1_arrayI < start_atomI) || ((int) atom1_arrayI > end_atomI))
	{
	return -3;
	}
if (((int) atom2_arrayI < start_atomI) || ((int) atom2_arrayI > end_atomI))
	{
	return -4;
	}

/*------reset auxiliaryI for each atom of a chosen residue:------------------*/

/* Scan the chosen residue, atom by atom: */
for (atomI = start_atomI; atomI <= end_atomI; atomI++)
	{
	/* Pointer to the current atom: */
	curr_atomSP = curr_mol_complexSP->atomSP + atomI;

	/* Reset the auxiliaryI: */
	curr_atomSP->auxiliaryI = 0;
	}

/*------try to find the CA atom:---------------------------------------------*/

/* Only the atoms which are opposite to the main chain with */
/* respect to the specified bond should be rotated. To find */
/* these atoms, a shock wave is send from the first atom to */
/* the second atom and further. If this wave will reach the */
/* CA atom,  the atoms  which should be rotated  are on the */
/* opposite side of the specified bond.  Otherwise,  the CA */
/* atom is either  missing or  placed on the opposite side. */

/* Initialize the step index: */
stepI = 1;

/* Associate the first step index with the first atom: */
curr_atomSP = curr_mol_complexSP->atomSP + atom1_arrayI;
curr_atomSP->auxiliaryI = 1;

/* Associate the second step index with the second atom: */
curr_atomSP = curr_mol_complexSP->atomSP + atom2_arrayI;
curr_atomSP->auxiliaryI = 2;

/* Now scan the residue repeatedly. The number of scans should not */
/* exceed the number of atoms  in the residue,  because the length */
/* of the fully extended residue is equal to  number_of_atoms - 1. */
atoms_in_residueN = end_atomI - start_atomI + 1;

/* Initialize the index of the previous step: */
previous_stepI = 2;

/* Initialize the flag which says that CA atom is found: */
CA_foundF = 0;

/* The loop which counts steps should start from the third step: */
for (stepI = 3; stepI < atoms_in_residueN; stepI++)
	{
	/* Reset the wave propagation flag: */
	propagationF = 0;

	/* In each step, scan the entire residue, atom by atom: */
	for (atomI = start_atomI; atomI <= end_atomI; atomI++)
		{
		/* In the third step,  the first atom of */
		/* the specified bond should be skipped: */ 
		if ((stepI == 3) && (atomI == (int) atom1_arrayI)) continue;

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

		/* If the current atom was reached */
		/* in the previous step,  skip it: */
		if (curr_atomSP->auxiliaryI == previous_stepI) continue;

		/* Reset the flag which says that the */
		/* atom is reached by the shock wave: */
		atom_reachedF = 0;

		/* Scan all bonds of the current atom: */
		for (bondI = 0; bondI < curr_atomSP->bondsN; bondI++)
			{
			/* Pointer to the current bond: */
			curr_bondSP = curr_atomSP->true_bondSA + bondI;

			/* Only the covalent bonds  are taken  into account. */
			/* Hydrogen, disulfide and pseudo-bonds are ignored. */
			if (curr_bondSP->bond_typeI != 1) continue;

			/* Bonds formed with atoms from another */
			/* macromolecular complex  are ignored: */
			if (curr_bondSP->neighbor_mol_complexI != mol_complexI)
				{
				continue;
				}

			/* If the bond partner was reached by the shock */
			/* wave in the previous step,  the current atom */
			/* is reached  by the shock wave  in this step: */
			curr_neighborSP = curr_mol_complexSP->atomSP +
					  curr_bondSP->neighbor_arrayI;

			if (curr_neighborSP->auxiliaryI == previous_stepI)
				{
				/* Set the flag which says  that the */
				/* shock wave continues propagation: */
				propagationF = 1;

				/* Set the flag which says  that this */
				/* atom is reached by the shock wave: */
				atom_reachedF = 1;

				/* Do not check  the remaining */
				/* bonds, it is not necessary: */
				break;
				}
			}

		/* If the current atom is reached in this step: */
		if (atom_reachedF)
			{
			/* If the current atom is the CA atom, set the flag: */
			atom_nameP = curr_atomSP->raw_atomS.pure_atom_nameA;
			if (strcmp (atom_nameP, "CA") == 0) CA_foundF = 1;

			/* If the shock wave reached the current atom */
			/* for the first time,  store the step index: */
			if (curr_atomSP->auxiliaryI == 0)
				{
				curr_atomSP->auxiliaryI = stepI;
				}
			}
		}

	/* If the shock wave is not propagating any more, break: */
	if (!propagationF) break;

	/* Update the value of the previous step index: */
	previous_stepI = stepI;
	}
 
/* If the  CA atom was reached by the shock wave,  the atoms which should */
/* be rotated about  the specified bond are  on the opposite side of this */
/* bond,  and for that reason all  auxiliaryI  values  should be changed. */
/* Zeros should be replaced by values different from zero and vice versa. */
if (CA_foundF)
	{
	/* Scan the chosen residue, atom by atom: */
	for (atomI = start_atomI; atomI <= end_atomI; atomI++)
		{
		/* Pointer to the current atom: */
		curr_atomSP = curr_mol_complexSP->atomSP + atomI;

		/* Revert the auxiliaryI: */
		if (curr_atomSP->auxiliaryI) curr_atomSP->auxiliaryI = 0;
		else curr_atomSP->auxiliaryI = 1;
		}
	}

/*------rotate atoms:--------------------------------------------------------*/

/* Prepare the coordinates of two atoms which define the rotation axis: */

/* The position of the first atom: */
curr_atomSP = curr_mol_complexSP->atomSP + atom1_arrayI;
atom1_vectorS.x = curr_atomSP->raw_atomS.x[0];
atom1_vectorS.y = curr_atomSP->raw_atomS.y;
atom1_vectorS.z = curr_atomSP->raw_atomS.z[0];

/* The position of the second atom: */
curr_atomSP = curr_mol_complexSP->atomSP + atom2_arrayI;
atom2_vectorS.x = curr_atomSP->raw_atomS.x[0];
atom2_vectorS.y = curr_atomSP->raw_atomS.y;
atom2_vectorS.z = curr_atomSP->raw_atomS.z[0];

/* Rotate all atoms after the current residue about CA-C bond: */
for (atomI = start_atomI + 1; atomI <= end_atomI; atomI++)
	{
	/* Pointer to the current atom: */
	curr_atomSP = curr_mol_complexSP->atomSP + atomI;

	/* If this atom is not in the outer portion of the side chain */
	/* (with respect to  the specified bond),  do not  rotate it: */
	if (curr_atomSP->auxiliaryI == 0) continue;

	/* Rotate atom: */
	RotateAtom_ (curr_atomSP,
		     &atom1_vectorS, &atom2_vectorS, rotation_angle);
	}

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

/* Update dihedral angles and cis-trans flags: */
DihedralAngles_ (curr_mol_complexSP, configSP);

/* Return positive value (trivial): */
return 1;
}

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


