/**********************************************************************
bond.h - Handle OBBond class.

Copyright (C) 1998-2001 by OpenEye Scientific Software, Inc.
Some portions Copyright (C) 2001-2006 by Geoffrey R. Hutchison
Some portions Copyright (C) 2003 by Michael Banck
Some portions Copyright (C) 2008 by Tim Vandermeersch

This file is part of the Open Babel project.
For more information, see <http://openbabel.org/>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
***********************************************************************/

#ifndef OB_BOND_H
#define OB_BOND_H

#include <openbabel/babelconfig.h>

#ifndef EXTERN
#  define EXTERN extern
#endif

#include <openbabel/base.h>
#include <openbabel/atom.h>

namespace OpenBabel
{
  class OBAtom;
  class OBRing;

  //! OBEdgeBase is declared for backwards-compatibility with 2.0 and earlier code
  typedef OBBond OBEdgeBase;

  //BOND Property Macros (flags)
  //! An aromatic bond (regardless of bond order)
#define OB_AROMATIC_BOND  (1<<1)
  //! A solid black wedge in 2D representations -- i.e., "up" from the 2D plane
#define OB_WEDGE_BOND     (1<<2)
  //! A dashed "hash" bond in 2D representations -- i.e., "down" from the 2D plane
#define OB_HASH_BOND      (1<<3)
  //! A bond in a ring
#define OB_RING_BOND      (1<<4)
  //! The "upper" bond in a double bond cis/trans isomer (i.e., "\" in SMILES) <-- this was wrong (/), obsolete anyway
#define OB_TORUP_BOND     (1<<5)
  //! The "down" bond in a double bond cis/trans isomer (i.e., "/" in SMILES) <-- same
#define OB_TORDOWN_BOND   (1<<6)
  //! A Kekule single bond
#define OB_KSINGLE_BOND   (1<<7)
  //! A Kekule double bond
#define OB_KDOUBLE_BOND   (1<<8)
  //! A Kekule triple bond
#define OB_KTRIPLE_BOND   (1<<9)
  //! A bond which "closes" a ring when walking the molecular graph
#define OB_CLOSURE_BOND   (1<<10)
  // 11-16 currently unused

#define OB_WEDGE_OR_HASH_BOND     (1<<11)
#define OB_CIS_OR_TRANS_BOND     (1<<12)

  //class OBBondPrivate;
  class OBAPI OBBond: public OBBase
  {
    protected:
      unsigned int                _idx;   //!< Unique edge index used by GetIdx() and SetIdx()
      OBMol                      *_parent;//!< The molecule which contains me (if any)
      OBAtom                     *_bgn;   //!< I connect one node
      OBAtom                     *_end;   //!< to another node
      char                        _order; //!< Bond order (1, 2, 3, 5=aromatic)
      unsigned short int          _flags; //!< Any flags for this bond
      unsigned long                 _id;        //!< unique id
      //OBBondPrivate * const d;

      /**
      * @return True id the @p flag is set.
       */
      bool HasFlag(int flag) const { return ((_flags & flag) != 0); }
      /**
       * Sets the bitwise @p flag
       */
      void SetFlag(int flag) { _flags |= flag; }
      /**
       * Unsets the bitwise @p flag
       */
      void UnsetFlag(int flag) { _flags &= (~(flag)); }

    public:
      enum Flag {
        Aromatic = (1<<1), //!< An aromatic bond (regardless of bond order)
        Ring     = (1<<4), //!< A bond in a ring
        Closure  = (1<<10) //!< A bond which "closes" a ring when walking the molecular graph
      };
      enum StereoFlag {
        Wedge       = (1<<2),  //!< A solid black wedge in 2D representations -- i.e., "up" from the 2D plane
        Hash        = (1<<3),  //!< A dashed "hash" bond in 2D representations -- i.e., "down" from the 2D plane
        WedgeOrHash = (1<<11), //!< The bond is either wedge or hash, this is a seperate flag!
        CisOrTrans  = (1<<12)  //!< Indicates the 2D/3D coordinates are accidently cis/trans.
      };
      //! Whether this bond has been visited by a graph algorithm
      /** \deprecated Use OBBitVec objects instead to be fully thread-safe. **/
      bool Visit;

      //! Constructor
      OBBond();
      //! Destructor
      virtual ~OBBond();

