content: package com.actelion.research.chem.descriptor.pharmacophoretree;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  import java.util.stream.Collectors;
  
  import com.actelion.research.chem.AtomFunctionAnalyzer;
  import com.actelion.research.chem.RingCollection;
  import com.actelion.research.chem.StereoMolecule;
  
  
  public class IonizableGroupDetector2D {
  	
  	private StereoMolecule mol;
  	private List<ArrayList<Integer>> ionizableGroups;
  	private Set<Integer> positivelyIonizableAtoms;
  	private Set<Integer> negativelyIonizableAtoms;
  	
  	public IonizableGroupDetector2D(StereoMolecule mol) {
  		this.mol = mol;
  		ionizableGroups = new ArrayList<ArrayList<Integer>>();
  		positivelyIonizableAtoms = new HashSet<Integer>();
  		negativelyIonizableAtoms = new HashSet<Integer>();
  	}
  	
  	public void detect() {
  		ArrayList<Integer> ionizableGroup;
  		RingCollection ringCollection = mol.getRingSet();
  		//detect tetrazoles
  		for(int r=0;r<ringCollection.getSize();r++) {
  			ArrayList<Integer> tetrazole = new ArrayList<Integer>();
  			int[] ringAtoms = ringCollection.getRingAtoms(r);
  			for(Integer atom : ringAtoms) {
  				if(alreadyDetected(atom)) continue;
  				if(mol.getAtomicNo(atom)==7 && mol.isAromaticAtom(atom) && mol.getConnAtoms(atom)<=2)
  					tetrazole.add(atom);
  			}
  			if(tetrazole.size()==4) {
  				ionizableGroups.add(tetrazole);
  				negativelyIonizableAtoms.add(tetrazole.get(0));
  			}
  			
  			
  		}
  		for(int a=0;a<mol.getAtoms();a++) {
  			if(alreadyDetected(a)) continue;
  			if(mol.getAtomicNo(a)==8) { //oxygen
  				int aa = mol.getConnAtom(a,0);
  				if(alreadyDetected(aa)) continue;
  				if(AtomFunctionAnalyzer.isAcidicOxygen(mol, a)) { //COOH,SO3H,PO3H2, N(+)-OH
  					if(mol.getAtomicNo(aa)==6) { //COOH
  						ionizableGroup = new ArrayList<Integer>();
  						ionizableGroup.add(a);
  						ionizableGroup.add(aa);
  						int aaa1 = mol.getConnAtom(aa, 0);
  						int aaa2 = mol.getConnAtom(aa, 1);
  						int aaa3 = mol.getConnAtom(aa, 2);
  						int aaa = (aaa1!=a && mol.getAtomicNo(aaa1)==8) ? aaa1 : 
  							(aaa2!=a && mol.getAtomicNo(aaa2)==8) ? aaa2 : 
  								aaa3;
  						if(alreadyDetected(aaa)) continue;
  						ionizableGroup.add(aaa);
  						ionizableGroups.add(ionizableGroup);
  						negativelyIonizableAtoms.add(aa);
  						continue;
  					}
  					else if (mol.getAtomicNo(aa)==15) {//POO3H2
  						ionizableGroup = new ArrayList<Integer>();
  						ionizableGroup.add(a);
  						ionizableGroup.add(aa);
  						for(int i=0;i<mol.getConnAtoms(aa);i++) {
  							int aaa = mol.getConnAtom(aa, i);
  							if(mol.getAtomicNo(aaa) ==8 && aaa!=a) {
  								if(alreadyDetected(aaa)) continue;
  								ionizableGroup.add(aaa);
  							}
  							
  						}
  						ionizableGroups.add(ionizableGroup);
  						negativelyIonizableAtoms.add(aa);
  						continue;					
  					}
  					else if (mol.getAtomicNo(aa)==16) {//SOO3H
  						ionizableGroup = new ArrayList<Integer>();
  						ionizableGroup.add(a);
  						ionizableGroup.add(aa);
  						for(int i=0;i<mol.getConnAtoms(aa);i++) {
  							int aaa = mol.getConnAtom(aa, i);
  							if(mol.getAtomicNo(aaa) ==8 && aaa!=a) {
  								if(alreadyDetected(aaa)) continue;
  								ionizableGroup.add(aaa);
  							}
  							
  						}
  						ionizableGroups.add(ionizableGroup);
  						negativelyIonizableAtoms.add(aa);
  						continue;					
  					}
  				}
  				}
  				else if(mol.getAtomicNo(a)==7) {
  					if(mol.isAromaticAtom(a))
  						continue;
  					if(mol.getConnAtoms(a)<=2) { //HNR2 or H2NR
  							int nDBs = 0;
  							boolean found=false;
  							for(int i=0;i<mol.getConnAtoms(a) && !found;i++) { //search for amidine
  								int aa = mol.getConnAtom(a, i);
  								if(alreadyDetected(aa)) continue;
  								if(mol.getAtomicNo(aa)==6) {
  									if(mol.getBondOrder(mol.getBond(a, aa))==2)nDBs++;
  									for(int j=0;j<mol.getConnAtoms(aa) && !found;j++) {
  										int aaa = mol.getConnAtom(aa, j);
  										if(mol.isAromaticAtom(aaa))
  											continue;
  										if(aaa==a) continue;
  										if(alreadyDetected(aaa)) continue;
  										if(mol.getAtomicNo(aaa)==7 && mol.getConnAtoms(aaa)<=2) {;
  											if(mol.getBondOrder(mol.getBond(aa, aaa))==2)nDBs++;
  											if(nDBs==1) { //Amidine
  												ionizableGroup = new ArrayList<Integer>();
  												ionizableGroup.add(a);
  												ionizableGroup.add(aa);
  												ionizableGroup.add(aaa);
  												ionizableGroups.add(ionizableGroup);
  												positivelyIonizableAtoms.add(aa);
  												found = true;
  											}
  												
  										}
  									
  								}
  							}
  							
  					}
  					
  				}
  					if(alreadyDetected(a))continue;
  					if(AtomFunctionAnalyzer.isBasicNitrogen(mol, a)) {
  						ionizableGroup = new ArrayList<Integer>();
  						ionizableGroup.add(a);
  						ionizableGroups.add(ionizableGroup);
  						positivelyIonizableAtoms.add(a);
  						continue;
  					}
  			}
  			if(alreadyDetected(a))continue;
  			else {
  				int charge = mol.getAtomCharge(a);
  				if(charge!=0 && !hasCounterChargedNeighbour(a)) {
  					if(charge>0)
  						positivelyIonizableAtoms.add(a);
  					else
  						negativelyIonizableAtoms.add(a);
  
  				}
  			}
  		}
  	}
  		
  
  	
  	private boolean hasCounterChargedNeighbour(int a) {
  		for(int aa=0;aa<mol.getConnAtoms(a);aa++) {
  			if(mol.getAtomCharge(a)*mol.getAtomCharge(mol.getConnAtom(a,aa))<0)
  				return true;
  		}
  		return false;
  	}
  	
  	private boolean alreadyDetected(int a) {
  		boolean isDetected = ionizableGroups.stream().flatMap(List::stream).collect(Collectors.toList()).contains(a) ? true : false;
  		return isDetected;
  	}
  	
  	public Set<Integer> getPosIonizableAtoms() {
  		return positivelyIonizableAtoms;
  	}
  	
  	public Set<Integer> getNegIonizableAtoms() {
  		return negativelyIonizableAtoms;
  	}
  
  
  }
  