/* SPDX-FileCopyrightText: Copyright (c) 2020 - 2026, the adamantine authors.
 * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 */

#include <CubeHeatSource.hh>
#include <instantiation.hh>
#include <types.hh>

namespace adamantine
{
template <int dim>
CubeHeatSource<dim>::CubeHeatSource(
    boost::property_tree::ptree const &source_database,
    boost::optional<boost::property_tree::ptree const &> const
        &units_optional_database)
    : HeatSource<dim>()
{
  double dimension_scaling = 1.;
  double power_scaling = 1.;
  if (units_optional_database)
  {
    auto const &database = units_optional_database.get();
    // PropertyTreeInput units.heat_source.dimension
    std::string unit = database.get("heat_source.dimension", "meter");
    dimension_scaling = g_unit_scaling_factor[unit];
    // PropertyTreeInput units.heat_source.power
    unit = database.get("heat_source.power", "watt");
    power_scaling = g_unit_scaling_factor[unit];
  }

  _start_time = source_database.get<double>("start_time");
  _end_time = source_database.get<double>("end_time");
  _value = source_database.get<double>("value") * power_scaling;
  _min_point[0] = source_database.get<double>("min_x") * dimension_scaling;
  _max_point[0] = source_database.get<double>("max_x") * dimension_scaling;
  _min_point[1] = source_database.get<double>("min_y") * dimension_scaling;
  _max_point[1] = source_database.get<double>("max_y") * dimension_scaling;
  if constexpr (dim == 3)
  {
    _min_point[2] = source_database.get<double>("min_z") * dimension_scaling;
    _max_point[2] = source_database.get<double>("max_z") * dimension_scaling;
  }
}

template <int dim>
void CubeHeatSource<dim>::update_time(double time)
{
  this->_source_on = ((time > _start_time) && (time < _end_time));
}

template <int dim>
double CubeHeatSource<dim>::value(dealii::Point<dim> const &point) const
{
  if (this->_source_on)
  {
    bool in_source = true;
    for (int i = 0; i < dim; ++i)
    {
      if ((point[i] < _min_point[i]) || (point[i] > _max_point[i]))
      {
        in_source = false;
        break;
      }
    }

    if (in_source)
      return _value;
  }

  return 0.;
}

template <int dim>
dealii::VectorizedArray<double> CubeHeatSource<dim>::value(
    dealii::Point<dim, dealii::VectorizedArray<double>> const &points) const
{
  dealii::VectorizedArray<double> mask = 0.;

  if (this->_source_on)
  {
    for (unsigned int j = 0; j < points[0].size(); ++j)
    {
      if constexpr (dim == 2)
      {
        if ((points[0][j] >= _min_point[0]) &&
            (points[0][j] <= _max_point[0]) &&
            (points[1][j] >= _min_point[1]) && (points[1][j] <= _max_point[1]))
        {
          mask[j] = 1.;
        }
      }
      if constexpr (dim == 3)
      {
        if ((points[0][j] >= _min_point[0]) &&
            (points[0][j] <= _max_point[0]) &&
            (points[1][j] >= _min_point[1]) &&
            (points[1][j] <= _max_point[1]) &&
            (points[2][j] >= _min_point[2]) && (points[2][j] <= _max_point[2]))
        {
          mask[j] = 1.;
        }
      }
    }
  }

  return mask * _value;
}

template <int dim>
dealii::BoundingBox<dim>
CubeHeatSource<dim>::get_bounding_box(double const /*time*/,
                                      double const /*scaling_factor*/) const
{
  if constexpr (dim == 2)
  {
    return {{{_min_point[0], _min_point[1]}, {_max_point[0], _max_point[1]}}};
  }
  else
  {
    return {{{_min_point[0], _min_point[1], _min_point[2]},
             {_max_point[0], _max_point[1], _max_point[2]}}};
  }
}

} // namespace adamantine

INSTANTIATE_DIM(CubeHeatSource)