      //! \name Bond modification methods
      //@{
      //! Set the internal bond index
      /** \warning This will not update the index in the parent OBMol.
          Intended mainly for internal use. Use with care. **/
      void SetIdx(int idx)        {          _idx = idx;        }
      void SetId(unsigned long id) { _id = id; }
      //! Set the bond order to @p order (i.e., 1 = single, 2 = double, 5 = aromatic)
      /** \deprecated Use SetBondOrder() instead. **/
      void SetBO(int order);
      //! Set the bond order to @p order (i.e., 1 = single, 2 = double, 5 = aromatic)
      void SetBondOrder(int order);
      //! Set the beginning atom of this bond to @p begin. Does not update @p begin.
      void SetBegin(OBAtom *begin){          _bgn = begin;      }
      //! Set the ending atom of this bond to @p end. Does not update @p end.
      void SetEnd(OBAtom *end)    {          _end = end;        }
      //! Set the parent molecule to @p ptr. Does not update parent.
      void SetParent(OBMol *ptr)  {        _parent= ptr;        }
      //! Change the bond length to @p length, while keeping @p fixed stationary
      void SetLength(OBAtom *fixed,double length);
      //! Change the bond length to @p length, moving both atoms halfway
      //! \since version 2.2
      void SetLength(double length);
      //! Set the main bond information (i.e., when creating a bond)
      void Set(int index, OBAtom* begin,OBAtom* end,int order,int flags);
      //! \deprecated Use SetBondOrder() instead
      void SetKSingle();
      //! \deprecated Use SetBondOrder() instead
      void SetKDouble();
      //! \deprecated Use SetBondOrder() instead
      void SetKTriple();
      //! Mark that this bond is aromatic. Does not update atoms or validate.
      void SetAromatic()    { SetFlag(OB_AROMATIC_BOND); }
      /**
       * Mark that this bond has 2D "wedge" notation (i.e., goes in a positive
       * Z direction from the beginning to end atoms)
       */
      void SetWedge() { SetFlag(Wedge); }
      /**
       * Mark that this bond has 2D "hash" notation (i.e., goes in a negative
       * Z direction from the beginning to end atoms)
       */
      void SetHash() { SetFlag(Hash); }
      /**
       * Mark that this bond has 2D "wedge" notation (i.e., goes in a positive
       * Z direction from the beginning to end atoms)
       */
      void SetWedgeOrHash() { SetFlag(WedgeOrHash); }
      //! Mark that this bond has an "up" torsion for double-bond stereochem (i.e., "/" in SMILES notation
      void SetUp()          { SetFlag(OB_TORUP_BOND); UnsetFlag(OB_TORDOWN_BOND); }
      //! Mark that this bond has an "down" torsion for double-bond stereochem (i.e., "\" in SMILES notation
      void SetDown()        { SetFlag(OB_TORDOWN_BOND); UnsetFlag(OB_TORUP_BOND);   }
      //! Mark that this bond is in a ring. Primarily for internal use.
      void SetInRing(bool set=true) { if(set)SetFlag(OB_RING_BOND); else UnsetFlag(OB_RING_BOND);}
      //! Mark that this bond indicates a ring closure when walking the molecule
      /** \warning This is for internal use only. All closure bonds are marked
          automatically by lazy evaluation when requesting
          OBBond::IsClosure() **/
      void SetClosure()     { SetFlag(OB_CLOSURE_BOND);  }
      //! Clear any indication of 2D "hash" notation from SetHash()
      void UnsetHash()      { UnsetFlag(OB_HASH_BOND);    }
      //! Clear any indication of 2D "wedge" notation from SetWedge()
      void UnsetWedge()     { UnsetFlag(OB_WEDGE_BOND);   }
      //! Clear any indication of "/" double bond stereochemistry from SetUp()
      void UnsetUp()        { UnsetFlag(OB_TORUP_BOND);   }
      //! Clear any indication of "\" double bond stereochemistry from SetDown()
      void UnsetDown()      { UnsetFlag(OB_TORDOWN_BOND); }
      //! Clear all aromaticity information for the bond
      void UnsetAromatic()  { UnsetFlag(OB_AROMATIC_BOND);}
      //! Clear all Kekule information for the bond
      void UnsetKekule()
        {
          _flags &= (~(OB_KSINGLE_BOND|OB_KDOUBLE_BOND|OB_KTRIPLE_BOND));
        }
      //@}

      //! \name Bond data request methods
      //@{
      //! \return The unique bond index in a molecule.
      unsigned int     GetIdx()           const { return(_idx);  }
      unsigned long GetId()           const { return _id; }
      //! \return The bond order for the bond
      /** \deprecated Use GetBondOrder() as this method may be removed. **/
      unsigned int     GetBO()            const { return(_order); }
      //! \return The bond order for the bond
      unsigned int     GetBondOrder()     const { return(_order); }
      //! \return The set of property flags defined for this bond.
      unsigned int     GetFlags()         const { return(_flags);      }
      //! \return The atom index for the end atom in this bond (from OBAtom::GetIdx()
      unsigned int     GetBeginAtomIdx()  const
        { return (_bgn ? _bgn->GetIdx() : 0); }
      //! \return The atom index for the end atom in this bond (from OBAtom::GetIdx()
      unsigned int     GetEndAtomIdx()    const
        { return (_end ? _end->GetIdx() : 0); }
      //! \return The "beginning" atom for this bond
      OBAtom *GetBeginAtom()    { return(_bgn);    }
      const OBAtom *GetBeginAtom() const
        { return(_bgn);    }
      //! \return The "end" atom for this bond
      OBAtom *GetEndAtom()      { return(_end);    }
      const OBAtom *GetEndAtom() const
        { return(_end);    }
      //! \return The neighboring atom to @p ptr (i.e., the end if @p ptr is the start)
      /** \warning If @p ptr is not part of the bond, the beginning atom
          will always be returned **/
      OBAtom *GetNbrAtom(OBAtom *ptr)
        {
          return((ptr != _bgn)? _bgn : _end);
        }
      //! \return The enclosing OBMol for this bond, or NULL if none is defined.
      OBMol  *GetParent()                 {return(_parent);}
      //! \return The expected "equilibrium" length based on the covalent radii and bond order
      /** Length is given in Angstroms **/
      double  GetEquibLength() const;
      //! \return The current length of this bond in Angstroms
      double  GetLength() const;
      //! \return The index to the neighboring atom of @p ptr (i.e., the end if @p ptr is the start)
      /** \warning If @p ptr is not part of the bond, the beginning atom
          index will always be returned **/
      unsigned int     GetNbrAtomIdx(OBAtom *ptr)
        {
          if (ptr!=_bgn)
            return (_bgn ? _bgn->GetIdx() : 0);
          else
            return (_end ? _end->GetIdx() : 0);
        }
      //! Find the smallest ring containing this bond (returns a NULL pointer if none exists)
      OBRing* FindSmallestRing() const;
      //@}

