/*
 * 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 .
 */
/*
 * ShapeFactory.cpp
 *
 *  Created on: Sep 5, 2012
 *      Author: ankele
 */
// include config.h
#ifdef HAVE_CONFIG_H
#include 
#endif
#include "CodePatterns/MemDebug.hpp"
#include "ShapeFactory.hpp"
#include "CodePatterns/Log.hpp"
#include "BaseShapes.hpp"
#include "ShapeOps.hpp"
#include "CodePatterns/Singleton_impl.hpp"
#include "LinearAlgebra/Vector.hpp"
#include "LinearAlgebra/RealSpaceMatrix.hpp"
#include "molecule.hpp"
#include "World.hpp"
ShapeFactory::ShapeFactory()
{
  // Create map (type -> name).
  shapeNameMap[NowhereType] = "nowhere";
  shapeNameMap[EverywhereType] = "everywhere";
  shapeNameMap[SphereType] = "sphere";
  shapeNameMap[CuboidType] = "cube";
  shapeNameMap[PolygonType] = "polygon";
  shapeNameMap[CombinedType] = "combined";
  shapeNameMap[CylinderType] = "cylinder";
  shapeNameMap[MoleculeSurfaceType] = "molecule-surface";
  // Create inverse map.
  for (ShapeNameMap::iterator iter = shapeNameMap.begin(); iter != shapeNameMap.end(); ++ iter)
    nameShapeMap[iter->second] = iter->first;
  // Create baseShapeName list.
  for (ShapeNameMap::iterator iter = shapeNameMap.begin(); iter != shapeNameMap.end(); ++ iter)
    if (iter->first != CombinedType)
      baseShapeNames.push_back(iter->second);
}
ShapeType ShapeFactory::getShapeByName(const std::string& name) const
{
  NameShapeMap::const_iterator iter = nameShapeMap.find(name);
  ASSERT(iter != nameShapeMap.end(),
      "ShapeFactory::getShapeByName() - unknown name: "+name+".");
  return iter->second;
}
std::string ShapeFactory::getShapeName(ShapeType type) const
{
  ShapeNameMap::const_iterator iter = shapeNameMap.find(type);
  ASSERT(iter != shapeNameMap.end(),
      "ShapeFactory::getShapeName() - unknown type: "+toString(type)+".");
  return iter->second;
}
bool ShapeFactory::isValidShapeName(const std::string& name) const
{
  NameShapeMap::const_iterator iter = nameShapeMap.find(name);
  return (iter != nameShapeMap.end());
}
bool ShapeFactory::isSimpleShape(const ShapeType type) const
{
  return ((type != CombinedType) && (type != MoleculeSurfaceType) );
}
bool ShapeFactory::isSimpleShape(const std::string &name) const
{
  return isSimpleShape(getShapeByName(name));
}
const std::vector &ShapeFactory::getBaseShapeNames() const
{
  return baseShapeNames;
}
ShapeFactory::~ShapeFactory()
{
}
Shape ShapeFactory::produce(ShapeType type, const Vector &translation, const Vector &stretch, double angleX, double angleY, double angleZ) const
{
  for (int i=0; i 0,
        "ShapeFactory::setStretch() - non positive component.");
  }
  // Create the basic shape.
  Shape s = Nowhere();
  switch(type){
    case NowhereType:
      s = Nowhere();
      break;
    case EverywhereType:
      s = Everywhere();
      break;
    case CuboidType:
      s = Cuboid();
      break;
    case SphereType:
      s = Sphere();
      break;
    case CylinderType:
      s = Cylinder();
      break;
    case MoleculeSurfaceType:
    {
      const std::vector molecules =
          const_cast(World::getInstance()).getSelectedMolecules();
      if (molecules.empty()) {
        ELOG(2, "No molecule is selected, cannot factorize its bounding shape.");
        s = Nowhere();
      } else {
        const molecule * const mol = (*molecules.begin());
        LOG(2, "DEBUG: Factoring shape from bounding box of molecule " << mol->getName() << ".");
        s = mol->getBoundingShape(stretch[0]);
      }
      break;
    }
    default:
      ASSERT(false,
          "ShapeFactory::produce - unhandled shape type: "+toString(type)+".");
  }
  if (type != MoleculeSurfaceType) {
    // Transform (if necessary).
    if (stretch != Vector(1., 1., 1.))
      s = ::stretch(s, stretch);
    if ((angleX != 0) || (angleY != 0) || (angleZ != 0)){
      RealSpaceMatrix rotation;
      rotation.setRotation(angleX, angleY, angleZ);
      s = transform(s, rotation);
    }
    if (!translation.IsZero())
      s = translate(s, translation);
  }
  return s;
}
CONSTRUCT_SINGLETON(ShapeFactory)