/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-2012 University of Bonn. All rights reserved.
 * Copyright (C)  2013 Frederik Heber. All rights reserved.
 * 
 *
 *   This file is part of MoleCuilder.
 *
 *    MoleCuilder 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, either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    MoleCuilder 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.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with MoleCuilder.  If not, see .
 */
/** \file Bond.cpp
 * 
 * Function implementations for the classes BondLeaf, BondTree and bond.
 * 
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/Verbose.hpp"
#include "Atom/atom.hpp"
#include "Bond/bond.hpp"
#include "Element/element.hpp"
/***************************************** Functions for class bond::ptr *******************************/
/** Empty Constructor for class bond.
 */
bond::bond() :
  leftatom(NULL),
  rightatom(NULL),
  HydrogenBond(0),
  BondDegree(0)
{};
/** Constructor for class bond, taking right and left bond partner
 * \param *left left atom
 * \param *right right atom
 * \param degree bond degree
 * \param number increasing index
 */
bond::bond(atom *left, atom *right, const int degree) :
  leftatom(left),
  rightatom(right),
  HydrogenBond(0),
  BondDegree(degree)
{
  if ((left != NULL) && (right != NULL)) {
    if ((left->getType() != NULL) && (left->getType()->getAtomicNumber() == 1))
      HydrogenBond++;
    if ((right->getType() != NULL) && (right->getType()->getAtomicNumber() == 1))
      HydrogenBond++;
  }
};
/** Empty Destructor for class bond.
 */
bond::~bond()
{
  // first signal destruction of this bond
  {
    OBSERVE;
    NOTIFY(BondRemoved);
  }
  // atoms should have been destroyed and NULL'd their entry already
  ASSERT (leftatom == NULL,
      "~bond() - leftatom is not NULL.");
  ASSERT (rightatom == NULL,
      "~bond() - rightatom is not NULL.");
};
ostream & operator << (ostream &ost, const bond &b)
{
  ost << "[" << b.leftatom->getName() << " <" << b.getDegree() << "(H" << b.HydrogenBond << ")>" << b.rightatom->getName() << "]";
  return ost;
};
/** Get the other atom in a bond if one is specified.
 * \param *Atom the pointer to the one atom
 * \return pointer to the other atom in the bond, NULL if no match (indicates something's wrong with the bond) 
 */
atom * bond::GetOtherAtom(const ParticleInfo * const Atom) const
{
  ASSERT( (leftatom != NULL) && (rightatom != NULL),
      "bond::GetOtherAtom() - one of the atoms refs is NULL.");
  if(leftatom == Atom) 
    return rightatom;
  if(rightatom == Atom) 
    return leftatom;
  ELOG(1, "Bond " << *this << " does not contain atom " << *Atom << "!");
  return NULL;
};
/** Checks if an atom exists in a bond.
 * \param *ptr pointer to atom
 * \return true if it is either bond::leftatom or bond::rightatom, false otherwise
 */
bool bond::Contains(const ParticleInfo * const ptr) const
{
  return ((leftatom == ptr) || (rightatom == ptr));
};
/** Checks if an atom exists in a bond.
 * \param Nr index of atom
 * \return true if it is either bond::leftatom or bond::rightatom, false otherwise
 */
bool bond::Contains(const int number) const
{
  return ((leftatom->getNr() == number) || (rightatom->getNr() == number));
};
/** Calculates the bond length.
 * \return |a - b| with a = bond::leftatom and b = bond::rightatom.
 */
double bond::GetDistance() const
{
  return (leftatom->distance(*rightatom));
};
/** Calculates the bond length.
 * \return |a - b|^2 with a = bond::leftatom and b = bond::rightatom.
 */
double bond::GetDistanceSquared() const
{
  return (leftatom->DistanceSquared(*rightatom));
};
/** Sets either leftatom or rightatom to NULL.
 *
 * \param Atom atom to remove
 */
void bond::removeAtom(const ParticleInfo * const Atom)
{
  if (static_cast(leftatom) == Atom)
    leftatom = NULL;
  if (static_cast(rightatom) == Atom)
    rightatom = NULL;
}
void bond::setDegree(const int _degree)
{
  OBSERVE;
  BondDegree = _degree;
  NOTIFY(DegreeChanged);
}