      //! \name property request methods
      //@{
      //! \return Is the bond aromatic?
      //!  (Note that the two atoms of the bond may be aromatic,
      //!   but not the bond)
      bool IsAromatic() const;
      //! \return Is the bond part of a ring?
      bool IsInRing() const;
      //! \return Is the bond a rotatable bond?
      /**  Currently, this function classifies any bond with at least one heavy
           atom, no sp-hybrid atoms (e.g., a triple bond somewhere) not in a ring
           as a potential rotor. No other bond typing is attempted.
           For more detailed rotor detection, check the OBRotorList and
           OBRotorRules classes **/
      bool IsRotor();
      /** \return Is the bond an amide link (i.e., between a carbonyl C and a N)?
           No distinction is made between primary, secondary, and tertiary amides. **/
      bool IsAmide();
      /** \return Is the bond a primary amide (i.e., between carbonyl C and a NH2)?
           In versions prior to 2.3, this function incorrectly identified secondary amides. **/
      bool IsPrimaryAmide();
      /** \return Is the bond a secondary amide (i.e., between a carbonyl C and a NH1)?
           In versions prior to 2.3, this function incorrectly identified tertiary amides. **/
      bool IsSecondaryAmide();
      /** \return Is the bond a teriary amide (i.e., between a carbonyl C and a NH0)?
           This function is new since release 2.3.**/
      bool IsTertiaryAmide();
      //! \return Is the bond an ester link (i.e., between a carbonyl C and an O)?
      bool IsEster();
      //! \return Is the bond a carbonyl C=O?
      bool IsCarbonyl();
      //! \return Is the bond a single bond?
      bool IsSingle();
      //! \return Is the bond is a double bond?
      bool IsDouble();
      //! \return Is the bond is a triple bond?
      bool IsTriple();
      //! \deprecated Use IsSingle() instead
      bool IsKSingle();
      //! \deprecated Use IsDouble() instead
      bool IsKDouble();
      //! \deprecated Use IsTriple() instead
      bool IsKTriple();
      //! \return Does this bond "close" a ring when walking the molecular graph?
      bool IsClosure();
      /** \return Whether this is the "upper" bond in a double bond cis/trans
          isomer (i.e., "/" in SMILES) **/
      bool IsUp()    {    return(HasFlag(OB_TORUP_BOND));    }
      /** \return Whether this is the "lower" bond in a double bond cis/trans
          isomer (i.e., "\" in SMILES) **/
      bool IsDown()  {    return(HasFlag(OB_TORDOWN_BOND));  }
      /** \return Whether this bond is a "wedge" in 2D representations
          (i.e., goes in a positive Z direction from the beginning to end atoms) **/
      bool IsWedge() {    return(HasFlag(OB_WEDGE_BOND));    }
      /** \return Whether this bond is a "hash" in 2D representations
          (i.e., goes in a negative Z direction from the beginning to end atoms) **/
      bool IsHash()  {    return(HasFlag(OB_HASH_BOND));     }
      /**
       * @return True if this bond is either a wedge or hash.
       * @note: This is a seperate bond type
       * @since version 2.3
       */
      bool IsWedgeOrHash() const { return(HasFlag(WedgeOrHash)); }
      /**
       * @return True if this bond is either a cis or trans.
       * @since version 2.3
       */
      bool IsCisOrTrans() const { return(HasFlag(CisOrTrans)); }

      //! \return whether the geometry around this bond "looks" unsaturated
      bool IsDoubleBondGeometry();
      //@}

    }; // class OBBond

  //! A standard iterator over a vector of bonds
  typedef std::vector<OBBond*>::iterator OBBondIterator;

}// namespace OpenBabel

#endif   // OB_BOND_H

//! @file bond.h
//! @brief Handle bonds
