/// \file VolumeFactory.h
/// \author Johannes de Fine Licht (johannes.definelicht@cern.ch)

#ifndef VECGEOM_MANAGEMENT_VOLUMEFACTORY_H_
#define VECGEOM_MANAGEMENT_VOLUMEFACTORY_H_

#include "VecGeom/volumes/PlacedVolume.h"
#include "VecGeom/base/Transformation3D.h"
#include "VecGeom/volumes/LogicalVolume.h"

namespace vecgeom {
inline namespace VECGEOM_IMPL_NAMESPACE {

class VolumeFactory {

public:
  static VolumeFactory &Instance()
  {
    static VolumeFactory instance;
    return instance;
  }

#ifndef VECCORE_CUDA

  template <typename VolumeType>
  static VPlacedVolume *CreateByTransformation(LogicalVolume const *const logical_volume,
                                               Transformation3D const *const transformation,
                                               const TranslationCode trans_code, const RotationCode rot_code,
                                               VPlacedVolume *const placement = NULL);

  // takes an existing placed volume and creates new one (changes its type) to a given Helper and ImplKernel
  // but keeps the transformation specialization properties
  // (used only internally)
  template <template <typename, int, int> class SpecializedHelper, typename ImplKernel>
  static VPlacedVolume *ChangeTypeKeepTransformation(VPlacedVolume const *);

#else

  template <typename VolumeType>
  VECCORE_ATT_DEVICE
  static VPlacedVolume *CreateByTransformation(LogicalVolume const *const logical_volume,
                                               Transformation3D const *const transformation,
                                               const TranslationCode trans_code, const RotationCode rot_code,
                                               const int id, const int copy_no, const int child_id,
                                               VPlacedVolume *const placement = NULL);

#endif

private:
  VolumeFactory() {}
  VolumeFactory(VolumeFactory const &);
  VolumeFactory &operator=(VolumeFactory const &);
};

template <typename VolumeType>
VECCORE_ATT_DEVICE
VPlacedVolume *VolumeFactory::CreateByTransformation(LogicalVolume const *const logical_volume,
                                                     Transformation3D const *const transformation,
                                                     const TranslationCode trans_code, const RotationCode rot_code,
#ifdef VECCORE_CUDA
                                                     const int id, const int copy_no, const int child_id,
#endif
                                                     VPlacedVolume *const placement)
{

  (void)trans_code;
  (void)rot_code;

// Specialization on translation and rotation.
// Script generated by ../scripts/generate_specializations.py
#include "TransformationSpecializations.icc"
}

#ifndef VECCORE_CUDA
template <template <typename, int, int> class SpecializedHelper, typename ImplKernel>
VPlacedVolume *VolumeFactory::ChangeTypeKeepTransformation(VPlacedVolume const *pvol)
{
#ifndef VECGEOM_NO_SPECIALIZATION
  const auto trans_code = pvol->GetTransCode();
  const auto rot_code   = pvol->GetRotCode();
  // the if statements are taken from TransformationSpecializations.icc
  if (trans_code == translation::kGeneric && rot_code == 0x1b1) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x1b1>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x1b1) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x1b1>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x18e) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x18e>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x18e) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x18e>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x076) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x076>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x076) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x076>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x16a) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x16a>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x16a) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x16a>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x155) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x155>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x155) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x155>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x0ad) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x0ad>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x0ad) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x0ad>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x0dc) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x0dc>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x0dc) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x0dc>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x0e3) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x0e3>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x0e3) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x0e3>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x11b) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x11b>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x11b) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x11b>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x0a1) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x0a1>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x0a1) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x0a1>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x10a) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x10a>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x10a) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x10a>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x046) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x046>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x046) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x046>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x062) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x062>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x062) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x062>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x054) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x054>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x054) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x054>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x111) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x111>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x111) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x111>(pvol);
  }
  if (trans_code == translation::kGeneric && rot_code == 0x200) {
    return new SpecializedHelper<ImplKernel, translation::kGeneric, 0x200>(pvol);
  }
  if (trans_code == translation::kIdentity && rot_code == 0x200) {
    return new SpecializedHelper<ImplKernel, translation::kIdentity, 0x200>(pvol);
  }
#endif
  return new SpecializedHelper<ImplKernel, translation::kGeneric, rotation::kGeneric>(pvol);
}
#endif
} // namespace VECGEOM_IMPL_NAMESPACE
} // namespace vecgeom

#endif // VECGEOM_MANAGEMENT_VOLUMEFACTORY_H_
