/*
 * Project: MoleCuilder
 * Description: creates and alters molecular systems
 * Copyright (C)  2010-2012 University of Bonn. 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 .
 */
/*
 * ListOfBondsUnitTest.cpp
 *
 *  Created on: 18 Oct 2009
 *      Author: user
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
using namespace std;
#include 
#include 
#include 
#include 
#include "CodePatterns/Log.hpp"
#include "World.hpp"
#include "Atom/atom.hpp"
#include "Bond/bond.hpp"
#include "Element/element.hpp"
#include "molecule.hpp"
#include "Element/periodentafel.hpp"
#include "World.hpp"
#include "WorldTime.hpp"
#include "ListOfBondsUnitTest.hpp"
#ifdef HAVE_TESTRUNNER
#include "UnitTestMain.hpp"
#endif /*HAVE_TESTRUNNER*/
/********************************************** Test classes **************************************/
// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( ListOfBondsTest );
void ListOfBondsTest::setUp()
{
  atom *Walker = NULL;
  WorldTime::getInstance().setTime(0);
  // construct element
  hydrogen = World::getInstance().getPeriode()->FindElement(1);
  CPPUNIT_ASSERT(hydrogen != NULL && "could not find element hydrogen");
  // construct molecule (tetraeder of hydrogens)
  TestMolecule = World::getInstance().createMolecule();
  CPPUNIT_ASSERT(TestMolecule != NULL && "could not create molecule");
  Walker = World::getInstance().createAtom();
  CPPUNIT_ASSERT(Walker != NULL && "could not create atom");
  Walker->setType(hydrogen);
  Walker->setPosition(Vector(1., 0., 1. ));
  TestMolecule->AddAtom(Walker);
  Walker = World::getInstance().createAtom();
  CPPUNIT_ASSERT(Walker != NULL && "could not create atom");
  Walker->setType(hydrogen);
  Walker->setPosition(Vector(0., 1., 1. ));
  TestMolecule->AddAtom(Walker);
  Walker = World::getInstance().createAtom();
  CPPUNIT_ASSERT(Walker != NULL && "could not create atom");
  Walker->setType(hydrogen);
  Walker->setPosition(Vector(1., 1., 0. ));
  TestMolecule->AddAtom(Walker);
  Walker = World::getInstance().createAtom();
  CPPUNIT_ASSERT(Walker != NULL && "could not create atom");
  Walker->setType(hydrogen);
  Walker->setPosition(Vector(0., 0., 0. ));
  TestMolecule->AddAtom(Walker);
  // check that TestMolecule was correctly constructed
  CPPUNIT_ASSERT_EQUAL( TestMolecule->getAtomCount(), 4 );
};
void ListOfBondsTest::tearDown()
{
  // remove
  World::getInstance().destroyMolecule(TestMolecule);
  // note that all the atoms, molecules, the tafel and the elements
  // are all cleaned when the world is destroyed
  World::purgeInstance();
  logger::purgeInstance();
};
/** Tests whether setup worked correctly.
 *
 */
void ListOfBondsTest::SetupTest()
{
  CPPUNIT_ASSERT_EQUAL( false, TestMolecule->empty() );
  CPPUNIT_ASSERT_EQUAL( (size_t)4, TestMolecule->size() );
};
/** Unit Test of molecule::AddBond()
 *
 */
void ListOfBondsTest::AddingBondTest()
{
  bond *Binder = NULL;
  molecule::iterator iter = TestMolecule->begin();
  atom *atom1 = *iter;
  iter++;
  atom *atom2 = *iter;
  CPPUNIT_ASSERT( atom1 != NULL );
  CPPUNIT_ASSERT( atom2 != NULL );
  // add bond
  Binder = TestMolecule->AddBond(atom1, atom2, 1);
  CPPUNIT_ASSERT( Binder != NULL );
  CPPUNIT_ASSERT_EQUAL ( true, TestMolecule->hasBondStructure() );
  // check that bond contains the two atoms
  CPPUNIT_ASSERT_EQUAL( true, Binder->Contains(atom1) );
  CPPUNIT_ASSERT_EQUAL( true, Binder->Contains(atom2) );
  // check that bond is present in both atoms
  const BondList &bondlist1 = atom1->getListOfBonds();
  BondList::const_iterator bonditer;
  bonditer = bondlist1.begin();
  bond *TestBond1 = *bonditer;
  CPPUNIT_ASSERT_EQUAL( TestBond1, Binder );
  const BondList &bondlist2 = atom2->getListOfBonds();
  bonditer = bondlist2.begin();
  bond *TestBond2 = *bonditer;
  CPPUNIT_ASSERT_EQUAL( TestBond2, Binder );
};
/** Unit Test of molecule::RemoveBond()
 *
 */
