package com.actelion.research.chem.contrib;

import java.util.Arrays;
import java.util.Vector;

import com.actelion.research.chem.Canonizer;
import com.actelion.research.chem.ExtendedMolecule;
import com.actelion.research.chem.IDCodeParser;
import com.actelion.research.chem.Molecule;
import com.actelion.research.chem.StereoMolecule;

public class HoseCodeCreator {

	public final static int FULL_HOSE_CODE=0;
	public final static int HOSE_CODE_CUT_C_SP3_SP3=1;
	
	final static boolean DEBUG=false;
	
	/** 
	 * This descriptor requires proper up/down bonds, because it encodes stereo parities. 
	 * If a passed molecule is generated from idcode parsing, make sure that coordinates 
	 * and up/down/bonds are available, i.e. that the IDCodeParser was instantiated with 
	 * the respective option. 
	 */ 
	public static String[][] getHoseCodes(StereoMolecule mol, int maxSphereSize, int type) {
		String[][] ids=new String[mol.getAtoms()][maxSphereSize];
		mol.ensureHelperArrays(Molecule.cHelperRings); 

		for (int rootAtom=0; rootAtom<mol.getAtoms(); rootAtom++) { 
			ids[rootAtom]=getHoseCodesForAtom(mol, rootAtom, maxSphereSize, type);
			
		}
		return ids;
	}
	
	private static boolean isCsp3(ExtendedMolecule mol, int atomID) {
		if (mol.getAtomicNo(atomID)!=6) return false;
		if (mol.getAtomCharge(atomID)!=0) return false;
		if ((mol.getImplicitHydrogens(atomID)+mol.getConnAtoms(atomID))!=4) return false;
		return true;
	}
	
	private static String[] getHoseCodesForAtom(StereoMolecule mol, int rootAtom, int maxSphereSize, int type) {
		StereoMolecule fragment = new StereoMolecule(mol.getAtoms(), mol.getBonds()); 
		Vector<String> ids=new Vector();
		int min = 0; 
		int max = 0;
		boolean[] atomMask = new boolean[mol.getAtoms()];
		int[] atomList = new int[mol.getAtoms()]; 
		for (int sphere=0; sphere<maxSphereSize && max<mol.getAtoms(); sphere++) { 
			if (max == 0) { 
				atomList[0] = rootAtom; 
				atomMask[rootAtom] = true; 
				max = 1; 
			} 
			else { 
				int newMax = max; 
				for (int i=min; i<max; i++) { 
					int atom = atomList[i]; 
					for (int j=0; j<mol.getConnAtoms(atom); j++) {
						int connAtom = mol.getConnAtom(atom, j); 
						if (DEBUG) System.out.println("---> "+atom+" to "+connAtom);
						if (!atomMask[connAtom]) {
							switch (type) {
							case FULL_HOSE_CODE:
								atomMask[connAtom] = true; 
								atomList[newMax++] = connAtom;
								break;
							case HOSE_CODE_CUT_C_SP3_SP3:
								if ( ! (isCsp3(mol, atom) && isCsp3(mol,connAtom))) {
									if (DEBUG) System.out.println("NO SKIP");
									atomMask[connAtom] = true; 
									atomList[newMax++] = connAtom;
								} else {
									if (DEBUG) System.out.println("SKIP");
								}
								break;
							}
						} 
					} 
				} 
				min = max; 
				max = newMax; 
			} 

			mol.copyMoleculeByAtoms(fragment, atomMask, true, null); 

			// TO GET ONLY THE SKELETON
			/*
			for (int atom=0; atom<fragment.getAllAtoms(); atom++)  {
				fragment.setAtomicNo(atom, 6); 
			}
			*/
			
			ids.add(new Canonizer(fragment, Canonizer.ENCODE_ATOM_CUSTOM_LABELS).getIDCode()); 
		}
		return ids.toArray(new String[ids.size()]);
	}

	public static String[] getHoseCodesFromDiaID(String diastereotopicID, int maxSphereSize, int type) {
		// We need atom coordinates to properly determine stereo features of fragments later
		StereoMolecule molecule= new IDCodeParser(true).getCompactMolecule(diastereotopicID);
		// One of the atom has to be marked !
		int atomID=-1;
		for (int i=0; i<molecule.getAllAtoms(); i++) {
			// we need to find the marked atom
			String atomCustomLabel=molecule.getAtomCustomLabel(i);
			if (atomCustomLabel!=null && atomCustomLabel.endsWith("*")) {
				atomID=i;
				break;
			};
		}
		if (atomID>=0) {
			return HoseCodeCreator.getHoseCodesForAtom(molecule, atomID, maxSphereSize, type);
		}
		return new String[0];
	}
	
	public static void main(String[] args) {
		StereoMolecule molecule= new IDCodeParser(false).getCompactMolecule("deT@@DjU_k``b`@@");
		StereoMolecule otherMolecule = new StereoMolecule(molecule.getAtoms(), molecule.getBonds()); 
		boolean[] atomMask = new boolean[molecule.getAtoms()];
		Arrays.fill(atomMask, true);
		molecule.copyMoleculeByAtoms(otherMolecule, atomMask, true, null);
		System.out.println(new Canonizer(otherMolecule, Canonizer.ENCODE_ATOM_CUSTOM_LABELS).getIDCode()); 		
		
		// String id="deT@`@f\bbbRK]@PT@@";
		String id="fi{qa@DyZkQPSI`cHhhdhdhddhekF\\\\fNXBBjfjjjaXTh@RB@QJh";
		
		String[] hoses= HoseCodeCreator.getHoseCodesFromDiaID(id, 20, FULL_HOSE_CODE);
		for (int i=0; i<hoses.length; i++) {
			System.out.println(hoses[i]);
		}
		
		hoses= HoseCodeCreator.getHoseCodesFromDiaID(id, 8, HOSE_CODE_CUT_C_SP3_SP3);
		for (int i=0; i<hoses.length; i++) {
			System.out.println(hoses[i]);
		}		
	}
}
