import java.util.HashMap;
import java.lang.Math; 

/**
 * this class models an amino acid residue in Shiftx
 * @author yifeng
 * @date November 9th, 2009
 * # fields
 * #################################################################################################
 * 1) resno,iupac,ss,hbond,bturn,iupac_p,ss_p,iupac_n,ss_n,
 * 2) resASA,fracASA,resVol,fracVol,sideASA,sideFracASA,SFE,ssbond,
 * 3) phi,psi,omega,chi,chi2,chi3,theta_donor,kappa_donor,theta_acceptor,kappa_acceptor,
 * 4) phi_p,psi_p,omega_p,chi_p,phi_n,psi_n,omega_n,chi_n,
 * 5) phi_p2,psi_p2,omega_p2,chi_p2,phi_n2,psi_n2,omega_n2,chi_n2,
 * 6) hydro,asa,
 * 7) rcurr, 
 * 8) hs_phipsi,hs_phichi,hs_psichi, 
 * 9) rcoil, atomname
 * 10) cs_secondary <- predicted values (set '0')
 * #################################################################################################
 * line 1,2 : from vadar output
 * line 3-5 : from angles
 * line 6   : hydro from hydro, asa from vdar:<.areaatom>, hs_* will be predicted from weka
 * line 7   : ring current from shiftx_gen
 * line 8   : hypersurfaces which will be predicted in Java programs with weka modules 
 * line 9   : rcoil, atomname from random coil table (about 230 rows)
 * line 10  : predicted secondary chemical shifts which will be predicted
####################################################################################################
 *
 * Everything is set to public for easy data manipulation despite it's a bad programming practice.
 */
public class Residue {

	//public static enum BTURN {I, II, III, IP, IIP, IIIP};

	public String id = ""; // id number, for example 1ACX or any other String id.
	public int resNum; // residue number.
	public String resName; // residue name.
	public String res_p = "NA"; // previous residue name
	public String res_n = "NA"; // next residue name
	
	/* residue type */
	public String iupac    = "NA";
	public String iupac_p  = "NA"; // iupac for previous residue
	public String iupac_n  = "NA"; // iupac for next residue
	public String iupac_p2 = "NA"; // iupac for pre-previous residue
	public String iupac_n2 = "NA"; // iupac for next-next residue
	
	/* secondary structure */
	public String ss = "NA"; // secondary structure for this residue
	public String ss_p = "NA"; // SS for previous residue
	public String ss_n = "NA"; // SS for next residue
	
	public int   hbond   = 0; // 0:1(true) - indicate whether this residue forms a H-bond with some other residue
	public int   hbond_p = 0; // 
	public int   hbond_n = 0; //
	public String bturn   = "NA"; // beta turn type
	
	public double resASA = 0.0; // residue Accessible Surface Area
	public double fracASA = 0.0; // fractional Accessible Surface Area
	public double resVol = 0.0; // residue Volume 
	public double fracVol = 0.0; // fractional residue volume
	public double sideASA = 0.0; // side chain accessible surface area
	public double sideFracASA = 0.0; // side fractional surface area
	public double SFE = 0.0; 

        // Angles
	public double phi = 0.0; // residue i phi angle
	public double phi_p = 0.0; // residue i-1 phi angle
	public double phi_p2 = 0.0; // residue i-2 phi angle
	public double phi_n = 0.0; // residue i+1
	public double phi_n2 = 0.0; // residue i+2

	public double psi = 0.0;
	public double psi_p = 0.0;
	public double psi_p2 = 0.0;
	public double psi_n = 0.0;
	public double psi_n2 = 0.0;

	public double omega = 0.0;
	public double omega_p = 0.0;
	public double omega_p2 = 0.0;
	public double omega_n = 0.0;
	public double omega_n2 = 0.0;

	public double chi  = 0.0;
	public double chi_p = 0.0;
	public double chi_n = 0.0;
	public double chi_p2 = 0.0;
	public double chi_n2 = 0.0;

	public double chi2 = 0.0;
	public double chi3 = 0.0;

	public double theta_donor = 0.0;
	public double kappa_donor = 0.0;
	public double theta_acceptor = 0.0;
	public double kappa_acceptor = 0.0;

	public double theta = 0.0;
	public double kappa = 0.0;

	/* disulfide bond */
	public String envClass = "NA" ; // environment class
	public int  ssbond = 0; // form a disulfide bond with some other residue
	public double ssdist = 0.0; // distance of disulfide bond

	public int dbond   = 0; // form a disulfide bond with some other residue
	public int dbond_p = 0; 
	public int dbond_n = 0; 

	/* H-bond length from shiftx */
	public double ha1dist = 0.0;
	public double ha2dist = 0.0;
	public double hndist  = 0.0;
	public double ordist  = 0.0;

	public double ha1dist_p = 0.0;
	public double ha2dist_p = 0.0;
	public double hndist_p  = 0.0;
	public double ordist_p  = 0.0;