void ListOfBondsTest::RemovingBondTest()
{
  bond *Binder = NULL;
  molecule::iterator iter = TestMolecule->begin();
  atom *atom1 = *iter;
  iter++;
  atom *atom2 = *iter;
  CPPUNIT_ASSERT( atom1 != NULL );
  CPPUNIT_ASSERT( atom2 != NULL );
  // add bond
  Binder = TestMolecule->AddBond(atom1, atom2, 1);
  CPPUNIT_ASSERT( Binder != NULL );
  // remove bond
  TestMolecule->RemoveBond(Binder);
  // check if removed from atoms
  {
    const BondList& ListOfBonds = atom1->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 0, ListOfBonds.size() );
  }
  {
    const BondList& ListOfBonds = atom2->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 0, ListOfBonds.size() );
  }
  // check if removed from molecule
  CPPUNIT_ASSERT_EQUAL( false, TestMolecule->hasBondStructure() );
};
/** Unit Test of molecule::RemoveBonds()
 *
 */
void ListOfBondsTest::RemovingBondsTest()
{
  bond *Binder = NULL;
  molecule::iterator iter = TestMolecule->begin();
  atom *atom1 = *iter;
  iter++;
  atom *atom2 = *iter;
  iter++;
  atom *atom3 = *iter;
  CPPUNIT_ASSERT( atom1 != NULL );
  CPPUNIT_ASSERT( atom2 != NULL );
  CPPUNIT_ASSERT( atom3 != NULL );
  // add bond
  Binder = TestMolecule->AddBond(atom1, atom2, 1);
  CPPUNIT_ASSERT( Binder != NULL );
  Binder = TestMolecule->AddBond(atom1, atom3, 1);
  CPPUNIT_ASSERT( Binder != NULL );
  Binder = TestMolecule->AddBond(atom2, atom3, 1);
  CPPUNIT_ASSERT( Binder != NULL );
  // check that all are present
  {
    const BondList& ListOfBonds = atom1->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 2, ListOfBonds.size() );
  }
  {
    const BondList& ListOfBonds = atom2->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 2, ListOfBonds.size() );
  }
  {
    const BondList& ListOfBonds = atom3->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 2, ListOfBonds.size() );
  }
  // remove bond
  TestMolecule->RemoveBonds(atom1);
  // check if removed from atoms
  {
    const BondList& ListOfBonds = atom1->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 0, ListOfBonds.size() );
  }
  {
    const BondList& ListOfBonds = atom2->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 1, ListOfBonds.size() );
  }
  {
    const BondList& ListOfBonds = atom3->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 1, ListOfBonds.size() );
  }
  // check if removed from molecule
  CPPUNIT_ASSERT_EQUAL( true, TestMolecule->hasBondStructure() );
  CPPUNIT_ASSERT_EQUAL( (int)1, TestMolecule->getBondCount() );
};
/** Unit Test of delete(bond *)
 *
 */
void ListOfBondsTest::DeleteBondTest()
{
  bond *Binder = NULL;
  molecule::iterator iter = TestMolecule->begin();
  atom *atom1 = *iter;
  iter++;
  atom *atom2 = *iter;
  CPPUNIT_ASSERT( atom1 != NULL );
  CPPUNIT_ASSERT( atom2 != NULL );
  // add bond
  Binder = TestMolecule->AddBond(atom1, atom2, 1);
  CPPUNIT_ASSERT( Binder != NULL );
  // remove bond
  delete(Binder);
  // check if removed from atoms
  {
    const BondList& ListOfBonds = atom1->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 0, ListOfBonds.size() );
  }
  {
    const BondList& ListOfBonds = atom2->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 0, ListOfBonds.size() );
  }
  // check if removed from molecule
  CPPUNIT_ASSERT_EQUAL( false, TestMolecule->hasBondStructure() );
};
/** Unit Test of molecule::RemoveAtom()
 *
 */
void ListOfBondsTest::RemoveAtomTest()
{
  bond *Binder = NULL;
  molecule::iterator iter = TestMolecule->begin();
  atom *atom1 = *iter;
  iter++;
  atom *atom2 = *iter;
  CPPUNIT_ASSERT( atom1 != NULL );
  CPPUNIT_ASSERT( atom2 != NULL );
  // add bond
  Binder = TestMolecule->AddBond(atom1, atom2, 1);
  CPPUNIT_ASSERT( Binder != NULL );
  // remove atom2
  TestMolecule->RemoveAtom(atom2);
  // check bond if removed from other atom
  {
    const BondList& ListOfBonds = atom1->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 0, ListOfBonds.size() );
  }
  // check if removed from molecule
  CPPUNIT_ASSERT_EQUAL( false, TestMolecule->hasBondStructure() );
};
/** Unit Test of delete(atom *)
 *
 */
