############################################################################
#
# Authors: Stefano Forli, Ruth Huey
#
# Copyright: M. Sanner TSRI 2010
#
#############################################################################

#
# $Header: /opt/cvs/python/packages/share1.5/MolKit/WaterBuilder.py,v 1.1 2010/10/07 17:28:48 rhuey Exp $
#
# $Id: WaterBuilder.py,v 1.1 2010/10/07 17:28:48 rhuey Exp $
#

"""
This module implements the WaterBuilder class which adds water atom records to pdb files 

"""

import numpy as np

from .utils import geomutils


class WaterBuilder:
    """Class for adding waters to a pdb file.
    """

    def __init__(self, space=3.0, bond_dist=1.85, pcharge=0.000, residue="WAT", ATYPE="W", verbose=False):
        self.space = space
        self.bond_dist = bond_dist
        self.pcharge = pcharge
        self.residue = residue
        self.ATYPE = ATYPE
        self.verbose = verbose

    def atom_coord(self, atom):
        return list(map(float, atom[28:56].split()))

    def dist(self, firstline, secondline, precision=4):  
        coord1, coord2 = list(map(self.atom_coord, [firstline, secondline]))
        return np.round(np.sqrt((coord1[0]-coord2[0])**2+(coord1[1]-coord2[1])**2+(coord1[2]-coord2[2])**2), precision)

    def mean_pdb(self, firstline, secondline):
        # INFO   : calculate the mean point between two PDB atom lines
        # INPUT  : two pdb lines, an number for residue and atom numbering
        # OUTPUT : a pdb line
        coord1 = self.atom_coord(firstline)
        coord2 = self.atom_coord(secondline)
        x = (coord1[0]+coord2[0])/2
        y = (coord1[1]+coord2[1])/2
        z = (coord1[2]+coord2[2])/2
        atype = firstline[12:16]
        residue = "MEA"
        self.chain = "Y"
        count = 1
        index = 1
        #?SHOULD THIS BE atype from line 58?
        mean_atom = "ATOM  %5d %4s %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00          %1s" % (count, atype, residue, self.chain, index, x, y, z, self.ATYPE)
        #mean_atom = "ATOM  %5d %4s %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00          %1s" % (count, self.ATYPE, residue, self.chain, index, x, y, z, self.ATYPE)
        return mean_atom

    def closest(self, first_atom, atom_list, cutoff=99999):
        # INFO   : find the self.closest atom 
        # INPUT  : a PDB atom, a list of PDB atoms [, cutoff self.distance]
        # OUTPUT : the self.closest atom [=null, if cutoff not satisfied] and short self.distance found
        # EXTRA  : self.dist function required
        best_self.distance = 999999
        best_candidate = None
        for second_atom in atom_list:
            self.distance = self.dist(first_atom, second_atom)
            if self.distance < best_self.distance:
                best_self.distance = self.distance
                if best_self.distance < cutoff:
                    best_candidate = second_atom
        #if best_candidate != "":
        #    return best_candidate, best_self.distance
        #else:
        #    return best_candidate, best_self.distance
        return best_candidate, best_self.distance

    def mean3(self, firstline, secondline, thirdline):
        # INFO   : calculate the mean point between two PDB atom lines
        # INPUT  : two pdb lines, an number for residue and atom numbering
        # OUTPUT : a pdb line
        coord1 = self.atom_coord(firstline)
        coord2 = self.atom_coord(secondline)
        coord3 = self.atom_coord(thirdline)
        x = (coord1[0]+coord2[0]+coord3[0])/3
        y = (coord1[1]+coord2[1]+coord3[1])/3
        z = (coord1[2]+coord2[2]+coord3[2])/3
        atype = firstline[12:16]
        residue = "MEA"
        chain = "Y"
        count = 1
        index = 1
        mean_atom = "ATOM  %5d %4s %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00          %1s" % (count, self.ATYPE, residue, chain, index, x, y, z, self.ATYPE)
        return mean_atom

    def hydro(self, atom1, atom2, atom3=None, spacing=3):
        # INFO   : place a water atom W at given self.distance from an atom
        # INPUT  : (a) two pdb lines for N-H; (b) three pdb lines for -[N|O]- acceptor, -O-H donors; the spacing self.distance between the water and the atom
        # OUTPUT : one pdb line 
        #
        # Synopsis:
        #
        # -C-NH-C  => atom1 = N, atom2 = H
        # -C-N=C   => atom1 = C, atom2 = N, atom3 = C
        # -C-O-H   => atom1 = C, atom2 = O, atom3 = H

        #if not atom3:
        #    atype = line.split()[-1]
        if atom2.split()[-1]=="HD":
            spacing -= 1 # to put the W at 3A from the N-H
        index = 99 
        coord2 = self.atom_coord(atom2)
        x =(coord2[0])
        y =(coord2[1])
        z =(coord2[2])
        if self.verbose: print(x, y, z)
        #atype = "W"
        #residue="WAT"
        # residue 17:20
        # chain 21
        chain = atom1[21]
        residue = atom1[17:20]
        #chain="X"
        if atom3:
            atom4 = self.mean_pdb(atom1, atom3)
            vec_module = self.dist(atom2, atom4)
            coord1 = self.atom_coord(atom4) 
        else:
            coord1 = self.atom_coord(atom1)
            vec_module = self.dist(atom1, atom2)
        alpha = np.arccos((coord2[0]-coord1[0])/vec_module)    # x-axis angle
        beta  = np.arccos((coord2[1]-coord1[1])/vec_module)    # y-axis angle
        gamma = np.arccos((coord2[2]-coord1[2])/vec_module)    # z-axis angle
        wat_x = spacing*np.cos(alpha)+x
        wat_y = spacing*np.cos(beta)+y
        wat_z = spacing*np.cos(gamma)+z
        wet = "%s%5d %2s   %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00     %1.3f %1s\n" % (self.keyw, index, self.ATYPE, residue, chain, index, wat_x, wat_y, wat_z, self.pcharge, self.ATYPE)
        #wet = "%s%5d %2s   %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00     %1.3f %1s\n" % (self.keyw, index, self.ATYPE, self.residue, chain, index, wat_x, wat_y, wat_z, self.pcharge, self.ATYPE)
        return wet

    def hydroH(self, atom1, atom2, atom3):
        middle = self.hydro(atom1, atom2, spacing = 0)
        avg = self.mean_pdb(middle, atom3)
        last = self.hydro(avg, atom2, spacing=self.space)
        if self.verbose:
            print("middle=", middle)
            print("avg=", avg)
            print("last=", last)

    def vector(self, p1, p2=None):
        # accept both atoms and self.vectors as input
        #
        #if p1 == 0:
        #    coord1 = [ 0.0, 0.0, 0.0 ]
        #else:
        if self.verbose: print("received ", p1, p2)
        if type(p1) == type(str()):
            p1 = self.atom_coord(p1)
        x1,y1,z1 = p1
        if type(p2) == type(str()):
            p2 = self.atom_coord(p2)
        if not p2 == None:
            x2,y2,z2 = p2
            vec_x = x2-x1
            vec_y = y2-y1
            vec_z = z2-z1
            # it must be an array
            vec = np.array([vec_x, vec_y, vec_z], 'f')
            if self.verbose: print("REAL VECTOR", vec)
        else:
            vec = np.array([p1[0], p1[1], p1[2] ], 'f' )
            if self.verbose: print("ATOM VECTOR", vec)
        return vec

    def bound(self, atom, structure, exclude=None):
        """
        Identify all the atoms in "structure" that are @bond_dist from "atom"
        """
        bound_list = []
        tolerance = 0
        bond_dist = self.bond_dist
        if atom.split()[-1] == "HD":
            bond_dist = 1.15 # previous bond self.dist of 1.1 wouldn't be big enough for -S-H
            if self.verbose: print("HD mode")
        for candidate in structure:
            if candidate==atom or candidate==exclude:
                pass
            else:
                if candidate[0:4]=="ATOM" or candidate[0:6]=="HETATM":
                    if candidate.split()[-1]=="SA" or candidate.split()[-1]=="S":
                        tolerance = .35
                    else:
                        tolerance = 0
                    if self.dist(atom, candidate)<=bond_dist+tolerance:
                        if not candidate in bound_list:
                            bound_list.append(candidate)
                    else:
                        pass
        if len(bound_list) > 0:
            return bound_list
        else:
            print("ERROR: this atom seems to be disconnected:")
            print(atom)
            print("exit 3")
            exit(1)

    def furanbolic(self, atom, structure, max=2.35):
        # it should walk with pre-filtered co-planar atoms
        # HD's are automatically excluded
        the_ring = [atom]
        #print "I WAS REQUESTED TO OPERATE ON ", atom
        #print "I WILL SEARCH ON %d ATOMS"% len(structure)
        for item in structure:
            if item!=atom:
                if item.split()[-1]!="HD":
                    if self.dist(atom, item)<max:
                        the_ring.append(item)
                        if self.verbose:
                            print("APPEND")
                    #else:
                        #print "failed self.distance"
                        #print item
        #print "\n ########## FURANBOLIC CHECKPOINT ### \n"
        #print the_ring
        #print len(the_ring)
        if len(the_ring)==5:
            print(" - possible furan/oxazole found...")
            return True
        if len(the_ring)>6:
            print("WARNING: multiple atoms match the furan/oxazole check...")
            return True
        else:
            return False

    def Osp2(self, oxygen, atom1, atom2):
        waters = []
        # self.hydroxyl/ether mode
        angles=[120, -120]
        #angles = range(0, 360, 10)
        oxyvector = self.vector(oxygen, atom1)
        oxyvector = geomutils.normalize(oxyvector)
        residue = atom1[17:20]
        chain = atom1[21]
        for a in angles:
            roto = [oxyvector[0], oxyvector[1], oxyvector[2],  np.radians(a)]
            lone_pair_vector = self.vector(atom2, oxygen)
            lone_pair_vector = geomutils.normalize(lone_pair_vector)
            water =  geomutils.rotate_around_axis(-lone_pair_vector*self.space, roto, self.atom_coord(oxygen))  
            #residue = "99"
            #chain = "1"
            wet = "%s%5d %2s   %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00     %1.3f %1s\n"%(self.keyw, 1, self.ATYPE, residue, chain, 1, water[0], water[1], water[2], self.pcharge, self.ATYPE)
            #wet = "%s%5d %2s   %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00     %1.3f %1s\n"%(self.keyw, 1, self.ATYPE, residue, chain, 1, water[0], water[1], water[2], self.pcharge, self.ATYPE)
            waters.append(wet)
        if self.verbose: print("osp2 returning %d waters"%(len(waters))) 
        return waters

    def Osp2_NEW(self, oxygen, atom1, atom2):
        waters = []
        # self.hydroxyl/ether mode
        #angles = [120, -120]
        angles = list(range(0, 360, 10))
        oxyvector = self.vector(oxygen, atom1)
        oxyvector = self.vector(atom1, atom2)
        oxyvector = geomutils.normalize(oxyvector)
        mid = self.mean_pdb(atom1, atom2)
        residue = atom1[17:20]
        chain = atom1[21]
        for a in angles:
            roto = [oxyvector[0], oxyvector[1], oxyvector[2],  np.radians(a)]
            lone_pair_vector = self.vector(mid, oxygen)
            lone_pair_vector = geomutils.normalize(lone_pair_vector)
            water =  geomutils.rotate_around_axis(+lone_pair_vector*self.space, roto, self.atom_coord(oxygen))  
            #residue = "99"
            #chain = "1"
            # change to self.residue? replacing 99 by WAT??
            wet = "%s%5d %2s   %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00     %1.3f %1s\n"%(self.keyw, 1, self.ATYPE, residue, chain, 1, water[0], water[1], water[2], self.pcharge, self.ATYPE)
            waters.append(wet)
        if self.verbose: print("Osp2_NEW returning %d waters"%(len(waters))) 
        return waters

    def hydrate(self, mol, extended_atoms=False):
        added_waters = []
        atoms_list = []
        hydrate_list = []
        numbering_stuff = []
        water_mates = []
        self.EXTENDED_ATOMS = extended_atoms

        input = open(pdbqt, 'r').readlines()
        for line in input:
            if line[0:4] == "ATOM" or line[0:6] == "HETATM":
                atype = line.split()[-1]
                atoms_list.append(line)
                if atype == "OA" or atype == "NA" or atype == "HD":
                    hydrate_list.append(line)
            if line[0:4] == "ATOM":
                self.keyw = "ATOM  "
            if line[0:6] == "HETATM":
                self.keyw = "HETATM"

        # Scan the list to add waters
        for atom in hydrate_list:
            atype = atom.split()[-1]
            if self.verbose:
                print("PROCESSING ATOM :", atom)
                print("atype = ", atype)
            HYDROXYL = False
            # ordinal position in the original file
            position = int(atom.split()[1])
            # add the atom to be hydrated to the buffer list
            waters_generated = [atom]  #@@reinitialize waters_generated here
            if self.verbose:
                print("processing ", atom)
                print("0: water_mates=", water_mates)
                print("1: len(waters_generated)=", len(waters_generated))
                print("2:  len(water_mates=", len(water_mates))
            # find the master atom(s)
            master = self.bound(atom, atoms_list)
            if len(master) == 0:
                print("\n\nERROR: this atom is disconnected:\n", atom)
                #??return() instead??@@
                print("exit 0")
                exit(1)
            # HYDROGENS #####################################################################################
            #print "before HD , len(master)=", len(master), " atype=", atype
            if atype == "HD":
                # check for errors
                if len(master) > 1:
                    print("\n\nERROR (HD) : there is a proximity error and the following hydrogen is in close contact with more than one atom")
                    print(atom)
                    print("Bound mates:")
                    for m in master:
                        print(m[:-1]," ==>", self.dist(m,atom))
                    #??return() instead??@@
                    print("exit 1")
                    exit(1)
                else:
                    # calculate the Water vector
                    wet = self.hydro(master[0], atom) #@@REPAIR THIS apparent duplicate!!!
                    if self.verbose: 
                        print("after call to hydro, wet=",wet)
                    waters_generated.append(wet)
                    if self.verbose:
                        print("HD: added wet: now %d waters_generated:" %len(waters_generated))
                        for ind in waters_generated: print(ind)
                        print("HD: added w_g to w_m: previously %d water_mates:" %len(water_mates))
                        for ind in water_mates: print(ind)
                        print("HD: 2: %d waters_generated:" %len(waters_generated))
                        for ind in waters_generated: print(ind)
                water_mates.append(waters_generated)
                if self.verbose: print("HD: 2: NOW %d water_mates:" %len(water_mates))
                #numbering_stuff has an entry for for each entry in water_mates
                numbering_stuff.append([position, (len(waters_generated)-1)] )

            # OXYGENS #####################################################################################
            if atype == "OA":
                # OA includes the following options:
                #
                ## Two mates
                #  ---------
                #
                # -X-OA-X-    (ethers)
                #
                # -X-OA-HD    (hydroxyls)
                #
                # [-X-OA-X-]  (furan/pyran like)
                #
                #
                ## One mate
                # ----------
                #
                # -X=O        (carbonyl, carboxylate, nitro)   [ DONE]
                #
                #
                #  |
                # -X=O        (sulphonyl, phosphonyl) 
                #  |
                #
                if len(master)==1:
                    # # identify the mates of the master
                    MASTER = master[0]
                    residue = MASTER[17:20]
                    chain = MASTER[21]
                    mates = self.bound(MASTER, atoms_list, exclude=atom)
                    #print "masters", master
                    #print "mates", mates
                    # Phosphates check
                    if len(mates) <=2:
                        # 1. calculate the plane
                        v12 = self.vector(atom, MASTER)
                        v23 = self.vector(mates[0],MASTER) 
                        #plane = np.cross(v12, v23)
                        plane0,plane1,plane2 = geomutils.normalize(np.cross(v12,v23))
                        self.chain = 1 # TODO read this from the atom
                        #keyw, index, ATYPE, residue, chain, pcharge = "ATOM  ", 1, "C", "RES", 1, 0.0000
                        #print "CARBONYL"
                        #print "%s%5d %2s   %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00     %1.3f %1s" % (keyw, index, ATYPE, residue, chain, index, plane[0], plane[1], plane[2], pcharge, ATYPE)

                        # O-lone pair 1
                        roto = [ plane0, plane1, plane2, np.radians(50) ]
                        wat = geomutils.rotate_around_axis(geomutils.normalize(-v12)*self.space, roto, self.atom_coord(atom))
                        wet = "%s%5d %2s   %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00     %1.3f %1s\n" % (self.keyw, 1, self.ATYPE, residue, chain, 1, wat[0], wat[1], wat[2], self.pcharge, self.ATYPE)
                        #wet = "%s%5d %2s   %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00     %1.3f %1s\n" % (self.keyw, 1, self.ATYPE, self.residue, self.chain, 1, wat[0], wat[1], wat[2], self.pcharge, self.ATYPE)
                        if self.verbose:
                            print("O1: wet:") 
                            print(wet)
                        waters_generated.append(wet)
                        if self.verbose:
                            print("waters_generated:")
                            print(waters_generated)
                            print("O1: waters_generated=", end=' ') 
                            for ind in waters_generated: print(ind)
            
                        # O-lone pair 2
                        roto = [ plane0, plane1, plane2, np.radians(-50) ]
                        if self.verbose: print("calling rotate_around_axis with self.atom_coord(", atom, "), roto=",roto)
                        wat = geomutils.rotate_around_axis(geomutils.normalize(-v12)*self.space, roto, self.atom_coord(atom))
                        if self.verbose: print("wat =", wat)
                        wet = "%s%5d %2s   %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00     %1.3f %1s\n" % (self.keyw, 1, self.ATYPE, residue, chain, 1, wat[0], wat[1], wat[2], self.pcharge, self.ATYPE)
                        #wet = "%s%5d %2s   %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00     %1.3f %1s\n" % (self.keyw, 1, self.ATYPE, self.residue, self.chain, 1, wat[0], wat[1], wat[2], self.pcharge, self.ATYPE)
                        waters_generated.append(wet)
                        if self.verbose:
                            print("O2: %d wet: %s" %(len(wet), wet))
                            print("O2: %d waters_generated="%(len(waters_generated)), end=' ') 
                            for ind in waters_generated: print(ind)
                        water_mates.append(waters_generated)
                        if self.verbose:
                            print("O2: %d water_mates="%(len(water_mates)), end=' ') 
                            for ind in water_mates: print(ind)
                        numbering_stuff.append([int(position), len(waters_generated)-1])
                        #print waters_generated
                    else:
                        if self.EXTENDED_ATOMS:
                            #if self.verbose:
                                #print "master", master[0]
                                #print "mates", mates
                                #print atom
                                #print "Possible phosphate/sulphate atom... I can't stand Phosphates... GIVING UP"
                                #print "\n\n[ we're not equipped to manage those atoms... yet ]"
                                #print "\n\n[ we're testing to manage those atoms...EXPERIMENTAL!  ]"
                            #chain = 1
                            #self.residue = 1 
                            #print "\n\n  ############## TESTING FACILITY #########"
                            directive = self.vector(master[0],atom)
                            directive = geomutils.normalize(directive)*self.space
                            #if self.verbose: print "%s%5d %2s   %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00     %1.3f %1s\n" % (keyw, 1, self.ATYPE, residue, chain, 1, directive[0], directive[1], directive[2], pcharge, self.ATYPE)

                            for q in mates:
                                #print q
                                position_q = self.vector(q) 
                                position_q = geomutils.vecSum(position_q, directive)
                                push = geomutils.normalize(self.vector(atom, position_q))
                                start = self.vector(atom)
                                lpair = geomutils.vecSum(start, push*self.space)
                                wet = "%s%5d %2s   %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00     %1.3f %1s\n" % (self.keyw, 1, self.ATYPE, residue, chain, 1, lpair[0], lpair[1], lpair[2], self.pcharge, self.ATYPE)
                                #wet = "%s%5d %2s   %3s %1s%4d    %8.3f%8.3f%8.3f  1.00 10.00     %1.3f %1s\n" % (self.keyw, 1, self.ATYPE, self.residue, self.chain, 1, lpair[0], lpair[1], lpair[2], self.pcharge, self.ATYPE)
                                if self.verbose:
                                    print("EA: wet=", end=' ') 
                                    for ind in wet: print(ind)
                                #position_q = normalize(position_q)
                                #ACTUAL = normalize(-vector(atom, position_q))*space
                                waters_generated.append(wet)
                                if self.verbose:
                                    print("EA: waters_generated=", end=' ') 
                                    for ind in waters_generated: print(ind)
                                #if self.verbose: print "EXT ATOMS: waters_generated=", waters_generated
                            water_mates.append(waters_generated)
                            if self.verbose: 
                                print("661:EA: %d water_mates:"%(len(water_mates)), end=' ') 
                                for ind in water_mates: 
                                    for j in ind:
                                        print(j)
                            #if self.verbose: print "         : water_mates=", water_mates
                            numbering_stuff.append([int(position), len(waters_generated)-1])
                            #print "\n\n  ############## TESTING FACILITY #########"
                            #exit(1)
                if len(master) == 2:
                    #print "---[ MODE 2 ]---"
                    for m in master:
                        if m.split()[-1] == "HD":
                            HYDROXYL = True
                            #print " >>> Found hydroxyl"
                    if not HYDROXYL:
                        # calculate the plane formed by oxygen and the two masters
                        O_plane = geomutils.calcPlane(atom, master[0], master[1])
                        # get the list of all the co-planar atoms in the structure
                        coplanar_mates = geomutils.coplanar(O_plane, atoms_list, atom)
                        #print "COPLANAR_MATES", len(coplanar_mates)
                        #print "FURANBOLIC", furanbolic(atom, coplanar_mates)
                        # check if there are at least 4 coplanar atoms to make a ring with the OA
                        if len(coplanar_mates) >=4 and self.furanbolic(atom, coplanar_mates):    
                            wet = self.hydro(master[0], atom, master[1]) #2: @@REPAIR THIS apparent duplicate!!!
                            if self.verbose:
                                print("notHYDROXYL: wet=", end=' ') 
                                for ind in wet: print(ind)
                            waters_generated.append(wet)
                            if self.verbose:
                                print("notHYDROXYL: waters_generated=", end=' ') 
                                for ind in waters_generated: print(ind)
                            #if self.verbose:
                            #    print "NOT_HYDROXYL: waters_generated=", waters_generated
                            #print "FURAN MODE"
                        else:
                            if self.verbose: print("FURAN MODE FAILED")
                            lp_waters = self.Osp2(atom, master[0], master[1])
                            if self.verbose:
                                print("lp_waters:", end=' ')
                                for ind in lp_waters: print(ind)
                            for w in lp_waters:
                                waters_generated.append(w)
                            if self.verbose:
                                print("else in not HYDROXYL: waters_generated=", end=' ') 
                                for ind in waters_generated: print(ind)
                                print("NOT_COPLANAR ELSE 1: waters_generated=", waters_generated)
                    else:
                        #print "HYDROXYL/ETHER MODE"
                        lp_waters = self.Osp2(atom, master[0], master[1])
                        if self.verbose: print("lp_waters:")
                        for w in lp_waters:
                            if self.verbose:print(w)
                            waters_generated.append(w)
                        if self.verbose:
                            print("ELSE: waters_generated=", end=' ') 
                            for ind in waters_generated: print(ind)
                            print("before adding else of not HYDROXYL, water_mates=", end=' ')
                            for ind in water_mates: print(ind)
                    water_mates.append(waters_generated)
                    if self.verbose:
                        print("LAST 1: waters_generated=", end=' ') 
                        for ind in waters_generated: print(ind)
                        print("LAST 1: water_mates =", end=' ') 
                        for wm in water_mates : print(wm)
                        print("LAST 1: waters_generated=", waters_generated)
                        print("LAST 1: water_mates =", water_mates) 
                    numbering_stuff.append([int(position), len(waters_generated)-1])

            # NITROGEN #####################################################################################
            if atype == "NA":
                if len(master) == 1:
                    # calculate the Water vector
                    wet = self.hydro(master[0], atom) #3: @@REPAIR THIS APPARENT DUPLICATE??
                    waters_generated.append(wet)
                    water_mates.append(waters_generated)
                    if self.verbose:
                        print("NA 1: wet=", end=' ') 
                        for ind in wet: print(ind)
                        print("NA 1: waters_generated=", end=' ') 
                        for ind in waters_generated: print(ind)
                        print("NA 1: water_mates=", end=' ') 
                        for ind in water_mates: print(ind)
                    numbering_stuff.append([int(position), len(waters_generated)-1])

                # nitrile mode
                if len(master) == 2:
                    wet = self.hydro(master[0], atom, master[1]) #4: @@REPAIR THIS APPARENT DUPLICATE4??
                    if self.verbose: print("nitrile mode: wet:", wet)
                    #for ind in wet: print ind
                    waters_generated.append(wet)
                    if self.verbose: 
                        print("N: waters_generated:", end=' ')
                        for ind in waters_generated: print(ind)
                    water_mates.append(waters_generated)
                    if self.verbose: 
                        print("N: water_mates:", end=' ')
                        for ind in water_mates: print(ind)
                        print("NA 2: wet=", wet)
                        print("NA 2: waters_generated=", waters_generated)
                        print("NA 2: water_mates=", water_mates)
                    numbering_stuff.append([int(position), len(waters_generated)-1])
                    if self.verbose:
                        print("nitrile numbering_stuff:", end=' ')
                        for ind in numbering_stuff: print(ind)

                # tertiary amine HB receptor
                if len(master) == 3: 
                    #print "EXPERIMENTAL! potential tertiary amine"
                    master_center = self.mean3(master[0], master[1], master[2])
                    wet = self.hydro(master_center, atom) #5: @@REPAIR THIS APPARENT DUPLICATE5??
                    if self.verbose: 
                        print("tertiary amine: wet=", end=' ')
                        for ind in wet: print(ind)
                    waters_generated.append(wet)
                    if self.verbose: 
                        print("tertiary amine: waters_generated=", end=' ')
                        for ind in waters_generated: print(ind)
                    water_mates.append(waters_generated)
                    if self.verbose: 
                        print("tertiary amine: water_mates=", end=' ')
                        for ind in water_mates: print(ind)
                        print("NA 3: wet=", wet)
                        print("NA 3: waters_generated=", waters_generated)
                        print("NA 3: water_mates=", water_mates)
                    numbering_stuff.append([int(position), len(waters_generated)-1])
                    if self.verbose: print(numbering_stuff[-1], ' added to numbering_stuff')


        ctr = 0
        for mates in water_mates:
            index = input.index(mates[0])
            if self.verbose: 
                print("mates=", mates)
                print("mates[0]=", mates[0])
            line = ""
            for atom in mates:
                line += atom
            if self.verbose: print("@@ %d input line was = %s"%(index,  input[index]))
            input[index] = line
            if self.verbose:
                print("now %d input line set to %s"%(index,  line))
                print("~~~numbering_stuff[%d]=%s"%(ctr, numbering_stuff[ctr]))
            ctr+=1
            #if index==20: raise 'abc'
        count = 1
        # nicely split and format the lines that need to be splitted from
        # the previous addition of water molecules
        final = []
        if self.verbose: print("len(input)=", len(input), ' len(final)=', len(final))
        for line in input:
            line = line.split("\n")
            if self.verbose:
                print("ZZZZ: line=", end=' ') 
                for  YYYY in line: print(YYYY)
            if len(line)==3 and line[-1]!='': 
                #raise 'THREE!'
                line = line.remove(line[1])
            ct = 0
            for item in line:
                #@@@FIX THIS HACK!!!
                if not item == "" and item.find("WAT")<0 and item.find("W    99 1")<0:
                    final.append(item)
                    if len(item)>13 and item[13]=='W':
                        if self.verbose: print("after adding W ", item," len(final)=", len(final))
                    ct += 1
        if self.verbose: print("len(input)=", len(input), ' len(final)=', len(final))

        # renumbering the PDBQT atoms
        for line in final:
            if self.verbose: print("at top line=", line, " contains WAT ", line.find("WAT")>0)
            if line.find("WAT")<0 and (line[0:4] == "ATOM" or line[0:6] == "HETATM"):
                value = "%4s" % count
                idx = final.index(line)
                final[idx] = line[0:7]+value+line[11:]
                count += 1
                if self.verbose: print("in loop line=", line)

        # process the BRANCH numbers
        for line in final:
            idx = final.index(line)
            if "BRANCH" in line:
                #print "Processing line => ", line
                line = line.split()
                value1, value2 = int(line[1]), int(line[2])
                #print "BEFORE =>", value1, value2 
                addendum1 = 0
                addendum2 = 0
                for mark in numbering_stuff:
                    #print "Mark is :\t POSITION:", mark[0], " COUNT", mark[1]
                    if value1 > mark[0]:
                        addendum1 += mark[1]
                        #print "value 1 is bigger than mark[0]:", value1, " | ", mark[0]
                    if value2 > mark[0]:
                        addendum2 += mark[1]
                        #print "value 2 is bigger than mark[0]:", value2, " | ", mark[0]
                value1 += addendum1
                value2 += addendum2
                #print "AFTER  =>", value1, value2
                #print "- - - - - "
                final[idx] = line[0]+" "+str(value1)+" "+str(value2)
