/**************************************************************************
  phipsi.c
     Generates a file of internal angles and coordinates from a PDB File

  Date:	
     March, 2003

  Author:
     Aaron Davidson @ BTI
	 Modified by David Arndt, 2006-2007.
	 Modified by Beomsoo Han, 27 Sep. 2011.
  
 **************************************************************************/

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

#include "phipsi.h"
#include "util.h"
#include "vec3D.h"
#include "pdbfile.h"



/*************************************************************/
/* calculate the diffence of the vectors x and y.            */
/*************************************************************/
static void atom_difference(Atom *A, Atom *B, Vec3D dif) {
	difference(A->point, B->point, dif);
}

/*************************************************************/
/* Calculate torsion angle for four atoms                    */
/*************************************************************/
static double torsionAtom(Atom *A, Atom *B, Atom *C, Atom *D) {
	Vec3D X, Y, AB, BC, CD, cXY;
	double result;

	atom_difference(A, B, AB);
	atom_difference(B, C, BC);
	atom_difference(C, D, CD);

	crossproduct(AB, BC, X);
	crossproduct(BC, CD, Y);

   result = angle(X, Y);
   crossproduct(X, Y, cXY);
   if (dotproduct(BC, cXY) > 0.0) {
      result = -result;
	}
   return(result);
}

/*************************************************************/
/* Rotate the atoms in a residue into the template position. */
/* max_atom_index is the maximum index into vecs and names   */
/* of an entry with meaningful data.                         */
/*************************************************************/
static double canonicalForm(Vec3D *vecs, char names[MAX_ATOMS][5], int max_atom_index,
		int lastResidue) {
	int i;
	Vec3D xAxis = {1,0,0}; // a unit vector
	Vec3D T; // a temp vector
	double theta;
	double psi;
	
	// debug
	/*
	printf("canonicalForm:\n");
	printf("   max_atom_index = %d, psi = %lf, lastResidue = %d\n",
		max_atom_index, psi, lastResidue);
	for (i = 0; i <= max_atom_index; i++) {
		printf("   %4s %11.3lf %11.3lf %11.3lf\n", names[i],
			vecs[i][0], vecs[i][1], vecs[i][2]);
	}
	*/
	
	// translate to origin with CA at origin
	difference(vecs[Np], vecs[CA], vecs[Np]);
	for (i = CA+1; i <= max_atom_index; i++) {
		if (vecs[i][0] != ATOM_COORD_NA) {
			difference(vecs[i], vecs[CA], vecs[i]);
		}
	}
	difference(vecs[CA], vecs[CA], vecs[CA]);
	
	// compute angle from X axis
	setVector(T, vecs[C1][0], vecs[C1][1], 0);
	theta = angle(T, xAxis);	
	if (vecs[C1][1] > 0) theta = 360 - theta;
	
	// rotate atoms around Z axis
	for (i = 0; i <= max_atom_index; i++) {
		if (vecs[i][0] != ATOM_COORD_NA) {
			rotateZ(vecs[i], theta);
		}
	}
	
	// compute angle from X axis
	setVector(T, vecs[C1][0], 0, vecs[C1][2]);
	theta = angle(T, xAxis);
	if (vecs[C1][2] < 0) theta = 360 - theta;
	
	// rotate atoms around Y-axis
	for (i = 0; i <= max_atom_index; i++) {
		if (vecs[i][0] != ATOM_COORD_NA) {
			rotateY(vecs[i], theta);
		}
	}
	
	if (!lastResidue) {
		
		// compute psi angle
		psi = torsion(vecs[Np], vecs[CA], vecs[C1], vecs[N]);
		
		// compute angle for N
		theta = atan(vecs[N][2] / vecs[N][1]);
		theta = 180 - (theta * 180/PI);
		if (vecs[N][1] < 0) theta = 180+theta;
		
		// Rotate such that N is in the x-y plane
		for (i = 0; i <= max_atom_index; i++) {
			if (vecs[i][0] != ATOM_COORD_NA) {
				rotateX(vecs[i], theta);
			}
		}
		
		// Rotate around CA-C1 bond to set psi to 180 degrees. -- Added by David Arndt.
		rotateX(vecs[Np], 180+psi);
		for (i = HN+1; i <= max_atom_index; i++) {
			if (vecs[i][0] != ATOM_COORD_NA) {
				rotateX(vecs[i], 180+psi);
			}
		}
		
	}
	else {
		// Last residue. Data for N atom location is unreliable (the N atom does not exist for
		// the last residue!).
		
		// compute angle for OXT (instead of N)
		int OXT_index = -1;
		for (i=HN; i <= max_atom_index; i++) {
			if (strncmp(names[i], " OXT", 4) == 0) {
				OXT_index = i;
				break;
			}
		}
		if (OXT_index != -1) { // In all normal cases, OXT_index should have been found.
			theta = atan(vecs[OXT_index][2] / vecs[OXT_index][1]);
			theta = 180 - (theta * 180/PI);
			if (vecs[OXT_index][1] < 0) theta = 180+theta;
			
			for (i=0; i <= max_atom_index; i++) {
				if (vecs[i][0] != ATOM_COORD_NA) {
					rotateX(vecs[i], theta);
				}
			}
		}
		
		// Rotate about CA-C1 bond so that Np atom is in the plane along with the CA and C1 atoms.
		setVector(T, 0, vecs[Np][1], vecs[Np][2]); // T is CA-Np projected onto y-z plane.
		Vec3D yAxis = {0,1,0}; // a unit vector
		theta = angle(T, yAxis);
		if (vecs[Np][2] > 0) theta = 360 - theta;
		rotateX(vecs[Np], theta);
		
		for (i = HN+1; i <= max_atom_index; i++) {
			if (strncmp(names[i], " OXT", 4) != 0) { // Do not rotate the terminal O atom labeled " OXT"
				if (vecs[i][0] != ATOM_COORD_NA) {
					rotateX(vecs[i], theta);
				}
			}
		}
		
	}

	return 0.0;
}


