/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                            B O N D O R D E R . C                             *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: bondorder.c,v 1.5 2003/11/07 10:58:19 jrh Exp $
* $Log: bondorder.c,v $
* Revision 1.5  2003/11/07 10:58:19  jrh
* Release 2.4
*
* Revision 1.4  2000/12/10 15:02:22  jrh
* Release 2.3
*
* Revision 1.3  1999/05/24 01:24:49  jrh
* Release 2.2.1
*
* Revision 1.2  1999/02/07 21:44:46  jrh
* Release 2.2
*
* Revision 1.1  1998/01/26 00:34:45  jrh
* Initial revision
*
*/
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include "viewmol.h"

extern struct MOLECULE *molecules;

extern void *getmem(size_t, size_t);

void calculateShift(struct MOLECULE *, struct BOND *, int *, int, int, int);
void findConjugation(struct MOLECULE *, struct BOND *, int *, int, int);
void vectorProduct(double *, double *, double *);
double scalarProduct(double *, double *);

void bondOrder(struct MOLECULE *mol, struct BOND *bond, int *connectivity,
               int maxBondsPerAtom)
{
  int i, j, ni, nj, bo=1;

/* Assign bond orders by counting electrons. If the number of electrons
   for an atom is set to -1 assign only single bonds. */

  i=bond->first;
  if (mol->atoms[i].nelectrons == (-1))
  {
    bond->order=1;
    return;
  }
  j=bond->second;
  if (mol->atoms[j].nelectrons == (-1))
  {
    bond->order=1;
    return;
  }

/* Compare number of electrons with number of atoms atom is bound to
   and deduce bond order */

  do
  {
    if (mol->atoms[i].nelectrons > 4)
      ni=8-mol->atoms[i].nelectrons;
    else
      ni=mol->atoms[i].nelectrons;
    if (mol->atoms[j].nelectrons > 4)
      nj=8-mol->atoms[j].nelectrons;
    else
      nj=mol->atoms[j].nelectrons;
    ni-=mol->atoms[i].nbonds-mol->atoms[i].hbonds;
    nj-=mol->atoms[j].nbonds-mol->atoms[j].hbonds;
    if (ni < 0)
      ni=mol->atoms[i].nelectrons-mol->atoms[i].nbonds+mol->atoms[i].hbonds;
    if (nj < 0)
      nj=mol->atoms[j].nelectrons-mol->atoms[j].nbonds+mol->atoms[j].hbonds;

    if (ni > 0 && nj > 0)
    {
      mol->atoms[i].nbonds++;
      mol->atoms[j].nbonds++;
      bo++;
    }
    else
      break;
  } while (bo <= 4);
  bond->order=bo;

/* Bond order has been found, calculate shift vector to draw multiple bonds. */

  if (bo > 1) calculateShift(mol, bond, connectivity, maxBondsPerAtom, i, j);
}

