# This is statement is required by the build system to query build info
if __name__ == '__build__':
	raise Exception

import sys
import os
import string
import Numeric
import Geometry
import copy

GUAAtoms = [ "N2", "O6", "C6", "C5", "N7", "C8", "N9", "C4", "N3", "C2", "N1" ]

def rotmat(phi, theta, psi, tx=0, ty=0, tz=0):

	s1 = Numeric.sin(phi)
	s2 = Numeric.sin(theta)
	s3 = Numeric.sin(psi)
	c1 = Numeric.cos(phi)
	c2 = Numeric.cos(theta)
	c3 = Numeric.cos(psi)

	newmat = Numeric.array([
		[ c2*c3, s2*s1*c3 - c1*s3, s2*c1*c3 + s1*s3, 0],
		[ c2*s3, s2*s1*s3 + c1*c3, s2*c1*s3 - s1*c3, 0],
		[-s2, c2*s1, c2*c1, 0],
		[tx, ty, tz, 1]])

	return newmat

def matrix_apply(crd, mat):
	o = [crd[0], crd[1], crd[2], 1.0]
	n = [0.0, 0.0, 0.0, 0.0]

	for i in range(4):
		for j in range(4):
			n[i] = n[i] + o[j] * mat[j][i]
			
	new = [None, None, None]
	
	for i in range(3):
		new[i] = (n[i]/n[3])
	return new



class PDBRecord:
	def __init__(self, type=None, anum=None, atom=None, residue=None, chain=None, rnum=None):
		self.type = type
		self.anum = anum
		self.atom = atom
		self.residue = residue
		self.chain = chain
		self.rnum = rnum



class PDB:
	def __init__(self, filename = None, crds = None, records = None, connect = None):
		if crds == None:
			crds = []
		if records == None:
			records = []
		self.records = records
		self.crds = crds
		self.connect = connect

		if filename != None:
			sys.stderr.write("Reading in new PDB %s\n" % filename)
			self.Read(filename)


## note: we read the anum here, which doesn't necessarily correspond to the actual
## record number.
	def Read(self, filename):
		pdbfile = open(filename)
		sys.stderr.write("Opened '%s' for reading as PDB\n" % filename)

		while(1):
			line = pdbfile.readline()

			if line == '':
				break

			if line[0:4] == 'ATOM' or line[0:6] == 'HETATM':
				type = string.strip(line[0:6])
				anum = string.atoi(string.strip(line[7:11]))
				atom = string.strip(line[12:17])
				residue = string.strip(line[17:20])
				chain = string.strip(line[21:22])
				rnum = string.atoi(string.strip(line[23:26]))
				x = string.atof(string.strip(line[31:38]))
				y = string.atof(string.strip(line[39:46]))
				z = string.atof(string.strip(line[47:54]))

				self.records.append(PDBRecord(type, anum, atom, residue, chain, rnum))
				self.crds.append((x,y,z))
				
		
		self.crds = Numeric.array(self.crds)

	def Write(self, filename):
		self.pdbfd=open(filename,"w")
			
		for i in range(len(self.records)):
			record = self.records[i]
			self.pdbfd.write("%-6s%5d %-4s%c%-4s%c%4d%c   %8.3f%8.3f%8.3f\n" % \
							 ( record.type, record.anum, record.atom,' ', record.residue,' ', record.rnum, ' ', 
							   self.crds[i][0], self.crds[i][1], self.crds[i][2]))

		self.pdbfd.write("TER\n")

		if self.connect != None:
			for i in self.connect:
				self.pdbfd.write("CONECT")
				for j in i:
					self.pdbfd.write("%5d" % (j+1))
				self.pdbfd.write("\n")

		self.pdbfd.write("END\n")
		self.pdbfd.close()


	def Rotate(self,alpha, beta, gamma, tx, ty, tz):
		r = rotmat(alpha, beta, gamma, tx, ty, tz)
		self.RotateMatrix(r)

	def RotateMatrix(self,r):
		for i in range(len(self.crds)):
			self.crds[i] = matrix_apply(self.crds[i], r)

	def Center(self):
		center = Numeric.add.reduce(self.crds) / len(self.crds)
		self.crds = Numeric.subtract(self.crds, center)

	def Print(self):
		for i in self.records:
			print i.type, i.anum, i.atom, i.residue, i.chain, i.rnum

	def ReturnAnum(self, atom, rnum):
		for i in range(len(self.records)):
			record = self.records[i]
			if record.atom == atom and record.rnum == rnum:
 				return i
		sys.stderr.write("Unable to find atom '%s' in residue %d\n" % (atom, rnum))
		return None

	def CrdByName(self, atom, res):
		x = self.ReturnAnum(atom, res)
		if x == None:
			return None
		return self.crds[x]

	def FixResNum(self):
		rnum = 1
		for i in range(len(self.records)):
			newrnum = rnum
			if i < len(self.records)-1 and \
			   self.records[i+1].rnum != self.records[i].rnum:
				rnum = rnum + 1
			self.records[i].rnum = newrnum
			self.records[i].anum = i

	def Copy(self):
		crds = Numeric.array(self.crds)
		records = copy.copy(self.records)
		return PDB(None, crds, records)


	def Append(self, struct2):
		records = self.records + copy.copy(struct2.records)
		crds = Numeric.concatenate((self.crds, struct2.crds))

		return PDB(None, crds, records)

