/*---------------------------------------------------------------------------*\
**$Author: antanas $
**$Date: 2021-02-25 22:01:31 +0200 (Thu, 25 Feb 2021) $
**$Revision: 255 $
**$URL: svn+ssh://www.crystallography.net/home/coder/svn-repositories/smiles-scripts/tags/v0.2.0/src/cdkcif2smiles.java $
\*---------------------------------------------------------------------------*/

// This CLI Java program reads a CIF (Crystallographic Information
// File, https://www.iucr.org/resources/cif), restores chemical bonds
// from interatomic distances and writes out the connected atom graph
// as a OpenSMILES (http://opensmiles.org/spec/open-smiles.html)
// string.

import org.openscience.cdk.ChemFile;
import org.openscience.cdk.aromaticity.Aromaticity;
import org.openscience.cdk.aromaticity.ElectronDonation;
import org.openscience.cdk.config.AtomTypeFactory;
import org.openscience.cdk.graph.CycleFinder;
import org.openscience.cdk.graph.Cycles;
import org.openscience.cdk.graph.rebond.RebondTool;
import org.openscience.cdk.interfaces.*;
import org.openscience.cdk.io.CIFReader;
import org.openscience.cdk.silent.SilentChemObjectBuilder;
import org.openscience.cdk.smiles.SmiFlavor;
import org.openscience.cdk.smiles.SmilesGenerator;

import java.io.FileReader;
import java.util.Hashtable;

import static net.crystallography.helpers.cdk.OrthogonaliseAtomCoordinates.computeOrthogonalCoordinates;
import static org.openscience.cdk.tools.manipulator.AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms;
import static org.openscience.cdk.tools.manipulator.AtomContainerManipulator.removeHydrogens;

@SuppressWarnings("ALL")
public class cdkcif2smiles {

    static OptionValue smilesOption = new OptionValue();
    static Option options[] = {
            new VersionOption     ( null, "--version", OptionType.OT_FUNCTION ),
            new CDKCif2SmilesHelp ( null, "--help",    OptionType.OT_FUNCTION ),
    };

    private static final String progName = CDKCif2SmilesHelp.progName;

    @SuppressWarnings("SingleStatementInBlock")
    public static void main(String[] argv ) {
        String filenames[] = null;
        try {
            filenames = SOptions.get_options( argv, options );
        }
        catch (SOptionsException e) {
            System.err.println( progName + ": " + e.getMessage() );
            System.exit(1);
        }

        if( filenames == null || filenames.length == 0 ) {
            filenames = new String[] { "-" };
        }
        for( String filename : filenames ) {
            try {
                CIFReader reader;
                if (filename.equals("-")) {
                    reader = new CIFReader(System.in);
                } else {
                    reader = new CIFReader(new FileReader(filename));
                }

                ChemFile cifFile = reader.read(new ChemFile());

                assert cifFile != null;
                for ( IChemSequence sequence : cifFile.chemSequences() ) {
                    assert sequence.chemModels() != null;
                    for (IChemModel model : sequence.chemModels()) {
                        ICrystal crystal = model.getCrystal();
                        assert crystal != null : "Crystal is null";
                        computeOrthogonalCoordinates( crystal );
                        try {
                            //------------------------------------------------------------
                            // Load atom radii from some (external) source).

                            // TODO: factor out the code below into a
                            // separate static helper function.

                            // FIXME: make the AtomTypeFactory factory
                            // a global readonly variable (aka "static
                            // final instance variable"), and
                            // initialise it only once:
                            AtomTypeFactory factory = AtomTypeFactory.getInstance(
                                    "org/openscience/cdk/config/data/jmol_atomtypes.txt",
                                    SilentChemObjectBuilder.getInstance());
                            IAtomType[] atomTypes = factory.getAllAtomTypes();
                            Hashtable<String,IAtomType> atomSymbolTable = new Hashtable<String,IAtomType>();
                            for( IAtomType atomType : atomTypes ) {
                                String atomSymbol = atomType.getSymbol();
                                atomSymbolTable.put( atomSymbol, atomType );
                            }
                            for( IAtom atom : crystal.atoms()) {
                                String atomSymbol = atom.getSymbol();
                                if( atomSymbolTable.containsKey(atomSymbol)) {
                                    IAtomType atomType = atomSymbolTable.get(atomSymbol);
                                    atom.setCovalentRadius(atomType.getCovalentRadius());
                                }
                            }
                            RebondTool rtool =
                                    new RebondTool(5.0,0.5,0.1);
                            assert rtool != null : "RebondTool instance is null";
                            rtool.rebond(crystal);
                            //------------------------------------------------------------
                            percieveAtomTypesAndConfigureAtoms( crystal );
                            IAtomContainer heavyAtoms = removeHydrogens( crystal );
                            // Taken from the example in
                            // https://cdk.github.io/cdk/1.5/docs/api/index.html?overview-summary.html
                            //ElectronDonation edmodel = ElectronDonation.cdk();
                            ElectronDonation edmodel = ElectronDonation.daylight();
                            CycleFinder cycles       = Cycles.cdkAromaticSet();
                            Aromaticity aromaticity  = new Aromaticity(edmodel, cycles);
                            aromaticity.apply(heavyAtoms);

                            // Alternative taken from:
                            // https://cdk.github.io/cdk/1.5/docs/api/org/openscience/cdk/aromaticity/CDKHueckelAromaticityDetector.html
                            // Aromaticity.cdkLegacy().apply(heavyAtoms);

                            // https://cdk.github.io/cdk/1.5/docs/api/org/openscience/cdk/aromaticity/Kekulization.html
                            // Kekulization k = new Kekulization();
                            // k.kekulize(heavyAtoms);

                            SmilesGenerator sg =
                                    new SmilesGenerator(SmiFlavor.Stereo |
                                            SmiFlavor.UseAromaticSymbols);
                            String smiles = sg.create(heavyAtoms);
                            System.out.println(smiles);
                        } catch (Exception e) {
                            System.err.println(progName + ": " + filename +
                                    ": ERROR, " + e);
                        } // try-catch
                    } // for
                } // for
            } // try
            catch(Exception e) {
                System.err.println( progName + ": " + "WARNING, " + e );
            }
        } // for
    }

}