/*
 * Reads all atoms for one residue in the given PDBFile struct, starting at
 * atom n for the residue that contains atom n. (The N and CA atoms of the
 * following residue are read as well). Sets the contents of 'atoms' to the
 * unmodified atom coordinates for each atom (arranged according to the
 * constants in pepmake.h). Sets the contents of 'templ' to the atom
 * coordinates in canonical form. Sets 'names' to the names of each
 * atom, in the same arrangement as 'atoms' and 'templ'. Sets 'numAtoms' to
 * the number of atoms read. Sets 'max_atom_index' to the maximum index in
 * 'atoms', 'templ', and 'names' with meaningful data.
 *
 * max_atom_index may optionally be NULL.
 */
void parseResidueFromPDBFile(PDBFile *pdb, int n, Vec3D *atoms,  Vec3D *templ,
		char names[MAX_ATOMS][5], int *numAtoms, int *max_atom_index) {
	
	int i;
	Atom *a;
	int iNp, iCA, iC1, iO, iN, iCA2, iHN, iHNp, iHNp2, iHNp3, iHA, iHA2, iCB, iCG;
	int res = pdb->atoms[n].resNum;
	int atom_index;
	int atom_count;
	char gamma_atom_names[7][5] = {" OG ", " OG1", " OG2", " CG ", " CG1", " CG2", " SG "};
	char found_gamma_name[5]; // Name of the first gamma atom we find, which will occupy index CG.
	int my_max_atom_index; // Memory to use in case user passed max_atom_index as NULL.
		// Note: As we calculate max_atom_index, we should have
		// (max_atom_index >= *numAtoms) and (max_atom_index < MAX_ATOMS).
	
	if (max_atom_index == NULL) {
		// User passed NULL; use my_max_atom_index in this function instead.
		max_atom_index = &my_max_atom_index;
	}
	
	// initialize
	for (i = 0; i < MAX_ATOMS; i++) {
		setVector(atoms[i], ATOM_COORD_NA, ATOM_COORD_NA, ATOM_COORD_NA);
		names[i][0] = (char) NULL;
	}
	
	iNp = getNextAtomIndex(pdb, n, " N  ", GET_ATOM_SAME_RES);
	if (iNp >= 0) {
		a = &pdb->atoms[iNp];
		strncpy(names[Np], a->name, 5);
		copyVector(a->point, atoms[Np]);
	}
	
	iCA = getNextAtomIndex(pdb, n, " CA ", GET_ATOM_SAME_RES);
	if (iCA >= 0) {
		a = &pdb->atoms[iCA];
		strncpy(names[CA], a->name, 5);
		copyVector(a->point, atoms[CA]);
	}
	
	iC1 = getNextAtomIndex(pdb, n, " C  ", GET_ATOM_SAME_RES);
	if (iC1 >= 0) {
		a = &pdb->atoms[iC1];
		strncpy(names[C1], a->name, 5);
		copyVector(a->point, atoms[C1]);
	}
	
	iO = getNextAtomIndex(pdb, n, " O  ", GET_ATOM_SAME_RES);
	if (iO >= 0) {
		a = &pdb->atoms[iO];
		strncpy(names[O], a->name, 5);
		copyVector(a->point, atoms[O]);
	}
	
	atom_count = 4;
	
	iHN = getNextAtomIndex(pdb, n, " H  ", GET_ATOM_NEXT_RES);

	if (iHN >= 0) {
		a = &pdb->atoms[iHN];
		strncpy(names[HN], a->name, 5);
		copyVector(a->point, atoms[HN]);
		atom_count++;
	}
	
	// Look for "1H  " to see if this is the first residue in the peptide.
	iHNp = getNextAtomIndex(pdb, n, "1H  ", GET_ATOM_SAME_RES);
	if (iHNp >= 0) {
		a = &pdb->atoms[iHNp];
		strncpy(names[HNp], a->name, 5);
		copyVector(a->point, atoms[HNp]);
		atom_count++;
	}
	
	iHNp2 = getNextAtomIndex(pdb, n, "2H  ", GET_ATOM_SAME_RES);
	if (iHNp2 >= 0) {
		a = &pdb->atoms[iHNp2];
		strncpy(names[HNp2], a->name, 5);
		copyVector(a->point, atoms[HNp2]);
		atom_count++;
	}
	
	iHNp3 = getNextAtomIndex(pdb, n, "3H  ", GET_ATOM_SAME_RES);
	if (iHNp3 >= 0) {
		a = &pdb->atoms[iHNp3];
		strncpy(names[HNp3], a->name, 5);
		copyVector(a->point, atoms[HNp3]);
		atom_count++;
	}
	
	iHA = getNextAtomIndex(pdb, n, " HA ", GET_ATOM_SAME_RES);
	if (iHA >= 0) {
		a = &pdb->atoms[iHA];
		strncpy(names[HA], a->name, 5);
		copyVector(a->point, atoms[HA]);
		atom_count++;
	}
	else {
		iHA = getNextAtomIndex(pdb, n, "1HA ", GET_ATOM_SAME_RES);
		if (iHA >= 0) {
			a = &pdb->atoms[iHA];
			strncpy(names[HA], a->name, 5);
			copyVector(a->point, atoms[HA]);
			atom_count++;
		}
	}
	
	// Check for 2nd HA atom (GLY only), and if it is present, copy the data into the CB position.
	iHA2 = getNextAtomIndex(pdb, n, "2HA ", GET_ATOM_SAME_RES);
	if (iHA2 >= 0) {
		a = &pdb->atoms[iHA2];
		strncpy(names[CB], a->name, 5);
		copyVector(a->point, atoms[CB]);
		atom_count++;
	}
	
	// Check for CB atom
	iCB = getNextAtomIndex(pdb, n, " CB ", GET_ATOM_SAME_RES);
	if (iCB >= 0) {
		a = &pdb->atoms[iCB];
		strncpy(names[CB], a->name, 5);
		copyVector(a->point, atoms[CB]);
		if (iHA2 < 0) { // This should always be true in proper PDB files.
			atom_count++;
		}
	}
	
	// Check for CG atom
	for (i = 0; i < 7; i++) {
		iCG = getNextAtomIndex(pdb, n, gamma_atom_names[i], GET_ATOM_SAME_RES);
		if (iCG >= 0) {
			a = &pdb->atoms[iCG];
			strncpy(names[CG], a->name, 5);
			copyVector(a->point, atoms[CG]);
			atom_count++;
			
			strncpy(found_gamma_name, a->name, 5);
			
			break;
		}
	}
	
	iN = getNextAtomIndex(pdb, n, " N  ", GET_ATOM_NEXT_RES);
	if (iN >= 0) {
		a = &pdb->atoms[iN];
		strncpy(names[N], a->name, 5);
		copyVector(a->point, atoms[N]);
		//atom_count++;
	}
	
	iCA2 = getNextAtomIndex(pdb, n, " CA ", GET_ATOM_NEXT_RES);
	if (iCA2 >= 0) {
		a = &pdb->atoms[iCA2];
		strncpy(names[CA2], a->name, 5);
		copyVector(a->point, atoms[CA2]);
		//atom_count++;
	}
	
	atom_count += 2; // For N2 and CA2, which we will always print out, even for the last residue.
	
	// side-chain (and the OXT atom if present)
	atom_index = CG + 1;
	for (i=n; i < pdb->numAtoms && pdb->atoms[i].resNum == res; i++) {
		if (i != iNp && i != iCA && i != iC1 && i != iO && i != iN && i != iCA2 &&
				i != iHNp && i != iHNp2 && i != iHNp3 && i != iHN && i != iHA && i != iHA2 &&
				i != iCB && i != iCG ) {
			a = &pdb->atoms[i];
			
			if ( (strncmp(a->name, " N  ", 4) != 0) && (strncmp(a->name, " CA ", 4) != 0) &&
					(strncmp(a->name, " C  ", 4) != 0) && (strncmp(a->name, " O  ", 4) != 0) &&
					(strncmp(a->name, " H  ", 4) != 0) && (strncmp(a->name, "1H  ", 4) != 0) &&
					(strncmp(a->name, "2H  ", 4) != 0) && (strncmp(a->name, "3H  ", 4) != 0) &&
					(strncmp(a->name, " HA ", 4) != 0) && (strncmp(a->name, "1HA ", 4) != 0) &&
					(strncmp(a->name, "2HA ", 4) != 0) && (strncmp(a->name, " CB ", 4) != 0) ) {
				
				if (strncmp(a->name, found_gamma_name, 4) != 0) {
					
                    // Increase MAX_ATOMS (100 --> 150) 
				    if (atom_index >= MAX_ATOMS) {
						fprintf(stderr, "ERROR: atom_index >= MAX_ATOMS (phipsi.c, parseResidueFromPDBFile())\n");
						fprintf(stderr, "atom_index:%d   MAX_ATOMS:%d   ResNo: %d\n", atom_index, MAX_ATOMS, res);
						exit(-1);
					}
					
					strncpy(names[atom_index], a->name, 4);
					names[atom_index][4] = (char) NULL;
					copyVector(a->point, atoms[atom_index]);
					atom_index++;
					atom_count++;
				}
			}
		}
	}
	
	if (numAtoms != NULL) {
		*numAtoms = atom_count;
	}
	
	*max_atom_index = 0;
	for (i = 0; i < MAX_ATOMS; i++) {
		if (atoms[i][0] != ATOM_COORD_NA) {
			*max_atom_index = i;
		}
		copyVector(atoms[i], templ[i]);
	}
	
	int lastResidue = (iN < 0);
	canonicalForm(templ, names, *max_atom_index, lastResidue); // do rotations of current coordinates

	// set any unfound atoms to defaults
	// Note by David Arndt: It appears that the default N and CA2 coordinates are used for the final residue of a peptide.
	if (iNp < 0)	{
		//printf("*** using default Np\n");
		strncpy(names[Np], " N  ", 5);
		setVector(templ[Np], -0.496989, 1.405916, 0.000000); // Added by David Arndt based on 3TRX.
		//setVector(templ[Np], ATOM_COORD_NA, ATOM_COORD_NA, ATOM_COORD_NA);
	}
	if (iCA < 0) {
		//printf("*** using default CA\n");
		strncpy(names[CA], " CA ", 5);
		setVector(templ[CA], 0.0, 0.0, 0.0);
	}
	if (iC1 < 0) {
		//printf("*** using default C1\n");
		strncpy(names[C1], " C  ", 5);
		setVector(templ[C1], 1.53, 0.0, 0.0);
	}
	if (iO < 0)	{
		//printf("*** using default O\n");
		strncpy(names[O], " O  ", 5);
		setVector(templ[O], 2.1686, 1.0629, 0.0);
	}
	if (iN < 0)	{
		//printf("*** using default N\n");
		strncpy(names[N], " N  ", 5);
		setVector(templ[N], 2.0669, -1.2059, 0.0);
		//setVector(templ[N], ATOM_COORD_NA, ATOM_COORD_NA, ATOM_COORD_NA);
	}
	if (iCA2 < 0) {
		//printf("*** using default CA2\n");
		strncpy(names[CA2], " CA ", 5);
		setVector(templ[CA2], 3.5188, -1.4359, 0.0);
		//setVector(vecs[CA2], ATOM_COORD_NA, ATOM_COORD_NA, ATOM_COORD_NA);
	}
	
	// Set HN if no HN atom was found in the PDB file.
	/*
	if (iHN < 0) {
		strncpy(names[HN], " H  ", 5);
		setVector(vecs[HN], 1.52, -2.04, 0.0);
	}
	*/
	
	// If any of HNp, HNp2, HNp3 are not present in this residue, set them to
	// (ATOM_COORD_NA, ATOM_COORD_NA, ATOM_COORD_NA).
	if (iHNp < 0) {
		setVector(templ[HNp], ATOM_COORD_NA, ATOM_COORD_NA, ATOM_COORD_NA);
	}
	if (iHNp2 < 0) {
		setVector(templ[HNp2], ATOM_COORD_NA, ATOM_COORD_NA, ATOM_COORD_NA);
	}
	if (iHNp3 < 0) {
		setVector(templ[HNp3], ATOM_COORD_NA, ATOM_COORD_NA, ATOM_COORD_NA);
	}
	
	// Likewise for HA.
	if (iHA < 0) {
		setVector(templ[HA], ATOM_COORD_NA, ATOM_COORD_NA, ATOM_COORD_NA);
	}
	
}



