#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <ctype.h>
#include <unistd.h>

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

static void usage();
static void check_add_HD1_in_HIS(ResData *res );
static void check_add_1H_in_first_res(ResData * res);
static void check_add_HE2_in_GLU(ResData *res);
static void check_add_HD2_in_ASP(ResData *res);
static void check_add_HE_in_GLN(ResData *res );
static double get_chi2_w_coor(ResData *res);
static double get_chi3_w_coor(ResData *res);
// static double get_chi4_w_coor(ResData *res);
// static double get_chi5_w_coor(ResData *res);
static void get_theta_score(ResData *res, int numRes);
static void print_angles(ResData *res, int numRes, char *inFname);
static void get_chi_angles(ResData *res, int n);
static void call_reduce(char *input_PDB_filename, char *reduced_PDB_filename, int trim_PDB_file_first, char *reduce_dir);
static void PDBtoResData(PDBFile pdb, ResData *res);
static void check_add_HN_in_single_res(ResData *res );
static void compute_angles(ResData *res, int n);
static int find_atom_index_in_res(ResData * res, int *atom_index, char atom_name[5]);
static double get_kappa_angle(ResData * res_accp, ResData *res_donor);
static void turn_struct(Vec3D ang_vec1, Vec3D ang_vec2, Vec3D O_C1, Vec3D O_N, Vec3D O_CA, Vec3D O_H);


static void usage() {
     printf("Usage: ./get_angles <Reduce file Dir> <PDB file> <reduced-flag:1/0]\n");
     exit(1);
}

int g_print_theta=1;

#define MODE_DEBUG 0

int main_get_angles(int argc, char *argv[]){
	int n = 0; // number of residues in the peptide
	char *PDB_filename, *inFname;
	char trimmed_pdb_file[MAX_FNAME_LEN], reduced_pdb_file[MAX_FNAME_LEN];

	ResData *res;
	PDBFile pdb;
	int i; 
	char hydroname[256];

	if(argc<3) usage();
	
	// Test to see whether the files exist.
	check_file_readability(argv[1]);
	inFname = argv[2]; // use for result file name

	//Call reduce to add hydrogen atoms to the structure, or to adjust them.
    if( argc == 4 && !strcmp(argv[3], "0")) {
	    PDB_filename = argv[2]; // no use reduce program
    } else {
	    call_reduce(argv[2], hydroname, TRUE, argv[1]);
	    PDB_filename = hydroname;
    } 
		
	int rc;
	rc = parsePDBFile(PDB_filename,&pdb, '?');
	if (rc != TRUE) {
       fprintf(stderr, "Error (%d): Get Aangle program could not parse PDB file %s.\n", rc, PDB_filename);
		exit(-1);
	}
	
	n=pdb.numRes; // Both values are the number of residues in the peptide.

	res=(ResData *)malloc(sizeof(ResData) * n);
	assert(res!=NULL);
	
	PDBtoResData(pdb, res);
  	for (i = 0; i <n ; i++){
		res[i].theta_donar = 0.0;
		res[i].theta_recp = 0.0;
		res[i].kappa_donar = 0.0;
		res[i].kappa_recp = 0.0;
		res[i].theta = 0.0;
		res[i].kappa = 0.0;
		check_add_HN_in_single_res(&res[i]);
		if(strcmp(res[i].resName, "HIS")==0) {
			check_add_HD1_in_HIS(&res[i]); 
		}
		if(strcmp(res[i].resName, "GLN")==0) {
			check_add_HE_in_GLN(&res[i]);  
		}
		if(strcmp(res[i].resName, "ASP")==0) {
			check_add_HD2_in_ASP(&res[i]);
		}
		if(strcmp(res[i].resName, "GLU")==0) {
			check_add_HE2_in_GLU(&res[i]);
		}
		if (i==0) {// first res
			check_add_1H_in_first_res(&res[i]);
		}
	}

	// now we are gonna calculate theta angle scores: mean and deviation.
	get_theta_score(res, n); 
	get_chi_angles(res, n);

	print_angles(res, n, inFname);
	free(res);

	sprintf(trimmed_pdb_file, "%s_trim", argv[2]);
	sprintf(reduced_pdb_file, "%sH", argv[2]);

    unlink(trimmed_pdb_file);
    unlink(reduced_pdb_file);

	return 0;
}


/* print angles from res */
static void print_angles(ResData *res, int numRes, char *inFname){
	int i;
    char resultFname[MAX_FNAME_LEN];

    FILE *fp;

    strcpy(resultFname, inFname);
    strcat(resultFname, ".angles");
    fp = fopen(resultFname, "w");

	fprintf(fp,"NUM,ResName,phi,psi,omega,chi,chi2,chi3,theta_donor,kappa_donor,theta_recp,kappa_recp,");
	fprintf(fp,"phi_p,psi_p,omega_p,chi_p,phi_n,psi_n,omega_n,chi_n,phi_p2,psi_p2,omega_p2,chi_p2,phi_n2,psi_n2,omega_n2,chi_n2,theta,kappa\n");

	for (i=0; i<numRes; i++){
		if (res[i].phi  == -1000000.00) res[i].phi  = 0.0;
		if (res[i].psi  == -1000000.00) res[i].psi  = 0.0;
		if (res[i].omega== -1000000.00) res[i].omega= 0.0;
		if (res[i].chi  == -1000000.00) res[i].chi  = 0.0;
		if (res[i].chi2 == -1000000.00) res[i].chi2 = 0.0;
		if (res[i].chi3 == -1000000.00) res[i].chi3 = 0.0;
		if (isnan(res[i].phi  ) )  res[i].phi   = 0.0;
		if (isnan(res[i].psi  ) )  res[i].psi   = 0.0;
		if (isnan(res[i].omega) )  res[i].omega = 0.0;
		if (isnan(res[i].chi  ) )  res[i].chi   = 0.0;
		if (isnan(res[i].chi2 ) )  res[i].chi2  = 0.0;
		if (isnan(res[i].chi3 ) )  res[i].chi3  = 0.0;

		// if (res[i].chi4==-1000000.00) res[i].chi4=0.0;
		// if (res[i].chi5==-1000000.00) res[i].chi5=0.0;
		/*
                   printf("%8d%10s%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%13.3f%13.3f%13.3f%13.3f\n",
                          res[i].id, res[i].resName, res[i].phi, res[i].psi, res[i].omega, res[i].chi, res[i].chi2, res[i].chi3, res[i].chi4, res[i].chi5,
                          res[i].theta_donar, res[i].kappa_donar, res[i].theta_recp, res[i].kappa_recp );
                */

		if (isnan(res[i].theta_donar) ) res[i].theta_donar = 0.0;
		if (isnan(res[i].theta_recp ) ) res[i].theta_recp  = 0.0;
		if (isnan(res[i].kappa_donar) ) res[i].kappa_donar = 0.0;
		if (isnan(res[i].kappa_recp ) ) res[i].kappa_recp  = 0.0;

		if (isnan(res[i].theta) ) res[i].theta = 0.0;
		if (isnan(res[i].kappa) ) res[i].kappa = 0.0;
        }

	for (i=0; i<numRes; i++){
        /*
		printf("%d,%s,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,",
		        res[i].id, res[i].resName, res[i].phi, res[i].psi, res[i].omega, res[i].chi, 
			res[i].chi2, res[i].chi3, res[i].theta_donar, res[i].kappa_donar, res[i].theta_recp, res[i].kappa_recp );
        */

		fprintf(fp,"%d,%s,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,%.3f,",
		        res[i].id, res[i].resName, res[i].phi, res[i].psi, res[i].omega, res[i].chi, 
			    res[i].chi2, res[i].chi3, res[i].theta_donar, res[i].kappa_donar, res[i].theta_recp, res[i].kappa_recp );

		// i-1th angles 
        if (i > 0) {
		   fprintf(fp,"%.3f,%.3f,%.3f,%.3f,", res[i-1].phi, res[i-1].psi, res[i-1].omega, res[i-1].chi); 
        } else {
		   fprintf(fp,"%.3f,%.3f,%.3f,%.3f,", 0.0, 0.0, 0.0, 0.0 );
		}

		// i+1th angles 
        if (i < numRes) {
		   fprintf(fp,"%.3f,%.3f,%.3f,%.3f,", res[i+1].phi, res[i+1].psi, res[i+1].omega, res[i+1].chi); 
        } else {
		   fprintf(fp,"%.3f,%.3f,%.3f,%.3f,", 0.0, 0.0, 0.0, 0.0 );
		}

		// i-2th angles 
        if (i > 1) {
		   fprintf(fp,"%.3f,%.3f,%.3f,%.3f,", res[i-2].phi, res[i-2].psi, res[i-2].omega, res[i-2].chi); 
        } else {
		   fprintf(fp,"%.3f,%.3f,%.3f,%.3f,", 0.0, 0.0, 0.0, 0.0 );
		}

		// i+2th angles 
        if (i < numRes-1) {
		   fprintf(fp,"%.3f,%.3f,%.3f,%.3f,", res[i+2].phi, res[i+2].psi, res[i+2].omega, res[i+2].chi); 
        } else {
		   fprintf(fp,"%.3f,%.3f,%.3f,%.3f,", 0.0, 0.0, 0.0, 0.0 );
		}

        // new append theta & kappa 
	    fprintf(fp,"%.3f,%.3f\n", res[i].theta, res[i].kappa); 

	}	
}