void ListOfBondsTest::DeleteAtomTest()
{
  atom *atom1 = NULL;
  atom *atom2 = NULL;
  bond *Binder = NULL;
  {
    molecule::iterator iter = TestMolecule->begin();
    atom1 = *iter;
    iter++;
    atom2 = *iter;
  }
  CPPUNIT_ASSERT( atom1 != NULL );
  CPPUNIT_ASSERT( atom2 != NULL );
  // add bond
  Binder = TestMolecule->AddBond(atom1, atom2, 1);
  CPPUNIT_ASSERT( Binder != NULL );
  // access test via CurrentTime
  {
    const BondList& ListOfBonds = atom1->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 1, ListOfBonds.size() );
  }
  {
    const BondList& ListOfBonds = atom2->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 1, ListOfBonds.size() );
  }
  CPPUNIT_ASSERT_EQUAL( true, TestMolecule->hasBondStructure() );
  // remove atom2
  World::getInstance().destroyAtom(atom2);
  // check bond if removed from other atom for all time steps
  {
    const BondList& ListOfBonds = atom1->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 0, ListOfBonds.size() );
  }
  // check if removed from molecule
  CPPUNIT_ASSERT_EQUAL( false, TestMolecule->hasBondStructure() );
};
/** Unit test on ListOfBonds at multiple time steps.
 *
 */
void ListOfBondsTest::MultipleTimeStepTest()
{
  atom *atom1 = NULL;
  atom *atom2 = NULL;
  bond *Binder = NULL;
  {
    molecule::iterator iter = TestMolecule->begin();
    atom1 = *iter;
    iter++;
    atom2 = *iter;
  }
  CPPUNIT_ASSERT( atom1 != NULL );
  CPPUNIT_ASSERT( atom2 != NULL );
  // add bond
  WorldTime::getInstance().setTime(0);
  Binder = TestMolecule->AddBond(atom1, atom2, 1);
  CPPUNIT_ASSERT( Binder != NULL );
  WorldTime::getInstance().setTime(1);
  Binder = TestMolecule->AddBond(atom1, atom2, 1);
  CPPUNIT_ASSERT( Binder != NULL );
  // access test via CurrentTime
  { // time step 0
    WorldTime::getInstance().setTime(0);
    {
      const BondList& ListOfBonds = atom1->getListOfBonds();
      CPPUNIT_ASSERT_EQUAL( (size_t) 1, ListOfBonds.size() );
    }
    {
      const BondList& ListOfBonds = atom2->getListOfBonds();
      CPPUNIT_ASSERT_EQUAL( (size_t) 1, ListOfBonds.size() );
    }
    CPPUNIT_ASSERT_EQUAL( true, TestMolecule->hasBondStructure() );
  }
  { // time step 1
    WorldTime::getInstance().setTime(1);
    {
      const BondList& ListOfBonds = atom1->getListOfBonds();
      CPPUNIT_ASSERT_EQUAL( (size_t) 1, ListOfBonds.size() );
    }
    {
      const BondList& ListOfBonds = atom2->getListOfBonds();
      CPPUNIT_ASSERT_EQUAL( (size_t) 1, ListOfBonds.size() );
    }
    CPPUNIT_ASSERT_EQUAL( true, TestMolecule->hasBondStructure() );
    WorldTime::getInstance().setTime(0);
  }
  // access time step directly.
  { // time step 0
    {
      const BondList& ListOfBonds = atom1->getListOfBondsAtStep(0);
      CPPUNIT_ASSERT_EQUAL( (size_t) 1, ListOfBonds.size() );
    }
    {
      const BondList& ListOfBonds = atom2->getListOfBondsAtStep(0);
      CPPUNIT_ASSERT_EQUAL( (size_t) 1, ListOfBonds.size() );
    }
  }
  { // time step 1
    {
      const BondList& ListOfBonds = atom1->getListOfBondsAtStep(1);
      CPPUNIT_ASSERT_EQUAL( (size_t) 1, ListOfBonds.size() );
    }
    {
      const BondList& ListOfBonds = atom1->getListOfBondsAtStep(1);
      CPPUNIT_ASSERT_EQUAL( (size_t) 1, ListOfBonds.size() );
    }
  }
  // remove atom2
  World::getInstance().destroyAtom(atom2);
  // check bond if removed from other atom for all time steps
  {
    WorldTime::getInstance().setTime(0);
    const BondList& ListOfBonds = atom1->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 0, ListOfBonds.size() );
    CPPUNIT_ASSERT_EQUAL( false, TestMolecule->hasBondStructure() );
  }
  {
    WorldTime::getInstance().setTime(1);
    const BondList& ListOfBonds = atom1->getListOfBonds();
    CPPUNIT_ASSERT_EQUAL( (size_t) 0, ListOfBonds.size() );
    CPPUNIT_ASSERT_EQUAL( false, TestMolecule->hasBondStructure() );
    WorldTime::getInstance().setTime(0);
  }
}