/*	extra code for outputting side chain coordinates: */
static void printResidueCoordinates(char *name, double angles[4], double N_CA_C1_angle,
		Vec3D *template, char names[MAX_ATOMS][5], int numAtoms) {
	int i, j;
	printf("%s \t %lf \t %lf \t %d\n", name, angles[3], N_CA_C1_angle, numAtoms); // residue name, chi angle, N-CA-C1_angle, number of atoms in residue
	
	j = 0;
	i = 0;
	while (j < numAtoms) {
		//printf("%d\n", i);
		//if (i ==6){
		//	printf("H line %s  %11.6f %11.6f %11.6f\n", names[i], template[i][0], template[i][1], template[i][2]);
		//}
		if (template[i][0] != ATOM_COORD_NA) {
			printf(" %4s %11.6f %11.6f %11.6f\n", 
			names[i], template[i][0], template[i][1], template[i][2]);
			j++;
		}
		i++;
	}
}



/*************************************************************/
/* Print the actual line of output for a residue.            */
/*************************************************************/
static void printResidue(char *name, int num, double angles[4], 
					Vec3D *template, char names[MAX_ATOMS][5], int numAtoms) {
	printf("%4d  %s  %11.6lf %11.6lf %11.6lf %11.6lf \t%d\n", 	
				num, name, angles[0], angles[1], angles[2], angles[3], numAtoms);
}