/*
 * Call the program 'reduce' to add or reposition hydrogens on the given PDB file
 * 'input_PDB_filename'.
 *
 * If trim_PDB_file is TRUE, this function opens the PDB file and writes a trimmed PDB
 * file with only the ATOM lines from the first chain or model in the PDB file, since
 * reduce (v3.03 or v3.10 at least) takes a long time to add hydrogens to all models
 * in an NMR ensemble. The trimmed PDB file also omits all hydrogen atoms, in order to
 * solve a problem with reduce and hydrogen atom nomenclature. The trimmed PDB file is
 * then passed to reduce. The name of the reduced PDB file is written to the parameter
 * 'reduced_PDB_filename'.
 *
 * For info about reduce, see http://kinemage.biochem.duke.edu/software/reduce.php and
 * Word, et al.(1999) "Asparagine and glutamine: using hydrogen atom contacts in the choice
 * of sidechain amide orientation" J. Mol. Biol. 285, 1733-1745.
 *
 * input_PDB_filename: Name of input PDB file.
 * reduced_PDB_filename: Pointer to memory to which to write the name of the reduced PDB
 *    file.
 * trim_PDB_file_first: Boolean. Whether to trim the PDB file first.
 */
static void call_reduce(char *input_PDB_filename, char *reduced_PDB_filename, int trim_PDB_file_first, char *reduce_dir) {
	char trimmed_PDB_filename[512];
	char command[512];
	
	sprintf(trimmed_PDB_filename, "%s_trim", input_PDB_filename);
	sprintf(reduced_PDB_filename, "%sH", input_PDB_filename);
	
	// Open the PDB file and write a trimmed PDB file with only the first chain or model
	// and without hydrogen atoms. This is a simple way to fix the problem where reduce
	// v3.10 reads in PDB file with hydrogen atoms named according to one version of PDB
	// nomenclature, and adds duplicate hydrogens with different atom nomenclature.
	
	if (trim_PDB_file_first) {
		trim_PDB_file(input_PDB_filename, trimmed_PDB_filename, TRUE);
	}
	else {
		sprintf(trimmed_PDB_filename, "%s", input_PDB_filename);
	}
	
	// Call reduce
	
	// sprintf(command, "%s/reduce  -Quiet -OLDpdb -DB %s/reduce_wwPDB_het_dict.txt %s > %s",
        // append -FLIPs option
	sprintf(command, "%s/reduce  -Quiet -OLDpdb  -FLIPs -DB %s/reduce_wwPDB_het_dict.txt %s > %s",
		reduce_dir, reduce_dir, trimmed_PDB_filename, reduced_PDB_filename);
	system(command);
}


/*
 * Given a PDBFile struct from a parsed PDB file, this function loads the structure
 * data into the given array of pdb.numRes ResData structs. Sets the IDs, residues
 * names, 'atom', 'names' and 'template' arrays, and max_atom_index and fixed_position
 * values. Computes and sets the angles as well. If there are any missing atoms, the
 * angles that depend on them will be set to ANGLE_NA.
 */
static void PDBtoResData(PDBFile pdb, ResData *res) {
	
	int i, j;
	int resNumOfPreviousAtom = -1000000, resIndex = -1;
	
	// Initialize ResData atom locations and fixed_position member variables.
	for (i = 0; i < pdb.numRes; i++) {
		for (j = 0; j < MAX_ATOMS; j++) {
			setVector(res[i].atoms[j], ATOM_COORD_NA, ATOM_COORD_NA, ATOM_COORD_NA);
		}
		res[i].fixed_position = FALSE;
	}
	
	for (i = 0; i < pdb.numAtoms; i++) {

		if (pdb.atoms[i].resNum != resNumOfPreviousAtom) {
			resNumOfPreviousAtom = pdb.atoms[i].resNum;
			resIndex++;
			
			res[resIndex].id = pdb.atoms[i].resNum;
            
            // add to print original residue number 2011.08.24
			res[resIndex].origResNum = pdb.atoms[i].origResNum;
			res[resIndex].insCode = pdb.atoms[i].insCode;

			strcpy(res[resIndex].resName, pdb.atoms[i].resName);
			
			parseResidueFromPDBFile(&pdb, i, res[resIndex].atoms, res[resIndex].template,
				res[resIndex].names, NULL, &(res[resIndex].max_atom_index) );
			
			// Set fixed_position member variable. If the current residue is the first
			// residue in the peptide, or if it is the first residue after a gap in the
			// structure, then it should have a fixed position.
			
			if (resIndex == 0) {
				res[resIndex].fixed_position = TRUE;
				
				// debug
				//fprintf(stderr, "Residue %3s %d is fixed because it is the first residue.\n",
				//	res[resIndex].resName, res[resIndex].id);
			}
			else{
				
				if (	res[resIndex-1].atoms[CA][0] == ATOM_COORD_NA ||
						res[resIndex-1].atoms[C1][0] == ATOM_COORD_NA ||
						res[resIndex-1].atoms[N][0] == ATOM_COORD_NA  ) {
					res[resIndex].fixed_position = TRUE;
					
					// debug
					//fprintf(stderr, "Residue %3s %d is fixed due to previous residue missing atoms.\n",
					//	res[resIndex].resName, res[resIndex].id);
				}
				else if (res[resIndex].atoms[CA][0] != ATOM_COORD_NA) {
					if (distance(res[resIndex-1].atoms[CA], res[resIndex].atoms[CA]) > CA_CA_GAP_THRESHOLD) {
						res[resIndex].fixed_position = TRUE;
						
						// debug
						//fprintf(stderr, "Residue %3s %d is fixed due to CA-CA distance.\n",
						//	res[resIndex].resName, res[resIndex].id);
					}
				}
				
			}
			
			// debug
			//if (res[resIndex].fixed_position == TRUE) {
			//	fprintf(stderr, "Residue %3s %d is fixed.\n", res[resIndex].resName, res[resIndex].id);
			//}
			
		}
		
	}
	compute_angles(res, resIndex+1); // Computes the phi, psi, omega, chi, and N-CA-C1 angles
}