	public double ha1dist_n = 0.0;
	public double ha2dist_n = 0.0;
	public double hndist_n  = 0.0;
	public double ordist_n  = 0.0;


	/* hydrophobicity */
	public double hydro = 0.0;

	public double ph = 0.0; // default: 5.0
	public double temperature = 0.0;  // default: 298.0 

	public HashMap<String, Atom> atoms; // mapping from atom name to Atom object
		
	/**
	 * constructor.
	 */
	public Residue(){
		atoms = new HashMap<String, Atom>();
	}
	
	public String toString(){
		StringBuffer buf = new StringBuffer();
		
		// append the fields from .hydro
		buf.append(this.resNum + "\t" + this.resName + "\t" + this.iupac + "\t" + this.hydro + "\n");
		
		// append the fields from areaatom and ring current
		// ArrayList<String> keys = new ArrayList<String>(atoms.keySet());
		// Collections.sort(keys);
		for(String k : atoms.keySet()) {
			Atom atom = atoms.get(k);
		        buf.append("\t"+atom.atomName + "\thydro:"+this.hydro +"\tasa:"+ atom.asa + "\trcoil:"+atom.rcoil+"\trcurr:" + atom.rcurr+"\n" );
		}

		// append vadar main fields;
		buf.append("\tvadar output:\n"); 
		buf.append("\t" + this.resNum + " " + this.resName + " " + this.ss + " " + this.hbond + " " + this.bturn + " " + this.resASA + " " + this.fracASA + " " + this.resVol + " " + this.fracVol);
		buf.append("\t" + this.res_p + " " + this.iupac_p + " " + this.ss_p + " " + this.res_n + " "+ this.iupac_n + " " + this.ss_n );
		// append vadar side chain fields:
		// buf.append("\t" + this.sideASA + " " + this.sideFracASA + " " + this.SFE + " " + this.envClass + " " + this.ssbond + " " + this.ssdist + "\n");
		buf.append("\tsidechain:" + this.sideASA + " " + this.sideFracASA + " " + this.SFE + "\n");

		// append the fields from angles.
		// phi,psi,omega,chi,chi2,chi3,
		buf.append("\tAngles:\n"); 
		buf.append("\tphi-chi3:" + this.phi + " " + this.psi + " " + this.omega + " " + this.chi + " " + this.chi2 + " " + this.chi3 + "\n");
		// theta_donor,kappa_donor,theta_recp,kappa_recp,
		buf.append("\ttheta-kappa:" + this.theta_donor + " " + this.theta_acceptor + " " + this.kappa_donor + " " + this.kappa_acceptor + "\n");
		// phi_p,psi_p,omega_p,chi_p,
		buf.append("\tphi_p-chi_p:" + this.phi_p + " " + this.psi_p + " " + this.omega_p + " " + this.chi_p+ "\n");
		// phi_n,psi_n,omega_n,chi_n,
		buf.append("\tphi_n-chi_n:" + this.phi_n + " " + this.psi_n + " " + this.omega_n + " " + this.chi_n+ "\n");
		// phi_p2,psi_p2,omega_p2,chi_p2,
		buf.append("\tp2:" + this.phi_p2 + " " + this.psi_p2 + " " + this.omega_p2 + " " + this.chi_p2 + "\n");
		// phi_n2,psi_n2,omega_n2,chi_n2
		buf.append("\tn2:" + this.phi_n2 + " " + this.psi_n2 + " " + this.omega_n2 + " " + this.chi_n2 + "\n");

		return buf.toString();
	}

        // transform Chi angles if it is negative 
        private double  nnChi(double angle) {
            double nnangle;

            if ( angle < 0 ) nnangle = 360.0 + angle;  
            else nnangle = angle;  

            return nnangle;  
        }

