/*
 * =====================================
 *  Copyright (c) 2019 NextMove Software
 * =====================================
 */

package org.openscience.cdk.app;

import org.openscience.cdk.config.Elements;
import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.interfaces.IBond;

public class MolOp {

  private static int calcValence(IAtom atom) {
    Integer impHs = atom.getImplicitHydrogenCount();
    int v = impHs == null ? 0 : impHs;
    for (IBond bond : atom.bonds()) {
      IBond.Order order = bond.getOrder();
      if (order != null && order != IBond.Order.UNSET)
        v += order.numeric();
    }
    return v;
  }


  private static boolean isDativeDonor(IAtom a) {
    switch (a.getAtomicNumber()) {
      case 7:
      case 15:
        return a.getFormalCharge() == 0 && calcValence(a) == 4;
      case 8:
        return a.getFormalCharge() == 0 && calcValence(a) == 3;
      default:
        return false;
    }
  }


  private static boolean isDativeAcceptor(IAtom a) {
    if (Elements.isMetal(a))
      return true;
    switch (a.getAtomicNumber()) {
      case 5:
        return a.getFormalCharge() == 0 && calcValence(a) == 4;
      case 8:
        return a.getFormalCharge() == 0 && calcValence(a) == 1;
      default:
        return false;
    }
  }


  private static boolean isChargedDativeDonor(IAtom a) {
    switch (a.getAtomicNumber()) {
      case 7:
      case 15:
        return a.getFormalCharge() == +1 && calcValence(a) == 4;
      case 8:
        return a.getFormalCharge() == +1 && calcValence(a) == 3;
      default:
        return false;
    }
  }


  private static boolean isChargedDativeAcceptor(IAtom a) {
    if (a.getFormalCharge() != -1)
      return false;
    if (Elements.isMetal(a))
      return true;
    switch (a.getAtomicNumber()) {
      case 5:
        return calcValence(a) == 4;
      case 8:
        return calcValence(a) == 1;
      default:
        return false;
    }
  }


  public static void perceiveRadicals(IAtomContainer mol) {
    for (IAtom atom : mol.atoms()) {
      int     v;
      Integer q = atom.getFormalCharge();
      if (q == null) q = 0;
      switch (atom.getAtomicNumber()) {
        case 6:
          if (q == 0) {
            v = calcValence(atom);
            if (v == 2)
              mol.addSingleElectron(mol.indexOf(atom));
            if (v < 4)
              mol.addSingleElectron(mol.indexOf(atom));
          }
          break;
        case 7:
          if (q == 0) {
            v = calcValence(atom);
            if (v < 3)
              mol.addSingleElectron(mol.indexOf(atom));
          }
          break;
        case 8:
          if (q == 0) {
            v = calcValence(atom);
            if (v < 2)
              mol.addSingleElectron(mol.indexOf(atom));
            if (v < 1)
              mol.addSingleElectron(mol.indexOf(atom));
          }
          break;
      }
    }
  }


  public static void perceiveDativeBonds(IAtomContainer mol) {
    for (IBond bond : mol.bonds()) {
      IAtom beg = bond.getBegin();
      IAtom end = bond.getEnd();
      if (isDativeDonor(end) && isDativeAcceptor(beg)) {
        bond.setDisplay(IBond.Display.ArrowBeg);
        //bond.setOrder(IBond.Order.UNSET);
      } else if (isDativeDonor(beg) && isDativeAcceptor(end)) {
        bond.setDisplay(IBond.Display.ArrowEnd);
        //bond.setOrder(IBond.Order.UNSET);
      } else if (isChargedDativeDonor(end) && isChargedDativeAcceptor(beg)) {
        bond.setDisplay(IBond.Display.ArrowBeg);
        //bond.setOrder(IBond.Order.UNSET);
      } else if (isChargedDativeDonor(beg) && isChargedDativeAcceptor(end)) {
        bond.setDisplay(IBond.Display.ArrowEnd);
        //bond.setOrder(IBond.Order.UNSET);
      }
    }
    for (IBond bond : mol.bonds()) {
      IAtom beg = bond.getBegin();
      IAtom end = bond.getEnd();
      if (bond.getDisplay() == IBond.Display.ArrowBeg ||
          bond.getDisplay() == IBond.Display.ArrowEnd) {
        beg.setFormalCharge(0);
        end.setFormalCharge(0);
      }
    }
  }
}

@@ -424,21 +424,13 @@
     
-Integer impHs
+int v
  = a
@@ -465,47 +465,8 @@
 ();%0A
-    int v = impHs == null ? 0 : impHs;%0A
     
@@ -1322,31 +1322,27 @@
 c boolean is
-Charged
+Pos
 DativeDonor(
@@ -1636,23 +1636,19 @@
 olean is
-Charged
+Neg
 DativeAc
@@ -3182,52 +3182,8 @@
 g);%0A
-        //bond.setOrder(IBond.Order.UNSET);%0A
     
@@ -3295,52 +3295,8 @@
 d);%0A
-        //bond.setOrder(IBond.Order.UNSET);%0A
     
@@ -3302,39 +3302,35 @@
    %7D else if (is
-Charged
+Pos
 DativeDonor(end)
@@ -3331,31 +3331,27 @@
 r(end) && is
-Charged
+Neg
 DativeAccept
@@ -3422,41 +3422,105 @@
     
-//bond.setOrder(IBond.Order.UNSET
+beg.setFormalCharge(beg.getFormalCharge()+1);%0A        end.setFormalCharge(end.getFormalCharge()-1
 );%0A 
@@ -3537,23 +3537,19 @@
 e if (is
-Charged
+Pos
 DativeDo
@@ -3566,15 +3566,11 @@
 & is
-Charged
+Neg
 Dati
@@ -3649,276 +3649,53 @@
     
-//bond.setOrder(IBond.Order.UNSET);%0A      %7D%0A    %7D%0A    for (IBond bond : mol.bonds()) %7B%0A      IAtom beg = bond.getBegin();%0A      IAtom end = bond.getEnd();%0A      if (bond.getDisplay() == IBond.Display.ArrowBeg %7C%7C%0A          bond.getDisplay() == IBond.Display.ArrowEnd) %7B
+beg.setFormalCharge(beg.getFormalCharge()+1);
 %0A   
@@ -3691,35 +3691,35 @@
 e()+1);%0A        
-beg
+end
 .setFormalCharge
@@ -3723,25 +3723,13 @@
 rge(
-0);%0A        
 end.
-s
+g
 etFo
@@ -3739,17 +3739,19 @@
 lCharge(
-0
+)-1
 );%0A     

Fix #19