/*---------------------------------------------------------------------------*\
**$Author: saulius $
**$Date: 2018-03-02 11:46:28 +0200 (Fri, 02 Mar 2018) $
**$Revision: 138 $
**$URL: svn+ssh://www.crystallography.net/home/coder/svn-repositories/smiles-scripts/tags/v0.2.0/src/cdkrecharge.java $
\*---------------------------------------------------------------------------*/

// This CLI Java program reads SMILES strings, optionally supplemented
// with IDs, and transfers charge designators from various anions
// (presumably halogens) to a central arom of the kind listed in the
// 'cetralAtomSymbols' srting.

import org.openscience.cdk.smiles.*;
import org.openscience.cdk.silent.*;
import org.openscience.cdk.exception.*;
import org.openscience.cdk.interfaces.*;
import java.util.Hashtable;
import java.io.*;

class cdkrecharge {

    // This program will only process (redistribute charges) for
    // molecules that have the following number of elements:
    static int ELEMENT_NUMBER_TO_PROCESS = 2;

    static OptionValue smilesOption = new OptionValue();
    static OptionValue kekuliseOption = new OptionValue(true);
    static Option options[] = {
            new Option ( "-k", "--kekulise",        OptionType.OT_BOOLEAN_TRUE, kekuliseOption ),
            new Option ( "-k-","--no-kekulise",     OptionType.OT_BOOLEAN_FALSE,kekuliseOption ),
            new Option ( null, "--dont-kekulise",   OptionType.OT_BOOLEAN_FALSE,kekuliseOption ),
            new Option ( null, "--do-not-kekulise", OptionType.OT_BOOLEAN_FALSE,kekuliseOption ),

            new Option          ( "-s", "--smiles",  OptionType.OT_STRING, smilesOption ),
            new VersionOption   ( null, "--version", OptionType.OT_FUNCTION ),
            new CDKRechargeHelp ( null, "--help",    OptionType.OT_FUNCTION ),
        };
    
    private static final String progName = CDKRechargeHelp.progName;

    static final String[] centralAtomSymbols =
    {"B", "P", "S", "As", "Al", "Bi", "Sn", "Si", "Sb"};
    
    static final String[] periferalAtomSymbols =
        {"F", "Cl", "Br", "I", "At"};

    static Hashtable<String,String> centralAtomTable, periferalAtomTable;

    static boolean hashTablesInitialised = initHashTables();

    static boolean initHashTables() {
        centralAtomTable = new Hashtable<String,String>();
        for( String s : centralAtomSymbols ) {
            centralAtomTable.put( s, "" );
        }
        periferalAtomTable = new Hashtable<String,String>();
        for( String s : periferalAtomSymbols ) {
            periferalAtomTable.put( s, "" );
        }
        return true;
    }
    
    public static void main( String[] argv ) throws CDKException, IOException
    {
        String filenames[] = null;
        try {
            filenames = SOptions.get_options( argv, options );
        }
        catch (SOptionsException e) {
            System.err.println( progName + ": " + e.getMessage() );
            System.exit(1);
        }

        if( smilesOption.present ) {
            try {
                System.out.println(redistributeSmilesCharges(smilesOption.s,
                                                             kekuliseOption.bool));
                System.exit(0);
            }
            catch(Exception e) {
                System.err.println( progName + ": " + e);
                System.exit(1);
            }
        }
        
        if( filenames == null || filenames.length == 0 ) {
            filenames = new String[] { "-" };
        }
        for( String filename : filenames ) {
            try {
                InputStreamReader reader;
                if( filename.equals("-") ) {
                    reader = new InputStreamReader( System.in );
                } else {
                    reader = new FileReader(filename);
                }
                BufferedReader br = new BufferedReader( reader );

                int count = 1;
                String line;
                while( (line = br.readLine()) != null ) {
                    String parts[] = line.trim().split("[ \t]+");
                    if( parts != null &&
                        (parts.length == 1 || parts.length == 2)) {
                        String smiles = parts[0];
                        String ident = parts.length > 1 ? parts[1] : null;
                        try {
                            System.out.println( redistributeSmilesCharges(smiles,
                                                                          kekuliseOption.bool) +
                                                (ident != null? "\t" + ident : "") );
                        }
                        catch(Exception e) {
                            System.err.println( progName + ": " + filename +
                                                "(" + count + ")" +
                                                (ident != null? " " + ident : "") +
                                                ": ERROR, " + e);
                        }
                    } else {
                        int maxlen = 20;
                        System.err.println
                            ( progName + ": " + filename + "(" + count + "): " +
                              "WARNING, could not get SMILES and " +
                              "identifier from string '" +
                              line.substring(0,line.length() < maxlen ?
                                             line.length() : maxlen ) +
                              (line.length() < maxlen ? "" : "...") +
                              "'" );
                    }
                    count++;
                } // while
            } // try
            catch(Exception e) {
                System.err.println( progName + ": " + "WARNING, " + e );
            }
        } // for
    }