/*
 * Computes the phi, psi, omega, chi, and N-CA-C1 angles for the given ResData
 * array of size n, based on the coordinates in the 'atom' member variables of
 * each ResData struct. If there are any missing atoms, the angles that
 * depend on them will be set to ANGLE_NA.
 */
static void compute_angles(ResData *res, int n) {
	int i, j;
	Vec3D CA_N, CA_C1;
	
	for (i = 0; i < n; i++) {
			
			// phi
			if (i == 0) {
				res[i].phi = ANGLE_NA;
			}
			else {
				if (	res[i-1].atoms[C1][0] != ATOM_COORD_NA &&
						res[i-1].atoms[N][0] != ATOM_COORD_NA &&
						res[i].atoms[CA][0] != ATOM_COORD_NA &&
						res[i].atoms[C1][0] != ATOM_COORD_NA) {
					res[i].phi = torsion(res[i-1].atoms[C1], res[i-1].atoms[N],
						res[i].atoms[CA], res[i].atoms[C1]);
				}
				else {
					res[i].phi = ANGLE_NA;
				}
			}
			
			// psi
			if (i == 0) {
				if (	res[i].atoms[Np][0] != ATOM_COORD_NA &&
						res[i].atoms[CA][0] != ATOM_COORD_NA &&
						res[i].atoms[C1][0] != ATOM_COORD_NA &&
						res[i].atoms[N][0] != ATOM_COORD_NA) {
					res[i].psi = torsion(res[i].atoms[Np], res[i].atoms[CA],
						res[i].atoms[C1], res[i].atoms[N]);
				}
				else {
					res[i].psi = ANGLE_NA;
				}
			}
			else if (i == n-1) {
				// Last residue.
				
				// Look for " OXT" atom in last residue.
				int OXT_index = -1;
				for (j = HN; j <= res[i].max_atom_index; j++) {
					if (strncmp(res[i].names[j], " OXT", 4) == 0) {
						OXT_index = j;
					}
				}
				
				if (OXT_index >= 0) {
					// Use the OXT atom to measure psi, only for the final residue.
					if (	res[i-1].atoms[N][0] != ATOM_COORD_NA &&
							res[i].atoms[CA][0] != ATOM_COORD_NA &&
							res[i].atoms[C1][0] != ATOM_COORD_NA &&
							res[i].atoms[OXT_index][0] != ATOM_COORD_NA) {
						res[i].psi = torsion(res[i-1].atoms[N], res[i].atoms[CA],
							res[i].atoms[C1], res[i].atoms[OXT_index]);
					}
					else {
						res[i].psi = ANGLE_NA;
					}
				}
				else {
					// Final residue does not have an OXT atom listed in the PDB file.
					// Use the location of the O atom to measure psi.
					if (	res[i-1].atoms[N][0] != ATOM_COORD_NA &&
							res[i].atoms[CA][0] != ATOM_COORD_NA &&
							res[i].atoms[C1][0] != ATOM_COORD_NA &&
							res[i].atoms[O][0] != ATOM_COORD_NA) {
						res[i].psi = torsion(res[i-1].atoms[N], res[i].atoms[CA],
							res[i].atoms[C1], res[i].atoms[O]);
						
						if (res[i].psi > 0.0) {
							res[i].psi -= 180.0;
						}
						else {
							res[i].psi += 180.0;
						}
					}
					else {
						res[i].psi = ANGLE_NA;
					}
				}
			}
			else {
				if (	res[i-1].atoms[N][0] != ATOM_COORD_NA &&
						res[i].atoms[CA][0] != ATOM_COORD_NA &&
						res[i].atoms[C1][0] != ATOM_COORD_NA &&
						res[i].atoms[N][0] != ATOM_COORD_NA) {
					res[i].psi = torsion(res[i-1].atoms[N], res[i].atoms[CA],
						res[i].atoms[C1], res[i].atoms[N]);
				}
				else if (	res[i].atoms[Np][0] != ATOM_COORD_NA &&
						res[i].atoms[CA][0] != ATOM_COORD_NA &&
						res[i].atoms[C1][0] != ATOM_COORD_NA &&
						res[i].atoms[N][0] != ATOM_COORD_NA) {
					res[i].psi = torsion(res[i].atoms[Np], res[i].atoms[CA],
						res[i].atoms[C1], res[i].atoms[N]);
				}
				else {
					res[i].psi = ANGLE_NA;
				}
			}
			
			// omega
			if (i < n-1) {
				if (	res[i].atoms[CA][0] != ATOM_COORD_NA &&
						res[i].atoms[C1][0] != ATOM_COORD_NA &&
						res[i].atoms[N][0] != ATOM_COORD_NA &&
						res[i].atoms[CA2][0] != ATOM_COORD_NA) {
					res[i].omega = torsion(res[i].atoms[CA], res[i].atoms[C1],
						res[i].atoms[N], res[i].atoms[CA2]);
				}
				else {
					res[i].omega = ANGLE_NA;
				}
			}
			
			// chi
			if (strcmp(res[i].resName, "GLY") != 0 && strcmp(res[i].resName, "ALA") != 0) {
				if (i == 0) {
					if (	res[i].atoms[Np][0] != ATOM_COORD_NA &&
							res[i].atoms[CA][0] != ATOM_COORD_NA &&
							res[i].atoms[CB][0] != ATOM_COORD_NA &&
							res[i].atoms[CG][0] != ATOM_COORD_NA) {
						res[i].chi = torsion(res[i].atoms[Np], res[i].atoms[CA],
							res[i].atoms[CB], res[i].atoms[CG]);
					}
					else {
						res[i].chi = ANGLE_NA;
					}
				}
				else {
					if (	res[i-1].atoms[N][0] != ATOM_COORD_NA &&
							res[i].atoms[CA][0] != ATOM_COORD_NA &&
							res[i].atoms[CB][0] != ATOM_COORD_NA &&
							res[i].atoms[CG][0] != ATOM_COORD_NA) {
						res[i].chi = torsion(res[i-1].atoms[N], res[i].atoms[CA],
							res[i].atoms[CB], res[i].atoms[CG]);
					}
					else {
						res[i].chi = ANGLE_NA;
					}
				}
			}
			else {
				res[i].chi = ANGLE_NA;
			}
			
			// N-CA-C1 angle
			if (i == 0) {
				
				if (	res[i].atoms[Np][0] != ATOM_COORD_NA &&
						res[i].atoms[CA][0] != ATOM_COORD_NA &&
						res[i].atoms[C1][0] != ATOM_COORD_NA  ) {
					
					difference(res[i].atoms[Np], res[i].atoms[CA], CA_N);
					difference(res[i].atoms[C1], res[i].atoms[CA], CA_C1);
					res[i].N_CA_C1_angle = angle(CA_N, CA_C1);
				}
				else {
					res[i].N_CA_C1_angle = ANGLE_NA;
				}
			}
			else {
				
				if (	res[i-1].atoms[N][0] != ATOM_COORD_NA &&
						res[i].atoms[CA][0] != ATOM_COORD_NA &&
						res[i].atoms[C1][0] != ATOM_COORD_NA) {
					
					difference(res[i-1].atoms[N], res[i].atoms[CA], CA_N);
					difference(res[i].atoms[C1], res[i].atoms[CA], CA_C1);
					res[i].N_CA_C1_angle = angle(CA_N, CA_C1);
				}
				else {
					res[i].N_CA_C1_angle = ANGLE_NA;
				}
			}
			
			// debug
			//fprintf(stderr, "%3s %d:\t%lf\t%lf\t%lf\t%lf\n",
			//	res[i].resName, res[i].id, res[i].phi, res[i].psi, res[i].omega, res[i].chi);
	}
}

