/*
 * 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 .
 */
/*
 * ClusterUnitTest.cpp
 *
 *  Created on: Jan 17, 2012
 *      Author: heber
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include 
#include 
#include 
#include "CodePatterns/Assert.hpp"
#include "Atom/CopyAtoms/CopyAtoms_Simple.hpp"
#include "Descriptors/AtomIdDescriptor.hpp"
#include "Element/periodentafel.hpp"
#include "Filling/Cluster.hpp"
#include "LinearAlgebra/Vector.hpp"
#include "LinearAlgebra/RealSpaceMatrix.hpp"
#include "Shapes/BaseShapes.hpp"
#include "Shapes/Shape.hpp"
#include "World.hpp"
#include "WorldTime.hpp"
#include "ClusterUnitTest.hpp"
#ifdef HAVE_TESTRUNNER
#include "UnitTestMain.hpp"
#endif /*HAVE_TESTRUNNER*/
/********************************************** Test classes **************************************/
// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( ClusterTest );
void ClusterTest::setUp()
{
  // failing asserts should be thrown
  ASSERT_DO(Assert::Throw);
  // this must not compile: private default Cstor
  // cluster = new Cluster();
  shape  = new Shape(Sphere());
  // create an atom
  _atom = World::getInstance().createAtom();
  const element * hydrogen = World::getInstance().getPeriode()->FindElement(1);
  CPPUNIT_ASSERT(hydrogen != NULL);
  _atom->setType(hydrogen);
  _atom->setPosition(Vector(0.,0.,0.));
  _atomId = _atom->getId();
  cluster = new Cluster(*shape);
}
void ClusterTest::tearDown()
{
  delete cluster;
  World::purgeInstance();
  WorldTime::purgeInstance();
}
/** Test whether setting and getting works
 *
 */
void ClusterTest::insert_eraseTest()
{
  // check for empty cluster
  CPPUNIT_ASSERT_EQUAL( (size_t)0, cluster->getAtomIds().size() );
  // Shape is sphere at (0,0,0) with radius 1
  {
    // insert present atom at center of shape
#ifndef NDEBUG
    CPPUNIT_ASSERT_NO_THROW( cluster->insert(_atomId) );
#else
    cluster->insert(_atomId);
#endif
    CPPUNIT_ASSERT_EQUAL( (size_t)1, cluster->getAtomIds().size() );
#ifndef NDEBUG
    CPPUNIT_ASSERT_NO_THROW( cluster->erase(_atomId) );
#else
    cluster->erase(_atomId);
#endif
  }
  {
    // erase non-existing atom
    const atomId_t falseId = _atomId+1;
    CPPUNIT_ASSERT_EQUAL(
        (const atom *)NULL,
        const_cast(World::getInstance()).getAtom(AtomById(falseId)) );
#ifndef NDEBUG
    std::cout << "The following Assertion is intended and does not present a failure of the test." << std::endl;
    CPPUNIT_ASSERT_THROW( cluster->erase(falseId), Assert::AssertionFailure );
#else
    cluster->erase(falseId);
#endif
  }
  {
    // erase non-present atom
#ifndef NDEBUG
    std::cout << "The following Assertion is intended and does not present a failure of the test." << std::endl;
    CPPUNIT_ASSERT_THROW( cluster->erase(_atomId), Assert::AssertionFailure );
#else
    cluster->erase(_atomId);
#endif
  }
  {
    // insert present atom within shape
    _atom->setPosition(Vector(.5,.5,.5));
#ifndef NDEBUG
    CPPUNIT_ASSERT_NO_THROW( cluster->insert(_atomId) );
#else
    cluster->insert(_atomId);
#endif
    CPPUNIT_ASSERT_EQUAL( (size_t)1, cluster->getAtomIds().size() );
#ifndef NDEBUG
    CPPUNIT_ASSERT_NO_THROW( cluster->erase(_atomId) );
#else
    cluster->erase(_atomId);
#endif
    _atom->setPosition(Vector(0.,0.,0.));
  }
  {
    // insert present atom outside shape
    _atom->setPosition(Vector(2.,0.,0.));
#ifndef NDEBUG
    std::cout << "The following Assertion is intended and does not present a failure of the test." << std::endl;
    CPPUNIT_ASSERT_THROW( cluster->insert(_atomId), Assert::AssertionFailure );
#else
    cluster->insert(_atomId);
#endif
    CPPUNIT_ASSERT_EQUAL( (size_t)0, cluster->getAtomIds().size() );
    _atom->setPosition(Vector(0.,0.,0.));
  }
  {
    // insert present atom on boundary shape
    _atom->setPosition(Vector(1.,0.,0.));
#ifndef NDEBUG
    CPPUNIT_ASSERT_NO_THROW( cluster->insert(_atomId) );
#else
    cluster->insert(_atomId);
#endif
    CPPUNIT_ASSERT_EQUAL( (size_t)1, cluster->getAtomIds().size() );
#ifndef NDEBUG
    CPPUNIT_ASSERT_NO_THROW( cluster->erase(_atomId) );
#else
    cluster->erase(_atomId);
#endif
    _atom->setPosition(Vector(0.,0.,0.));
  }
  {
    // insert non-existing atom
    const atomId_t falseId = _atomId+1;
    CPPUNIT_ASSERT_EQUAL(
        (const atom *)NULL,
        const_cast(World::getInstance()).getAtom(AtomById(falseId)) );
#ifndef NDEBUG
    std::cout << "The following Assertion is intended and does not present a failure of the test." << std::endl;
    CPPUNIT_ASSERT_THROW( cluster->insert(falseId), Assert::AssertionFailure );
#else
    cluster->insert(falseId);
#endif
    CPPUNIT_ASSERT_EQUAL( (size_t)0, cluster->getAtomIds().size() );
  }
}
/** Test whether setting and getting works
 *
 */