/*************************************************************/
/* get the next atom in the backbone                         */
/*************************************************************/
static int getNextBackboneAtom(int n, PDBFile *pdb) {
	int i;
	for (i=n; i<pdb->numAtoms; i++)	{
		if (n == 0) {
			/* start backbone on an N only */
			if ( (pdb->atoms[i].type == N_TYPE) ) {
				return i;
			}
		} else {
			int oldtype = pdb->atoms[n-1].type;
			int curtype = pdb->atoms[i].type;
			if (curtype != oldtype) {
				if ((curtype == CA_TYPE) || (curtype == C_TYPE) || (curtype == N_TYPE)) {
					return i;
				}
			}
		}
   	}
	return -1;
}


/*************************************************************/
/* Get the next atom after index n, of the given type.       */
/* Returns -1 if none found.                                 */
/*************************************************************/
static int getAtomOfType(int n, PDBFile *pdb, int type) {
	int resNum = pdb->atoms[n].resNum;	
	while (pdb->atoms[n].resNum == resNum) {
		if (pdb->atoms[n].type == type) return n;
		if (++n >= pdb->numAtoms) return -1;
	}
	return -1;
}


/*************************************************************/
/* Compute & print the dihedral angles of the PDB file       */
/*************************************************************/
static int dihedralAngles(PDBFile *pdb, int makeDB) {
	double angles[8] = {0, 0, 0, 0}; // phi, psi, omega, chi
	double N_CA_C1_angle = 0;
	Vec3D atom_coords[MAX_ATOMS]; // Passed to parseResidueFromPDBFile, but not used thereafter in this function.
	Vec3D template[MAX_ATOMS];
	char names[MAX_ATOMS][5];
	int numAtoms = 0;
	int atoms[4];
	int a=1, n=0, count=0;
	
	/* Read in the first 4 atoms into our rotating array.
      We start at the first N, to compute PSI */
	atoms[0] = getNextBackboneAtom(0, pdb);
	atoms[1] = getNextBackboneAtom(atoms[0]+1, pdb);
	atoms[2] = getNextBackboneAtom(atoms[1]+1, pdb);
	atoms[3] = getNextBackboneAtom(atoms[2]+1, pdb);
	
	if (atoms[0] == -1) {
		fprintf(stderr, "phipsi: ERROR: No usable residues in PDB file.\n"); exit(1);
	}

	while (atoms[n] >= 0 && atoms[(n+2)%4] >= 0) {
		
		if (atoms[(n+3)%4] >= 0) {
			angles[a] = torsionAtom(
					&pdb->atoms[atoms[n]], 
					&pdb->atoms[atoms[(n+1)%4]],
					&pdb->atoms[atoms[(n+2)%4]], 
					&pdb->atoms[atoms[(n+3)%4]] );
		}
		else {
			// We are on the last residue, and the last N atom is not available in the PDB file.
			// This N would be assigned to the next residue (which does not exist) in the PDB file,
			// but in the residues we're outputting with the function printResidueCoordinates have the
			// boundaries between residues at different locations, starting with CA. So for the final
			// residue, we need to supply an N and CA2 (2nd C-alpha) that are not given in the PDB file.
			
			// Use the OXT atom to measure psi (we assume here that a == 1), only for the final residue.
			int OXT_index = getAtomOfType(atoms[n], pdb, OXT_TYPE);
			if (OXT_index >= 0) {
				angles[a] = torsionAtom(
						&pdb->atoms[atoms[n]],
						&pdb->atoms[atoms[(n+1)%4]],
						&pdb->atoms[atoms[(n+2)%4]],
						&pdb->atoms[OXT_index] );
			}
			else {
				// Final residue does not have an OXT atom listed in the PDB file.
				// Use the location of the O atom to measure psi.
								
				int O_index = getAtomOfType(atoms[n], pdb, O_TYPE);
				angles[a] = torsionAtom(
						&pdb->atoms[atoms[n]],
						&pdb->atoms[atoms[(n+1)%4]],
						&pdb->atoms[atoms[(n+2)%4]],
						&pdb->atoms[O_index] );
				if (angles[a] > 0.0) {
					angles[a] -= 180.0;
				}
				else {
					angles[a] += 180.0;
				}
			}
		}
		
		// compute N's chi angle
		if (pdb->atoms[atoms[n]].type == N_TYPE) { // (implies also that a == 1)
			// compute chi latte angle:
			int cbi = getAtomOfType(atoms[n], pdb, CB_TYPE);
			int cgi = getAtomOfType(atoms[n], pdb, CG_TYPE);
			if (cbi > 0 && cgi > 0) {
				angles[3] = torsionAtom(&pdb->atoms[atoms[n]],
							            &pdb->atoms[atoms[(n+1)%4]],
											&pdb->atoms[cbi], &pdb->atoms[cgi]);
			} else {
				angles[3] = 360;
			}
		}
		
		// Compute the N-CA-C1 angle that spans two residues. This angle should be around 111 degrees
		// but may deviate by +/- 5 degrees or more.
		if (pdb->atoms[atoms[n]].type == N_TYPE) { // (implies also that a == 1)
			Vec3D N_vec, CA_vec, C1_vec;
			copyVector((&pdb->atoms[atoms[n]])->point, N_vec);
			copyVector((&pdb->atoms[atoms[(n+1)%4]])->point, CA_vec);
			copyVector((&pdb->atoms[atoms[(n+2)%4]])->point, C1_vec);
			
			//printf("N is located at (%lf, %lf, %lf)\n", N_vec[0], N_vec[1], N_vec[2]);
			//printf("CA is located at (%lf, %lf, %lf)\n", CA_vec[0], CA_vec[1], CA_vec[2]);
			//printf("C1 is located at (%lf, %lf, %lf)\n", C1_vec[0], C1_vec[1], C1_vec[2]);
			//printf("N's residue: %s\n", pdb->atoms[atoms[n]].resName); // In PDB file.
			//printf("CA's residue: %s\n", pdb->atoms[atoms[(n+1)%4]].resName);
			//printf("C1's residue: %s\n", pdb->atoms[atoms[(n+2)%4]].resName);
			
			Vec3D CA_N, CA_C1;
			difference(N_vec, CA_vec, CA_N);
			difference(C1_vec, CA_vec, CA_C1);
			N_CA_C1_angle = angle(CA_N, CA_C1);
			
			//printf("N_CA_C1_angle is %lf\n", N_CA_C1_angle);
		}
		
		if (a == 1) {
			// Note by David Arndt: atoms[n] should be the index (atom index in PDB file) of an N atom.
			parseResidueFromPDBFile(pdb, atoms[n], atom_coords, template, names, &numAtoms, NULL);
		}
		
		if (a == 2) { /* last angle */
			// Note by David Arndt: atoms[n] should be the index (atom index in PDB file) of a CA atom.
			// pdb->atoms[atoms[n]].resName should be the name of the residue containing this CA atom.
			
			if (makeDB) {
				printResidueCoordinates(pdb->atoms[atoms[n]].resName, angles, N_CA_C1_angle, template, names, numAtoms);
				count++;
			} else {
				printResidue(pdb->atoms[atoms[n]].resName, pdb->atoms[atoms[n]].resNum, angles, template, names, numAtoms);
				count++;
			}
			angles[0] = angles[1] = angles[2] = 0;
		}
		atoms[n] = getNextBackboneAtom(atoms[(n+3)%4]+1, pdb);
		
		n = (n+1)%4;  /* rotate the pointer */
		a = (a+1)%3;  /* the angle we will read next */ 
	}

	if (a < 3 && a > 0) {
		if (makeDB) {
			printResidueCoordinates(pdb->atoms[atoms[n]].resName, angles, N_CA_C1_angle, template, names, numAtoms);
			count++;
		} else {
			printResidue(pdb->atoms[atoms[n]].resName, pdb->atoms[atoms[n]].resNum, angles, template, names, numAtoms);
			count++;
		}
	}

	return 0;
}


/*************************************************************/
/* The main entry point                                      */
/*************************************************************/
int main_phipsi(int argc, char **argv) {
	char chain = '?'; // read first chain found
	int db = FALSE;
 	char *input;
	PDBFile pdb;
	int i, file_exists;
	
	if (argc < 2) {
		printf("Usage:\n   phipsi [--all_chains] [--db] file.pdb\n");
		printf("Options:\n");
		printf(" --all_chains\n\t Process all chains in the pdb file\n");
		printf(" --db\n\t Print out residue template for the database\n");
		return 0;
	}
	
	for (i=1; i<argc-1; i++) {
		if (argc >= 3) {
			if (strcmp(argv[i], "--all_chains") == 0) {
				chain = '*'; // read all chains
			} else if (strcmp(argv[i], "--db") == 0) {
				db = TRUE;
			}
		}
	}
	
	input = argv[argc-1];

	file_exists = parsePDBFile(input, &pdb, chain);
	if(file_exists == ERROR_FLAG){
		printf("Could not open %s for parsing! Exiting...\n",input);exit(-1);
	}
	
	dihedralAngles(&pdb, db);
	freePDBFile(&pdb);
	return 0;
}