//***************************************************************
// this function will check HD1 atom in HIS. if not there, add 
// the coordinates and atom name into res.
// ResData * res:  'HIS'
//**************************************************************
static void check_add_HD1_in_HIS(ResData *res ){
	// not there, then
	Vec3D unit_vec;
	Vec3D HD1_vec;
	Vec3D ND1_vec;
	Vec3D CG_vec;
	Vec3D CE1_vec;
	Vec3D ND1_CG;
	Vec3D ND1_CE1;
	int i;

	for(i = 0; i <MAX_ATOMS; i++){
		if(strcmp(res->names[i], " HD1")==0){// found it 
			if (res->atoms[i][0] >-100000.00){
				return;
			}else{
				strcmp(res->names[i], "");
				res->max_atom_index--;
			}
		}
	}
	for(i = 0; i <MAX_ATOMS; i++){
		if(strcmp(res->names[i], " ND1")==0){
			copyVector(res->atoms[i], ND1_vec);
		}
		if(strcmp(res->names[i], " CE1")==0){
			copyVector(res->atoms[i], CE1_vec);
		}
		if(strcmp(res->names[i], " CG ")==0){
			copyVector(res->atoms[i], CG_vec);
		}
	}
	difference(CG_vec, ND1_vec, ND1_CG);
	difference(CE1_vec, ND1_vec, ND1_CE1);
	unitVector(ND1_CE1, ND1_CE1);
	rotateCC(ND1_CE1, ND1_CG, 120.00);
	//N_CG is what we want
	copyVector(ND1_CG, HD1_vec);
	unitVector(HD1_vec, unit_vec);
	copyVector(unit_vec, HD1_vec);
	summation(HD1_vec, ND1_vec, HD1_vec);
	// now we get HN vec;
	res->max_atom_index++;
	if(res->max_atom_index ==MAX_ATOMS) res->max_atom_index = MAX_ATOMS -1;
	strcpy(res->names[res->max_atom_index], " HD1");
	copyVector(HD1_vec, res->atoms[res->max_atom_index]);
	//printf("   --- we are here\n");	
}


//***************************************************************
// this function will check HD1 atom in HIS. if not there, add 
// the coordinates and atom name into res.
// ResData * res:  'GLN'
//**************************************************************
static void check_add_HE_in_GLN(ResData *res ){
	// not there, then
	Vec3D NE2_vec;
	Vec3D OE1_vec;
	Vec3D CD_vec;
	Vec3D OE1_CD;
	Vec3D NE2_CD;
	Vec3D HE21_vec;
	Vec3D HE22_vec;
	int i;
	for(i = 0; i <MAX_ATOMS; i++){
		if(strcmp(res->names[i], "1HE2")==0 ){// found it 
			strcpy(res->names[i], "");
			res->max_atom_index--;
		}
		if(strcmp(res->names[i], "2HE2")==0 ){// found it 
			strcpy(res->names[i], "");
			res->max_atom_index--;
		}
	}
	for(i = 0; i <MAX_ATOMS; i++){
		if(strcmp(res->names[i], " NE2")==0){
			copyVector(res->atoms[i], NE2_vec);
		}
		if(strcmp(res->names[i], " OE1")==0){
			copyVector(res->atoms[i], OE1_vec);
		}
		if(strcmp(res->names[i], " CD ")==0){
			copyVector(res->atoms[i], CD_vec);
		}
	}
	difference(CD_vec, NE2_vec, NE2_CD);
	unitVector(NE2_CD, NE2_CD); // this vec is axis
	difference(CD_vec, OE1_vec, OE1_CD);
	unitVector(OE1_CD, OE1_CD);
	summation(NE2_vec, OE1_CD, HE21_vec);
	res->max_atom_index++;
	strcpy(res->names[res->max_atom_index], "1HE2");
	copyVector(HE21_vec, res->atoms[res->max_atom_index]);
	
	rotateCC(NE2_CD, OE1_CD, 180.00);
	summation(NE2_vec, OE1_CD, HE22_vec);
	res->max_atom_index++;
	strcpy(res->names[res->max_atom_index], "2HE2");
	copyVector(HE22_vec, res->atoms[res->max_atom_index]);
	
	//printf("   --- we are here\n");	
}

//***********************************************************
// this function will handle HD2 atoms in ASP
// ResData * res: 'ASP'
//***********************************************************
static void check_add_HD2_in_ASP(ResData *res){
	Vec3D OD2_vec;
	Vec3D CG_vec;
	Vec3D HD2_vec;
	Vec3D CG_OD2;
	int i;
	for(i = 0; i <MAX_ATOMS; i++){
		if(strcmp(res->names[i], "2HD ")==0 ){
			if (res->atoms[i][0] >-100000.00){
				return;
			}else{
				strcpy(res->names[i], "");
				res->max_atom_index--;
			}
		}
	}
	for (i = 0; i <MAX_ATOMS; i++){
		if(strcmp(res->names[i], " OD2")==0){
			copyVector(res->atoms[i], OD2_vec);
		}
		if(strcmp(res->names[i], " CG ")==0){
			copyVector(res->atoms[i], CG_vec);
		}
	}
	difference(OD2_vec, CG_vec,  CG_OD2); 
	unitVector(CG_OD2, CG_OD2);
	summation(OD2_vec, CG_OD2, HD2_vec);
	res->max_atom_index++;
	if(res->max_atom_index ==MAX_ATOMS) res->max_atom_index = MAX_ATOMS -1;
	strcpy(res->names[res->max_atom_index], "2HD ");
	copyVector(HD2_vec, res->atoms[res->max_atom_index]);
	//res->max_atom_index = 17;
}