        // printout for making training & test dataset
	public String toAtomString(){
		StringBuffer buf = new StringBuffer();
		
		// append the fields from areaatom and ring current
		// ArrayList<String> keys = new ArrayList<String>(atoms.keySet());
		// Collections.sort(keys);

                // fieldlist:  
		for(String k : atoms.keySet()){
			Atom atom = atoms.get(k);

			// vadar main fields;
			buf.append(this.id +","+ this.resNum +","+ this.iupac +","+ this.ss +","+ this.iupac_p +","+ this.ss_p +","+ this.iupac_n +","+ this.ss_n );
                        buf.append(","+ this.iupac_p2 +","+ this.iupac_n2 +","+ this.hbond +","+ this.hbond_p +","+ this.hbond_n +","+ this.bturn ); 
			buf.append(","+ this.resASA +","+ this.fracASA +","+ this.resVol +","+ this.fracVol);

			// vadar side chain fields:
			buf.append(","+ this.sideASA +","+ this.sideFracASA +","+ this.SFE );

			// angles: phi,psi,omega,chi,chi2,chi3,
			buf.append(","+ this.phi +","+ this.psi +","+ this.omega +","+ this.chi +","+ this.chi2 +","+ this.chi3);
			buf.append(","+ this.phi_p +","+ this.psi_p +","+ this.omega_p +","+ this.chi_p);
			buf.append(","+ this.phi_n +","+ this.psi_n +","+ this.omega_n +","+ this.chi_n);
			buf.append(","+ this.phi_p2 +","+ this.psi_p2 +","+ this.omega_p2 +","+ this.chi_p2);
			buf.append(","+ this.phi_n2 +","+ this.psi_n2 +","+ this.omega_n2 +","+ this.chi_n2);

                        // various transform chi1,2,3 angles   
                        // non-negative : (if chi < 0) 360 + chi_original
                        // absolute angle  
			buf.append(","+ nnChi(this.chi) +","+ nnChi(this.chi2) +","+ nnChi(this.chi3) );
			buf.append(","+ nnChi(this.chi_p) +","+ nnChi(this.chi_n) +","+ nnChi(this.chi_p2) +","+ nnChi(this.chi_n2) );
			buf.append(","+ Math.abs(this.chi) +","+ Math.abs(this.chi2) +","+ Math.abs(this.chi3) );
			buf.append(","+ Math.abs(this.chi_p) +","+ Math.abs(this.chi_n) +","+ Math.abs(this.chi_p2) +","+ Math.abs(this.chi_n2) );

                        // Sine Angles
			buf.append(","+ Math.sin(this.phi) +","+ Math.sin(this.psi) +","+ Math.sin(this.omega) +","+ Math.sin(this.chi) +","+ Math.sin(this.chi2) +","+ Math.sin(this.chi3) );
			buf.append(","+ Math.sin(this.phi_p) +","+ Math.sin(this.psi_p) +","+ Math.sin(this.omega_p) +","+ Math.sin(this.chi_p) );
			buf.append(","+ Math.sin(this.phi_n) +","+ Math.sin(this.psi_n) +","+ Math.sin(this.omega_n) +","+ Math.sin(this.chi_n) );
			buf.append(","+ Math.sin(this.phi_p2) +","+ Math.sin(this.psi_p2) +","+ Math.sin(this.omega_p2) +","+ Math.sin(this.chi_p2) );
			buf.append(","+ Math.sin(this.phi_n2) +","+ Math.sin(this.psi_n2) +","+ Math.sin(this.omega_n2) +","+ Math.sin(this.chi_n2) );
                        //Cosine Angles
			buf.append(","+ Math.cos(this.phi) +","+ Math.cos(this.psi) +","+ Math.cos(this.omega) +","+ Math.cos(this.chi) +","+ Math.cos(this.chi2) +","+ Math.cos(this.chi3) );
			buf.append(","+ Math.cos(this.phi_p) +","+ Math.cos(this.psi_p) +","+ Math.cos(this.omega_p) +","+ Math.cos(this.chi_p) );
			buf.append(","+ Math.cos(this.phi_n) +","+ Math.cos(this.psi_n) +","+ Math.cos(this.omega_n) +","+ Math.cos(this.chi_n) );
			buf.append(","+ Math.cos(this.phi_p2) +","+ Math.cos(this.psi_p2) +","+ Math.cos(this.omega_p2) +","+ Math.cos(this.chi_p2) );
			buf.append(","+ Math.cos(this.phi_n2) +","+ Math.cos(this.psi_n2) +","+ Math.cos(this.omega_n2) +","+ Math.cos(this.chi_n2) );

			// theta_donor,kappa_donor,theta_recp,kappa_recp,
			buf.append(","+ this.theta_donor +","+ this.theta_acceptor +","+ this.kappa_donor +","+ this.kappa_acceptor);

                        //dbond status
			buf.append(","+ this.dbond +","+ this.dbond_p +","+this.dbond_n); 

                        //hbond lengths
			buf.append(","+ this.ha1dist +","+ this.ha2dist +","+this.hndist+","+this.ordist); 
			buf.append(","+ this.ha1dist_p +","+ this.ha2dist_p +","+ this.hndist_p +","+ this.ordist_p); 
			buf.append(","+ this.ha1dist_n +","+ this.ha2dist_n +","+ this.hndist_n +","+ this.ordist_n); 
/*
                        //hbond status: true-1: false-0
			buf.append(","+ this.ha1hb +","+ this.ha2hb +","+this.hnhb+","+this.orhb); 
			buf.append(","+ this.ha1hb_p +","+ this.ha2hb_p +","+ this.hnhb_p +","+ this.orhb_p); 
			buf.append(","+ this.ha1hb_n +","+ this.ha2hb_n +","+ this.hnhb_n +","+ this.orhb_n); 
*/
		        buf.append(","+this.hydro +","+ atom.asa + ","+atom.rcoil+","+ atom.rcurr +","+ atom.electrostat+","+ atom.hbondeffect +","+ atom.atomName );
		        buf.append(","+atom.sxpred );
		        buf.append(","+ (atom.rcoil + atom.rcurr + atom.electrostat + atom.hbondeffect)  );
		        buf.append("\n");
		}

		return buf.toString();
	}
}