void ClusterTest::setter_getterTest()
{
  CPPUNIT_ASSERT( *shape == cluster->getShape() );
  CPPUNIT_ASSERT( cluster->atoms == cluster->getAtomIds() );
}
/** Test whether translate() works
 *
 */
void ClusterTest::translateTest()
{
  // insert
  cluster->insert(_atomId);
  CPPUNIT_ASSERT_EQUAL( (size_t)1, cluster->getAtomIds().size() );
  // check we are at origin
  CPPUNIT_ASSERT_EQUAL( Vector(0.,0.,0.), cluster->getShape().getCenter() );
  const atom * _atom = NULL;
  CPPUNIT_ASSERT_NO_THROW( _atom = cluster->getAtomById(_atomId) );
  CPPUNIT_ASSERT( _atom != NULL );
  CPPUNIT_ASSERT_EQUAL( Vector(0.,0.,0.), _atom->getPosition() );
  // move
  cluster->translate( Vector(1.,0.,0.) );
  CPPUNIT_ASSERT_EQUAL( Vector(1.,0.,0.), cluster->getShape().getCenter() );
  CPPUNIT_ASSERT_EQUAL( Vector(1.,0.,0.), cluster->getAtomById(_atomId)->getPosition() );
}
/** Test whether transform() works
 *
 */
void ClusterTest::transformTest()
{
  // insert
  cluster->insert(_atomId);
  CPPUNIT_ASSERT_EQUAL( (size_t)1, cluster->getAtomIds().size() );
  // check we are at origin
  CPPUNIT_ASSERT_EQUAL( Vector(0.,0.,0.), cluster->getShape().getCenter() );
  const atom * _atom = NULL;
  CPPUNIT_ASSERT_NO_THROW( _atom = cluster->getAtomById(_atomId) );
  CPPUNIT_ASSERT( _atom != NULL );
  CPPUNIT_ASSERT_EQUAL( Vector(0.,0.,0.), _atom->getPosition() );
  // transform
  RealSpaceMatrix M;
  M.setIdentity();
  cluster->transform( M );
  CPPUNIT_ASSERT_EQUAL( Vector(0.,0.,0.), cluster->getShape().getCenter() );
  CPPUNIT_ASSERT_EQUAL( Vector(0.,0.,0.), cluster->getAtomById(_atomId)->getPosition() );
}
/** Test whether IsInShape() works
 *
 */
void ClusterTest::IsInShapeTest()
{
  // at origin we are inside
  CPPUNIT_ASSERT( shape->isInside(_atom->getPosition() ) );
  CPPUNIT_ASSERT( cluster->IsInShape(_atomId) );
  // at boundary we are inside
  _atom->setPosition( Vector(1.,0.,0.) );
  CPPUNIT_ASSERT( shape->isInside(_atom->getPosition() ) );
  CPPUNIT_ASSERT( cluster->IsInShape(_atomId) );
  // now we are outside
  _atom->setPosition( Vector(2.,0.,0.) );
  CPPUNIT_ASSERT( !shape->isInside(_atom->getPosition() ) );
  CPPUNIT_ASSERT( !cluster->IsInShape(_atomId) );
}
/** Test whether clone() works
 *
 */
void ClusterTest::cloneTest()
{
  // insert atom ...
  cluster->insert(_atomId);
  // ... and clone
  CopyAtoms_Simple copyMethod;
  ClusterInterface::Cluster_impl clonedCluster = cluster->clone(copyMethod);
  // check for present atom
  CPPUNIT_ASSERT_EQUAL( (size_t)1, clonedCluster->getAtomIds().size() );
  // check for different ids
  CPPUNIT_ASSERT_EQUAL(
      (size_t)2,
      const_cast(World::getInstance()).getAllAtoms().size() );
  CPPUNIT_ASSERT( *(cluster->getAtomIds().begin()) != *(clonedCluster->getAtomIds().begin()) );
  // check for same position
  atomId_t id = *(clonedCluster->getAtomIds().begin());
  const atom * const _atom = const_cast(World::getInstance()).
      getAtom(AtomById(id));
  CPPUNIT_ASSERT( _atom != NULL );
  CPPUNIT_ASSERT( (*cluster->getAtomRefs().begin())->getPosition() ==_atom->getPosition() );
  // check that shape is the same
  CPPUNIT_ASSERT( cluster->getShape() == clonedCluster->getShape() );
  CPPUNIT_ASSERT( cluster->getShape().getCenter() == clonedCluster->getShape().getCenter() );
  CPPUNIT_ASSERT( cluster->getShape().getRadius() == clonedCluster->getShape().getRadius() );
}
/** Test whether getAtomRefs() works
 *
 */
void ClusterTest::getAtomRefsTest()
{
  Cluster::AtomVector Atomvec;
  // check with empty cluster
  CPPUNIT_ASSERT_EQUAL( Atomvec, cluster->getAtomRefs() );
  // insert into both ...
  Atomvec.push_back(_atom);
  cluster->insert(_atomId);
  // ...and check again
  CPPUNIT_ASSERT_EQUAL( Atomvec, cluster->getAtomRefs() );
}