//***********************************************************
// this function will handle HD2 atoms in ASP
// ResData * res: 'GLU'
//***********************************************************
static void check_add_HE2_in_GLU(ResData *res){
	Vec3D OE2_vec;
	Vec3D CD_vec;
	Vec3D HE2_vec;
	Vec3D CD_OE2;
	int i;
	for(i = 0; i <MAX_ATOMS; i++){	
		if(strncmp(res->names[i], " HE2",4)==0 ){
			if (res->atoms[i][0]> -100000.00) {
				return;
			}else{
				strcpy(res->names[i], "");
				res->max_atom_index--;
			}
		}
	}
	
	for (i = 0; i <MAX_ATOMS; i++){
		if(strcmp(res->names[i], " OE2")==0){
			copyVector(res->atoms[i], OE2_vec);
		}
		if(strcmp(res->names[i], " CD ")==0){
			copyVector(res->atoms[i], CD_vec);
		}
	}
	difference(OE2_vec, CD_vec,  CD_OE2); 
	unitVector(CD_OE2, CD_OE2);
	summation(OE2_vec, CD_OE2, HE2_vec);
	res->max_atom_index++;
	if(res->max_atom_index ==MAX_ATOMS) res->max_atom_index = MAX_ATOMS -1;
	strcpy(res->names[res->max_atom_index], " HE2");
	copyVector(HE2_vec, res->atoms[res->max_atom_index]);
	//res->max_atom_index = 20;
}

//********************************************************
// find 1H atom in first res, if not there, we build one
// ResData * res: frist res in whole sequence of res
//******************************************************
static void check_add_1H_in_first_res(ResData * res){
	if ( res->atoms[HNp][0] != -1000000.0) return; // found	
	// not found 
	Vec3D CA_vec;
	Vec3D Np_vec;
	Vec3D HNp_vec;
	Vec3D CA_Np;
	int i;
	for(i = 0; i <MAX_ATOMS; i++){
		if(strcmp(res->names[i], "1H  ")==0){
			return;
		}
	}
	copyVector(res->atoms[CA], CA_vec);
	copyVector(res->atoms[Np], Np_vec);
	
	difference(Np_vec, CA_vec,  CA_Np); 
	unitVector(CA_Np, CA_Np);
	summation(Np_vec, CA_Np, HNp_vec);
	strcpy(res->names[HNp], "1H  ");
	copyVector(HNp_vec, res->atoms[HNp]);
}

/*********************************************************
* this function will check and add HN atom onto single residue
* if it is found that HN atom is missing
***********************************************************/
static void check_add_HN_in_single_res(ResData *res ){
	Vec3D unit_vec;
	Vec3D H_vec;
	Vec3D C_vec;
	Vec3D N_vec;
	Vec3D CA_vec;
	Vec3D N_C;
	Vec3D N_CA;
	// get HN by calcualtion	
	if( res->atoms[HN][0] !=  ATOM_COORD_NA ){
		// HN atom exists and its coordinates exist;
		return;
	}
	copyVector(res->atoms[C1], C_vec);
	copyVector(res->atoms[N], N_vec);
	copyVector(res->atoms[CA], CA_vec);
	difference(C_vec, N_vec, N_C);
	difference(CA_vec, N_vec, N_CA);
	unitVector(N_CA, N_CA);
	rotateCC(N_CA, N_C, 120.00);
	//N_C is what we want
	copyVector(N_C, H_vec);
	unitVector(H_vec, unit_vec);
	copyVector(unit_vec, H_vec);
	summation(H_vec, N_vec, H_vec);
	// now we get HN vec;
	strcpy(res->names[HN], " H  ");
	copyVector(H_vec, res->atoms[HN]);
	//printf("   --- we are here\n");
	//printf("N  %lf %lf %lf , HN  %lf %lf %lf \n", res->atoms[N][0], res->atoms[N][1],res->atoms[N][2], res->atoms[HN][0], res->atoms[HN][1], res->atoms[HN][2]);
}


/* get chi2 to chi5 angles*/
static void get_chi_angles(ResData *res, int n){
	int i;
	// double  chi2 = -1000000.00, chi3 = -1000000.00, chi4 = -1000000.00, chi5 = -1000000.00;	
	double  chi2 = -1000000.00, chi3 = -1000000.00;
	
	for(i = 0;i <n; i++){
		if (!(strcmp(res[i].resName, "GLY")==0 || strcmp(res[i].resName, "ALA")==0 || 
   		    strcmp(res[i].resName, "SER")==0 || strcmp(res[i].resName, "THR")==0 ||
    		    strcmp(res[i].resName, "CYS")==0 || strcmp(res[i].resName, "VAL")==0 ||	
    		    strcmp(res[i].resName, "PRO")==0  ) ){
			chi2 = get_chi2_w_coor(&res[i]);
			res[i].chi2 = chi2;
		}else { 
			res[i].chi2 = -1000000.00;
		}
		if (strcmp(res[i].resName, "MET")==0 || strcmp(res[i].resName, "GLU")==0 || 
    		    strcmp(res[i].resName, "GLN")==0 || strcmp(res[i].resName, "LYS")==0 ||
    		    strcmp(res[i].resName, "ARG")==0  ){
	 		chi3 = get_chi3_w_coor(&res[i]);
			res[i].chi3 = chi3;
		}else{
			res[i].chi3 = -1000000.00;
		}
		/*
		if (strcmp(res[i].resName, "LYS")==0 || strcmp(res[i].resName, "ARG")==0  ){
			chi4 = get_chi4_w_coor(&res[i]);
			res[i].chi4 = chi4;
		}else{
			res[i].chi4 = -1000000.00;
		}
		if (strcmp(res[i].resName, "LYS")==0 || strcmp(res[i].resName, "ARG")==0  ){
			chi5 = get_chi5_w_coor(&res[i]);
			res[i].chi5 = chi5;
		}else{
			res[i].chi5 = -1000000.00;
		}
		*/
		res[i].chi4 = -1000000.00;
		res[i].chi5 = -1000000.00;
	}	
}

