//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================
#include "smtk/session/rgg/Core.h"
#include "smtk/session/rgg/json/jsonCore.h"

#include <iostream>
#include <string>
namespace smtk
{
namespace session
{
namespace rgg
{
using json = nlohmann::json;

struct Core::Internal
{
  Internal()
  {
    this->name = "nuclearCore";
    this->geometry = nullptr;
  }

  std::string name;
  GeomType type;
  // Depending on type, only one would have real data
  GeomBase* geometry;
  // Map each assembly to its layouts
  // Rect: (i, j) where i is the index along width and y is along height.
  // Hex: (i, j) where i is the index along the ring and j is the index on that layer
  UuidToSchema layout;
  UuidToCoordinates entityCoords;
  UuidToCoordinates pinsAndDuctsCoords;
  CoreExportParameters exportParams;
};

// GeomBase
Core::GeomBase::GeomBase()
{
  this->zOrigin = 0;
  this->height = 10;
}

Core::GeomBase::GeomBase(double zO, double h)
{
  this->zOrigin = zO;
  this->height = h;
}

Core::GeomBase::~GeomBase()
{
}

// GeomHex
Core::GeomHex::GeomHex() : GeomBase()
{
  this->ls = 1;
  this->ds = 10;
}

Core::GeomHex::GeomHex(double zO, double h,
                       int latticeS, double ductT) : GeomBase(zO, h)
{
  this->ls = latticeS;
  this->ds = ductT;
}

Core::GeomHex::~GeomHex()
{
}

std::pair<int, int> Core::GeomHex::latticeSize() const
{
  return std::make_pair(this->ls, this->ls);
}

std::pair<double, double> Core::GeomHex::ductThickness() const
{
  return std::make_pair(this->ds, this->ds);
}

void Core::GeomHex::setLatticeSize(const std::pair<int, int>& size)
{
  this->ls = size.first;
}

void Core::GeomHex::setDuctThickness(const std::pair<double, double>& thicknesses)
{
  this->ds = thicknesses.first;
}

// GeomRect
Core::GeomRect::GeomRect() : GeomBase()
{
  this->ls = std::make_pair(1, 1);
  this->ds = std::make_pair(10, 10);
}


Core::GeomRect::GeomRect(double zO, double h, int latticeS0,
                         int latticeS1, double ductT0, double ductT1) : GeomBase (zO, h)
{
  this->ls = std::make_pair(latticeS0, latticeS1);
  this->ds = std::make_pair(ductT0, ductT1);
}

Core::GeomRect::~GeomRect()
{
}

std::pair<int, int> Core::GeomRect::latticeSize() const
{
  return ls;
}

std::pair<double, double> Core::GeomRect::ductThickness() const
{
  return ds;
}

void Core::GeomRect::setLatticeSize(const std::pair<int, int>& size)
{
  this->ls = size;
}

void Core::GeomRect::setDuctThickness(const std::pair<double, double>& thicknesses)
{
  this->ds = thicknesses;
}



Core::Core() : m_internal(std::make_shared<Internal>())
{
}

Core::~Core()
{
}

const std::string& Core::name() const
{
  return m_internal->name;
}

Core::GeomType Core::geomType() const
{
  return m_internal->type;
}

Core::UuidToSchema& Core::layout()
{
  return m_internal->layout;
}

const Core::UuidToSchema& Core::layout() const
{
  return m_internal->layout;
}

Core::UuidToCoordinates& Core::entityToCoordinates()
{
  return this->m_internal->entityCoords;
}

const Core::UuidToCoordinates& Core::entityToCoordinates() const
{
  return this->m_internal->entityCoords;
}

Core::UuidToCoordinates& Core::pinsAndDuctsToCoordinates()
{
  return this->m_internal->pinsAndDuctsCoords;
}

const Core::UuidToCoordinates& Core::pinsAndDuctsToCoordinates() const
{
  return this->m_internal->pinsAndDuctsCoords;
}

const double& Core::zOrigin() const
{
  return m_internal->geometry->zOrigin;
}

const double& Core::height() const
{
  return m_internal->geometry->height;
}

// Hex core only uses size0
std::pair<int, int> Core::latticeSize() const
{
  return m_internal->geometry->latticeSize();
}

// Hex core only uses thickness0
std::pair<double, double> Core::ductThickness() const
{
  return m_internal->geometry->ductThickness();
}

CoreExportParameters& Core::exportParams()
{
  return m_internal->exportParams;
}

const CoreExportParameters& Core::exportParams() const
{
  return m_internal->exportParams;
}

void Core::setName(const std::string& name)
{
  m_internal->name = name;
}

void Core::setGeomType(const GeomType& type)
{
  m_internal->type = type;
  if (m_internal->geometry)
  {
    delete m_internal->geometry;
  }
  if (type == GeomType::Hex)
  {
    m_internal->geometry = new GeomHex();
  }
  else
  {
    m_internal->geometry = new GeomRect();
  }
}

void Core::setZOrigin(const double& zO)
{
  if (!m_internal->geometry)
  {
    std::cerr << "set core geometry first before set zOrigin" <<std::endl;
    return;
  }
  m_internal->geometry->zOrigin = zO;
}

void Core::setHeight(const double& h)
{
  if (!m_internal->geometry)
  {
    std::cerr << "set core geometry first before set height" <<std::endl;
    return;
  }
  m_internal->geometry->height = h;
}

void Core::setLayout(const UuidToSchema& layout)
{
  m_internal->layout = layout;
}

void Core::setEntityToCoordinates(const UuidToCoordinates& uTC)
{
  this->m_internal->entityCoords = uTC;
}

void Core::setPinsAndDuctsToCoordinates(const UuidToCoordinates& uTC)
{
  this->m_internal->pinsAndDuctsCoords = uTC;
}

// Hex core only uses size0
void Core::setLatticeSize(const int& size0, const int& size1)
{
  if (!m_internal->geometry)
  {
    std::cerr << "set core geometry first before set lattice size" <<std::endl;
    return;
  }
  m_internal->geometry->setLatticeSize(std::make_pair(size0, size1));
}

void Core::setLatticeSize(const std::pair<int, int>& ls)
{
  if (!m_internal->geometry)
  {
    std::cerr << "set core geometry first before set lattice size" <<std::endl;
    return;
  }
  m_internal->geometry->setLatticeSize(ls);
}


// Hex core only uses thickness0
void Core::setDuctThickness(const double& t0, const double& t1)
{
  if (!m_internal->geometry)
  {
    std::cerr << "set core geometry first before set lattice thickness" <<std::endl;
    return;
  }
  m_internal->geometry->setDuctThickness(std::make_pair(t0, t1));
}

void Core::setExportParams(const CoreExportParameters& aep)
{
  m_internal->exportParams = aep;
}

bool Core::operator==(const Core& other) const
{
  json thisCore = *this;
  json otherCore = other;
  return (thisCore == otherCore);
}

bool Core::operator!=(const Core& other) const
{
  return !(*this == other);
}


} // namespace rgg
} //namespace session
} // namespace smtk