    private static int countNumberOfElements( IAtomContainer atomContainer ) {
        Hashtable<String,Integer> elementSymbols =
            new Hashtable<String,Integer>();
        for( int i = 0; i < atomContainer.getAtomCount(); i++ ) {
            IAtom atom = atomContainer.getAtom(i);
            String atomSymbol = atom.getSymbol();
            int currentCount = 0;
            if(elementSymbols.containsKey(atomSymbol)) {
                currentCount = elementSymbols.get(atomSymbol);
            }
            elementSymbols.put(atomSymbol,currentCount+1);
        }
        return elementSymbols.size();
    }
    
    private static void distributeCharged( IAtomContainer atomContainer, int totalCharge ) {
        for( int i = 0; i < atomContainer.getAtomCount(); i++ ) {
            IAtom atom = atomContainer.getAtom(i);
            String atomSymbol = atom.getSymbol();
            if( centralAtomTable.containsKey(atomSymbol)) {
                atom.setFormalCharge(totalCharge);
                break;
            }
        }
    }
    
    private static String redistributeSmilesCharges( String smiles, boolean kekulise )
        throws CDKException, InvalidSmilesException
    {
        String newSmiles = "";
        String[] smilesParts = smiles.split("\\.");

        for( String smilesPart : smilesParts ) {
            SmilesParser sp =
                new SmilesParser(SilentChemObjectBuilder.getInstance());

            sp.kekulise(kekulise);

            IAtomContainer ac = sp.parseSmiles( smilesPart );

            String converted = smilesPart;
            int totalCharge = getTotalCharge(ac);

            if( countNumberOfElements(ac) == ELEMENT_NUMBER_TO_PROCESS &&
                hasPeriferalAtom(ac) &&
                ((countCentralAtoms(ac) >  1 && totalCharge == -1) ||
                 (countCentralAtoms(ac) == 1 && totalCharge <= -1)) ) {

                resetCharges(ac);
                distributeCharged(ac,totalCharge);

                if( totalCharge == getTotalCharge(ac) ) {
                    SmilesGenerator sg =
                    new SmilesGenerator(SmiFlavor.Stereo|
                                        SmiFlavor.UseAromaticSymbols);
                    converted = sg.create(ac);
                }
            }

            if( !newSmiles.equals("") ) {
                newSmiles += ".";
            }
            newSmiles += converted;

        }
        return newSmiles;
    }
    
    private static int getTotalCharge( IAtomContainer ac )
    {
        int charge = 0;
        for( int i = 0; i < ac.getAtomCount(); i++ ) {
            charge += ac.getAtom(i).getFormalCharge();
        }
        return charge;
    }

    private static void resetCharges( IAtomContainer ac )
    {
        for( int i = 0; i < ac.getAtomCount(); i++ ) {
            ac.getAtom(i).setFormalCharge(0);
        }
    }

    private static boolean hasPeriferalAtom( IAtomContainer ac ) {
        for( int i = 0; i < ac.getAtomCount(); i++ ) {
            if( periferalAtomTable.containsKey(ac.getAtom(i).getSymbol()) ) {
                return true;
            }
        }        
        return false;
    }

    private static int countCentralAtoms( IAtomContainer ac ) {
        Hashtable<String,String> centralAtomTable =
            new Hashtable<String,String>();
        for( String s : centralAtomSymbols ) {
            centralAtomTable.put( s, "" );
        }
        int centralAtomCount = 0;
        for( int i = 0; i < ac.getAtomCount(); i++ ) {
            if( centralAtomTable.containsKey(ac.getAtom(i).getSymbol()) ) {
                centralAtomCount ++;
            }
        }        
        return centralAtomCount;
    }
}