// ********************************************************
// this function will get back the side chain chi2 angle based on
// the CA-CB-CG-CD coordinates. GLY, ALA, SER, THR, CYS, VAL, PRO have no chi2;
// they are filter out in parent function;
// ResData * res : single residue
// ********************************************************
static double get_chi2_w_coor(ResData *res){
	int find_it = 0;
	int atom_index = -1;
	double last_chi2 = -1000000.00;
	Vec3D CA_vec;
	Vec3D CB_vec;
	Vec3D CG_vec;
	Vec3D CD_vec;
	copyVector(res->atoms[CA], CA_vec);
	copyVector(res->atoms[CB], CB_vec);
	// there is some problem in CG;
	// ILE has no CG, use CG1 instead
	if (strncmp(res->resName, "ILE", 3)==0){
		find_it = find_atom_index_in_res(res, &atom_index, " CG1");
		if (find_it==0 && MODE_DEBUG){
			fprintf(stderr,"Cannot find CG1 atom in ILE, that is weird, please check! Program stops! \n");
		}
		if (atom_index != -1){
			copyVector(res->atoms[atom_index], CG_vec);
		}
	}else{
		copyVector(res->atoms[CG], CG_vec);
	}
	// there is some problem in CD  
	// LEU has no CD , we use CD1 instead
	// ILE has no CD , we use CD1
	// MET has no CD , we use SD
	// PHE has no CD , we use CD1
	// TYR has no CD , we use CD1
	// TRP has no CD , we use CD1
	// ASP has no CD,  we use OD2 , there is HD2
	// ASN has no CD,  we use ND2 , there is HD1 and HD2 there
	// HIS has no CD,  we use CD2 
	if (strncmp(res->resName, "LEU", 3) ==0){
		find_it = find_atom_index_in_res(res, &atom_index, " CD1");
		if (find_it==0 && MODE_DEBUG){
			fprintf(stderr,"Cannot find CD1 atom in LEU, that is weird, please check! \n");
		} 
	}else if (strncmp(res->resName, "ILE", 3) ==0){
		find_it = find_atom_index_in_res(res, &atom_index, " CD1");
		if (find_it ==0 && MODE_DEBUG){
			fprintf(stderr,"Cannot find CD1 atom in ILE, that is weird, please check! \n");
		} 
	}else if (strncmp(res->resName, "MET", 3) ==0){
		find_it = find_atom_index_in_res(res, &atom_index, " SD ");
		if (find_it ==0 && MODE_DEBUG){
			fprintf(stderr,"Cannot find SD atom in MET, that is weird, please check! \n");
		} 
	}else if (strncmp(res->resName, "PHE", 3) ==0){
		find_it = find_atom_index_in_res(res, &atom_index, " CD1");
		if (find_it ==0 && MODE_DEBUG){
			fprintf(stderr,"Cannot find CD1 atom in PHE, that is weird, please check! \n");
		} 
	}else if (strncmp(res->resName, "TYR", 3) ==0){
		find_it = find_atom_index_in_res(res, &atom_index, " CD1");
		if (find_it ==0 && MODE_DEBUG){
			fprintf(stderr,"Cannot find  atom CD1 in TYR, that is weird, please check! \n");
		} 
	}else if (strncmp(res->resName, "TRP", 3) ==0){
		find_it = find_atom_index_in_res(res, &atom_index, " CD1");
		if (find_it ==0 && MODE_DEBUG){
			fprintf(stderr,"Cannot find CD1 atom in TRP, that is weird, please check!  \n");
		} 
	}else if (strncmp(res->resName, "ASP", 3) ==0){
		find_it = find_atom_index_in_res(res, &atom_index, " OD2");
		if (find_it ==0 && MODE_DEBUG){
			fprintf(stderr,"Cannot find OD2 atom in ASP, that is weird, please check!  \n");
		}
	}else if (strncmp(res->resName, "ASN", 3) ==0){
		find_it = find_atom_index_in_res(res, &atom_index, " ND2");
		if (find_it ==0 && MODE_DEBUG){
			fprintf(stderr,"Cannot find ND2 atom in ASN, that is weird, please check!  \n");
		}
	}else if (strncmp(res->resName, "HIS", 3) ==0){
		find_it = find_atom_index_in_res(res, &atom_index, " CD2");
		if (find_it ==0 && MODE_DEBUG){
			fprintf(stderr,"Cannot find CD2 atom in HIS, that is weird, please check! Program stops! \n");
		}  
	}else {
		find_it = find_atom_index_in_res(res, &atom_index, " CD ");
		if (find_it ==0 && MODE_DEBUG){
			fprintf(stderr,"Cannot find CD atom in res '%s' id %d, that is weird, please check! Program stops! \n", res->resName, res->id);
		}
	}
	copyVector(res->atoms[atom_index], CD_vec);
	// get chi angle
	last_chi2 = torsion(CA_vec, CB_vec, CG_vec, CD_vec);
	return last_chi2;
}


// ********************************************************
// this function will get back the side chain chi3 angle based on
// the CB-CG-CD-CE coordinates. only MET , GLU, GLN, LYS, ARG have chi3;
// they are filter out in parent function;
// ResData * res : single residue
// ********************************************************
static double get_chi3_w_coor(ResData *res){
	int find_it = 0;
	int atom_index = -1;
	double last_chi3 = -1000000.00;
	Vec3D CB_vec;
	Vec3D CG_vec;
	Vec3D CD_vec;
	Vec3D CE_vec;
	copyVector(res->atoms[CB], CB_vec);
	copyVector(res->atoms[CG], CG_vec);
	
	// there is some problem in CD  
	// MET has no CD , we use SD
	if (strncmp(res->resName, "MET", 3) ==0){
		find_it = find_atom_index_in_res(res, &atom_index, " SD ");
		if (find_it ==0 && MODE_DEBUG){
			fprintf(stderr,"Cannot find SD atom in MET, that is weird, please check!  \n");
		} 
	}else {
		find_it = find_atom_index_in_res(res, &atom_index, " CD ");
		if (find_it ==0 && MODE_DEBUG){
			fprintf(stderr,"Cannot find CD atom in res '%s' id %d, that is weird, please check!   \n", res->resName, res->id);
		}
	}
	copyVector(res->atoms[atom_index], CD_vec);
	// there is some problem in CE
	// GLU has no CE, but OE2
	// GLN has no CE, but NE2
	// ARG has no CE, but NE
	if (strncmp(res->resName, "GLU", 3)==0){
		find_it = find_atom_index_in_res(res, &atom_index, " OE2");
		if (find_it ==0 && MODE_DEBUG){
			fprintf (stderr, "Cannot find OE2 in GLU, that is weird, please check!\n");
		}
	}else if(strncmp(res->resName, "GLN", 3)==0){
		find_it = find_atom_index_in_res(res, &atom_index, " NE2");
		if (find_it ==0 && MODE_DEBUG){
			fprintf (stderr, "Cannot find NE2 in GLN, that is weird, please check!\n");
		}
	}else if(strncmp(res->resName, "ARG", 3)==0){
		find_it = find_atom_index_in_res(res, &atom_index, " NE ");
		if (find_it ==0 && MODE_DEBUG){
			fprintf (stderr, "Cannot find NE in ARG, that is weird, please check!\n");
		}
	}else{
		find_it = find_atom_index_in_res(res, &atom_index, " CE ");
		if (find_it ==0 && MODE_DEBUG){
			fprintf (stderr, "Cannot find CE in MET or LYE, that is weird, please check!\n");
		}
	}
	copyVector(res->atoms[atom_index], CE_vec);
		
	
	// get chi angle
	last_chi3 = torsion(CB_vec, CG_vec, CD_vec, CE_vec);
	return last_chi3;
}