void calculateShift(struct MOLECULE *mol, struct BOND *bond, int *connectivity,
                    int maxBondsPerAtom, int i, int j)
{
  double v1[3], v2[3], v3[3];
  int ni, nj, bo, kh=(-1);
  register double r1, r2;
  register int k, l;

  /* Rules for multiple bonds: Shall be in the same plane as other bond framework,
                               but do not use hydrogens to determine this plane
                               unless impossible otherwise. */
  bo=abs(bond->order);
  ni=(mol->atoms[i].nbonds >= mol->atoms[j].nbonds ? i : j);
  nj=(ni == i ? j : i);
  l=0;
  do
  {
    k=abs(connectivity[maxBondsPerAtom*ni+l]);
    if (k != nj && strcmp(mol->atoms[k].element->symbol, "H")) break;
    if (k != nj && !strcmp(mol->atoms[k].element->symbol, "H")) kh=k;
    l++;
  } while (l < maxBondsPerAtom);
  /* No non-hydrogen atom found so we have to use a hydrogen atom */
  if (l == maxBondsPerAtom && kh != (-1)) k=kh;
  v1[0]=mol->atoms[k].x-mol->atoms[ni].x;
  v1[1]=mol->atoms[k].y-mol->atoms[ni].y;
  v1[2]=mol->atoms[k].z-mol->atoms[ni].z;
  r1=scalarProduct(v1, v1);
  v2[0]=mol->atoms[nj].x-mol->atoms[ni].x;
  v2[1]=mol->atoms[nj].y-mol->atoms[ni].y;
  v2[2]=mol->atoms[nj].z-mol->atoms[ni].z;
  r2=scalarProduct(v2, v2);
  if (r1 != 0.0 && r2 != 0.0)
    r1=fabs(scalarProduct(v1, v2)/(sqrt(r1*r2))+1.0);
  else
    r1=0.0;
  if (r1 > 1.0e-6)
  {
    vectorProduct(v1, v2, v3);
    vectorProduct(v2, v3, v1);
    r1=mol->bondShift/sqrt(scalarProduct(v1, v1));
    if (bo == 2) r1*=0.5;
    bond->x=v1[0]*r1;
    bond->y=v1[1]*r1;
    bond->z=v1[2]*r1;
  }
  else
  {
    v1[0]=1.0;
    v1[1]=0.0;
    v1[2]=0.0;
    r1=scalarProduct(v1, v2)/sqrt(r2);
    if (bo == 2) r2=0.5;
    else         r2=1.0;
    bond->x=r2*mol->bondShift*sqrt(1.0-r1*r1);
    bond->y=r2*mol->bondShift*r1;
    bond->z=0.0;
  }
/*v3[0]=0.5*(v1[0]-v2[0]);
  v3[1]=0.5*(v1[1]-v2[1]);
  v3[2]=0.5*(v1[2]-v2[2]);
  r1=mol->bondShift/sqrt(scalarProduct(v3, v3));
  if (bo == 2) r1*=0.5;
  bond->x=v3[0]*r1;
  bond->y=v3[1]*r1;
  bond->z=v3[2]*r1; */
}

void findConjugation(struct MOLECULE *mol, struct BOND *bonds, int *connectivity,
                     int maxBondsPerAtom, int nb)
{
  int atom1, atom2, conjugated;
  register int i, j, k, l;

  for (i=0; i<mol->nb; i++)
  {
    if (abs(mol->bonds[i].order) == 2)
    {
      atom1=mol->bonds[i].first;
	while (TRUE)
	{
        for (j=0; j<mol->nb; j++)
        {
          if ((mol->bonds[j].first == atom1
              || mol->bonds[j].second == atom1)
              && mol->bonds[j].order == 1)
          {
            atom2=mol->bonds[j].first == atom1 
                 ? mol->bonds[j].second : mol->bonds[j].first;
            for (k=0; k<mol->nb; k++)
            {
              if ((mol->bonds[k].first == atom2
                  || mol->bonds[k].second == atom2)
                  && abs(mol->bonds[k].order) == 2)
              {
                conjugated=TRUE;
                for (l=0; l<mol->nb; l++)
                {
                  if (l == i || l == j || l == k) continue;
                  if (mol->bonds[l].first != atom1
                      && mol->bonds[l].second != atom1) continue;
                  if (mol->bonds[l].order != 1
                      && mol->bonds[l].order != (-2))
                  {
                    conjugated=FALSE;
                    break;
                  }
                }
                if (conjugated)
                {
                  mol->bonds[i].order=(-2);
                  mol->bonds[j].order=(-2);
                  mol->bonds[k].order=(-2);
                  /* calculate shift vector for bond j */
                  calculateShift(mol, &(mol->bonds[j]), connectivity,
                                 maxBondsPerAtom, atom1, atom2);
                  break;
                }
              }
            }
          }
        }
	  if (atom1 == mol->bonds[i].second) break;
	  atom1=mol->bonds[i].second;
      }
    }
  }
}

void vectorProduct(double *v1, double *v2, double *v3)
{
  v3[0]=v1[1]*v2[2]-v1[2]*v2[1];
  v3[1]=v1[2]*v2[0]-v1[0]*v2[2];
  v3[2]=v1[0]*v2[1]-v1[1]*v2[0];
}

double scalarProduct(double *v1, double *v2)
{
  return(v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]);
}