// ********************************************************
// this function will get back the side chain chi4 angle based on
// the CG-CD-CE-CZ coordinates.  
// they are filter out in parent function;
// ResData * res : single residue
// only some res have chi4,  LYS, ARG.
// ********************************************************
/*
static double get_chi4_w_coor(ResData *res){
	int find_it = 0;
	int atom_index = -1;
	double last_chi4 = -1000000.00;
	Vec3D CG_vec;
	Vec3D CD_vec;
	Vec3D CE_vec;
	Vec3D CZ_vec;
	copyVector(res->atoms[CG], CG_vec);
	find_it = find_atom_index_in_res(res, &atom_index, " CD ");
	copyVector(res->atoms[atom_index], CD_vec);
	// there is some problem in CE;
	if (strncmp(res->resName, "ARG", 3)==0){
		find_it = find_atom_index_in_res(res, &atom_index, " NE ");
		if (find_it==0){
			fprintf(stderr,"Cannot find NE atom in ARG, that is weird, please check!  \n");
		}
	}else{
		find_it = find_atom_index_in_res(res, &atom_index, " CE ");
		if (find_it==0){
			fprintf(stderr,"Cannot find CE atom in LYS, that is weird, please check!  \n");
		}
	}
	copyVector(res->atoms[atom_index], CE_vec);
	// there is some problem in CZ
	// LYS has no CZ, but NZ 
	if (strncmp(res->resName, "LYS", 3) ==0){
		find_it = find_atom_index_in_res(res, &atom_index, " NZ ");
		if (find_it ==0){
			fprintf(stderr,"Cannot find NZ atom in LYS, that is weird, please check!   \n");
		} 
	}else {
		find_it = find_atom_index_in_res(res, &atom_index, " CZ ");
		if (find_it ==0){
			fprintf(stderr,"Cannot find CZ atom in ARG that is weird, please check!   \n" );
		}
	}
	copyVector(res->atoms[atom_index], CZ_vec);
	// get chi angle
	last_chi4 = torsion(CG_vec, CD_vec, CE_vec, CZ_vec);
	return last_chi4;
}

// ********************************************************
// this function will get back the side chain chi5 angle based on
// the CD-CE-CZ-CH coordinates.  
// they are filter out in parent function;
// ResData * res : single residue
// only ARG and LYS have chi5

// ********************************************************
static double get_chi5_w_coor(ResData *res){
	int find_it = 0;
	int atom_index = -1;
	double last_chi5 = -1000000.00;
	Vec3D CD_vec;
	Vec3D CE_vec;
	Vec3D CZ_vec;
	Vec3D CH_vec;
	find_it = find_atom_index_in_res(res, &atom_index, " CD ");
	copyVector(res->atoms[atom_index], CD_vec);
	if ( strncmp(res->resName, "ARG", 3)==0){
		find_it = find_atom_index_in_res(res, &atom_index, " NE ");
		if (find_it==0){
			fprintf(stderr,"Cannot find NE atom in ARG, that is weird, please check!  \n");
		}
	}else{
		find_it = find_atom_index_in_res(res, &atom_index, " CE ");
	}
	copyVector(res->atoms[atom_index], CE_vec);
	// there is some problem in CZ;
	// LYS has no CZ ,but NZ 
	if (strncmp(res->resName, "LYS", 3)==0){
		find_it = find_atom_index_in_res(res, &atom_index, " NZ ");
		if (find_it==0){
			fprintf(stderr,"Cannot find NZ atom in LYS, that is weird, please check!   \n");
		}
	}else{
		find_it = find_atom_index_in_res(res, &atom_index, " CZ ");
		if (find_it==0){
			fprintf(stderr,"Cannot find CZ atom in ARG, that is weird, please check!   \n");
		}
	}
	copyVector(res->atoms[atom_index], CZ_vec);
	// there is some problem in CH
	// LYS has no CH , but 1HZ
	// ARG has no CH, but NH1
	if (strncmp(res->resName, "LYS", 3) ==0){
		find_it = find_atom_index_in_res(res, &atom_index, "1HZ ");
		if (find_it ==0){
			fprintf(stderr,"Cannot find 1HZ atom in LYS, that is weird, please check!\n");
		} 
	}else {
		find_it = find_atom_index_in_res(res, &atom_index, " NH1");
		if (find_it ==0){
			fprintf(stderr,"Cannot find NH1 atom in ARG, that is weird, please check!  \n"); 
		}
	}
	copyVector(res->atoms[atom_index], CH_vec);
	// get chi angle
	last_chi5 = torsion(CD_vec, CE_vec, CZ_vec, CH_vec);
	return last_chi5;
}

comment from get_chi4
*/


/* 
 * this function will calculate theta angles' mean and standart deviation on all atoms of all res .
 */
static void get_theta_score(ResData *res, int numRes){
	int i,j,k;
	double mean = 0.0;
	double deviation = 0.0;
	double kappa_mean=0.0;
	double kappa_devi = 0.0;
	Vec3D O_vec, H_vec, OH_vec, OC_vec;
	double dist = -1.0;
	
	Hcontainer * h_cont = malloc(sizeof(Hcontainer)* numRes);
	assert(h_cont != NULL);
	memset(h_cont,0, sizeof(Hcontainer)*numRes);

	int first_time = 1;
	int oppo_res_num;
	int oppo_res_atom_num;
	for (i=0; i < numRes; i++){
		h_cont[i].index = i;
		copyVector(res[i].atoms[O], O_vec);
		difference(res[i].atoms[C1], O_vec, OC_vec);
		oppo_res_num = 0;
		oppo_res_atom_num = 0;
		dist = -1.0;
		for(j =0; j <numRes; j++) {
			first_time = 1;
			if (j==i)  continue; // skip same res
			
			for(k=0; k<MAX_ATOMS; k++){
				if (j==i-1 && k==HN) continue; // skip the current res' HN and O
				if ( strstr(res[j].names[k], " H  ") ==NULL) continue;// skip nonHN atoms
				copyVector(res[j].atoms[k], H_vec);
				dist = distance(H_vec, O_vec);
				if (dist <2.4 && dist > 1.4) {
					if (first_time){
						h_cont[i].oppo_res_count++;
						oppo_res_num = h_cont[i].oppo_res_count;
						h_cont[i].oppo_index[oppo_res_num-1] = j;
						first_time =0;
					}
					h_cont[i].single_res_atom_count[oppo_res_num-1]++;
					oppo_res_atom_num = h_cont[i].single_res_atom_count[oppo_res_num-1];
					// get atom name
					strcpy(h_cont[i].oppo_atom_name[oppo_res_num-1][oppo_res_atom_num-1], res[j].names[k]);
					// get theta
					difference(H_vec, O_vec, OH_vec);
					h_cont[i].theta[oppo_res_num-1][oppo_res_atom_num-1] = angle(OH_vec, OC_vec);

        res[i].theta = h_cont[i].theta[oppo_res_num-1][oppo_res_atom_num-1];

//	printf ("theta = %f\n", h_cont[i].theta[oppo_res_num-1][oppo_res_atom_num-1]); ////////////////////////////////

					h_cont[i].HO_dist[oppo_res_num-1][oppo_res_atom_num-1] = dist;
					h_cont[i].kappa[oppo_res_num-1][oppo_res_atom_num-1] = get_kappa_angle(&res[i], &res[j]);
        res[i].kappa = h_cont[i].kappa[oppo_res_num-1][oppo_res_atom_num-1];
				}
			}// for (k=
		}// for(j=
	}// for (i=

	int count=0;
	for (i = 0; i <numRes ; i++){
		for (j=0; j< h_cont[i].oppo_res_count; j++){
			for(k=0; k < h_cont[i].single_res_atom_count[j]; k++){
				mean += h_cont[i].theta[j][k];
				kappa_mean += h_cont[i].kappa[j][k];
				count++;
			}
		}
	}
	if (count==0){
		mean = 0.0;
		kappa_mean = 0.0;
	}else{	
		mean = mean/count;
		kappa_mean = kappa_mean/count;
	}
	double diff = 0.0;
	double kappa_diff = 0.0;
	for (i = 0; i <numRes ; i++){
		for (j=0; j< h_cont[i].oppo_res_count; j++){
			for(k=0; k < h_cont[i].single_res_atom_count[j]; k++){
				if (g_print_theta){
					if ( strcmp(h_cont[i].oppo_atom_name[j][k], " H  ")==0){
						//printf("%d %s O, %d %s %s , HO_dist = %7.3f, theta = %7.3f, kappa= %7.3f\n", res[i].id, res[i].resName, res[h_cont[i].oppo_index[j]+1].id, res[h_cont[i].oppo_index[j]+1].resName, " H  ", h_cont[i].HO_dist[j][k], h_cont[i].theta[j][k], h_cont[i].kappa[j][k]);
						res[i].theta_recp= h_cont[i].theta[j][k];
						res[i].kappa_recp= h_cont[i].kappa[j][k];
						res[h_cont[i].oppo_index[j]+1].theta_donar = h_cont[i].theta[j][k];
						res[h_cont[i].oppo_index[j]+1].kappa_donar = h_cont[i].kappa[j][k];
					}else{
						//printf("%d %s O, %d %s %s, HO_dist = %7.3f, theta = %7.3f, kappa= %7.3f\n", res[i].id, res[i].resName, res[h_cont[i].oppo_index[j]].id, res[h_cont[i].oppo_index[j]].resName, h_cont[i].oppo_atom_name[j][k], h_cont[i].HO_dist[j][k], h_cont[i].theta[j][k], h_cont[i].kappa[j][k]);
						res[i].theta_recp= h_cont[i].theta[j][k];
						res[i].kappa_recp= h_cont[i].kappa[j][k];
						res[h_cont[i].oppo_index[j]].theta_donar = h_cont[i].theta[j][k];
						res[h_cont[i].oppo_index[j]].kappa_donar = h_cont[i].kappa[j][k];
					}				
				}
				diff = fabs(h_cont[i].theta[j][k] - mean);
				deviation += pow(diff, 2.0);
				kappa_diff = fabs(h_cont[i].kappa[j][k] - kappa_mean);
				kappa_devi = pow(kappa_diff, 2.0);
			}
		}
	}
	if (count==0){	
		deviation=0.0;
		kappa_devi=0.0;
	}else{	
		deviation = sqrt(deviation/count);	
		kappa_devi = sqrt(kappa_devi/count);
	}
	free(h_cont);
}

/*
 *  this function will find atom index in atoms array in ResData that will match 
 *  the desired atom name;
 *  ResData * res : single residue
 *  int * atom_index : index in res->atoms[];
 *  char atom_names[5] : desired atom name.
 */
static int find_atom_index_in_res(ResData * res, int *atom_index, char atom_name[5]){
	int i;
	int find_it = 0;
	for (i = 0; i< MAX_ATOMS; i++){
		if (strncmp(res->names[i], atom_name, 4)==0){
			*atom_index = i;
			find_it = 1;
			return find_it;
		}
	}
	
	return find_it;

}

/*
 * get kappa angle bw two res
 */
static double get_kappa_angle(ResData * res_accp, ResData *res_donor){
	double kappa = -1.0; 
	Vec3D  O_vec, C1_vec, N_vec, CA_vec, H_vec;
	copyVector(res_accp->atoms[O], O_vec);
	copyVector(res_accp->atoms[C1], C1_vec);
	copyVector(res_accp->atoms[N], N_vec);
	copyVector(res_accp->atoms[CA], CA_vec);
	copyVector(res_donor->atoms[HN], H_vec);
	Vec3D  O_N, O_CA, O_C1, O_H;
	difference(N_vec, O_vec, O_N);
	difference(CA_vec, O_vec, O_CA);
	difference(C1_vec, O_vec, O_C1);
	difference(H_vec, O_vec, O_H);
	//printf("CA %f %f %f, C1 %f %f %f, N %f %f %f, O %f %f %f, H %f %f %f\n", O_CA[0],  O_CA[1],  O_CA[2],  O_C1[0],  O_C1[1],  O_C1[2],
			 //O_N[0], O_N[1], O_N[2], 0.0, 0.0 , 0.0,  O_H[0], O_H[1], O_H[2]); 
	//printf(" ****0     O_C1 %f %f %f   O_H %f %f %f\n", O_C1[0], O_C1[1], O_C1[2], O_H[0], O_H[1], O_H[2] );
	// now O is the original point. now we put C1 on the xy plane first.
	Vec3D tmp_C1; 
	tmp_C1[0] = O_C1[0];
	tmp_C1[1] = O_C1[1];
	tmp_C1[2] = 0.0;
	turn_struct(O_C1, tmp_C1,  O_C1, O_N, O_CA, O_H);
	//printf(" ****1 C1 on xy plain    O_C1 %f %f %f   O_H %f %f %f\n", O_C1[0], O_C1[1], O_C1[2], O_H[0], O_H[1], O_H[2] );
	
	// now C1 is on xy plain. now we move C1 onto x axis.
	tmp_C1[0] = O_C1[0];
        tmp_C1[1] = 0.0;
        tmp_C1[2] = 0.0;
	turn_struct(O_C1, tmp_C1,  O_C1, O_N, O_CA, O_H);
	//printf(" ****2  C1 on x axis    O_C1 %f %f %f   O_H %f %f %f\n", O_C1[0], O_C1[1], O_C1[2], O_H[0], O_H[1], O_H[2] );
	//now we turn O_N, make it on the xy plain too.
  	// we turn O_N, but not O_CA,  we know that they are on the same plain if we 
 	// turn O_N and O_CA at the same time.
	Vec3D tmp_N;
	tmp_N[0] = 0.0; 
	tmp_N[1] = O_N[1]; 
	tmp_N[2] = O_N[2];
	Vec3D y_axis;
	y_axis[0] = 0.0; 
	y_axis[1] = 1.0; 
	y_axis[2] = 0.0;
	turn_struct(tmp_N, y_axis, O_C1, O_N, O_CA, O_H);
	//printf(" ****3   N on xy plain  O_C1 %f %f %f    O_N %f %f %f  O_H %f %f %f\n", O_C1[0], O_C1[1], O_C1[2], O_N[0], O_N[1], O_N[2], O_H[0], O_H[1], O_H[2]  );
	// now the N and CA should be on xy plain, time to calculate kappa angle;
	//printf("CA %f %f %f, C1 %f %f %f, N %f %f %f, O %f %f %f, H %f %f %f\n", O_CA[0],  O_CA[1],  O_CA[2],  O_C1[0],  O_C1[1],  O_C1[2],
			 //O_N[0], O_N[1], O_N[2], 0.0, 0.0 , 0.0,  O_H[0], O_H[1], O_H[2]); 
	Vec3D tmp_H;
	tmp_H[0] = O_H[0]; 
	tmp_H[1] = O_H[1]; 
	tmp_H[2] = 0.0;
	kappa = angle(tmp_H, O_H);	
	// printf ("kappa = %f\n", kappa); ////////////////////////////////
		
	return kappa;
}

/*
 * turn the structure with some angle and some axis;
 * first two arguments for defining the angle. and we build an axis with these two 
 * vectors and turn the other argument vectors;
 */ 
static void turn_struct(Vec3D ang_vec1, Vec3D ang_vec2, Vec3D O_C1, Vec3D O_N, Vec3D O_CA, Vec3D O_H){
	double  turn_angle = angle(ang_vec1, ang_vec2);
	Vec3D L;
	crossproduct(ang_vec1, ang_vec2, L);
	unitVector(L, L);
	if (L[0] ==0.0 && L[1] ==0.0 && L[2]==0.0) {
		printf ("Error: get L axis coord 0,0,0!\n");
		exit(-1);
	}
	rotateCC(L, O_C1, turn_angle);
	rotateCC(L, O_N, turn_angle);
	rotateCC(L, O_CA, turn_angle);
	rotateCC(L, O_H, turn_angle);
		
}

