// SPDX-License-Identifier: LGPL-3.0-or-later
// Author: Kristian Lytje

#include <data/symmetry/PointSymmetry.h>
#include <math/MatrixUtils.h>

#include <cassert>

using namespace ausaxs;
using namespace ausaxs::symmetry;

PointSymmetry::PointSymmetry() = default;

PointSymmetry::PointSymmetry(const Vector3<double>& translation, const Vector3<double>& rotation) {
    this->translation = translation;
    this->rotation  = rotation;
}

bool PointSymmetry::is_closed() const { return false; }

std::unique_ptr<ISymmetry> PointSymmetry::clone() const {
    return std::make_unique<PointSymmetry>(*this);
}

std::function<ausaxs::Vector3<double>(ausaxs::Vector3<double>)> PointSymmetry::get_transform(const Vector3<double>& cm, int rep) const {
    assert(rep <= 1 && "PointSymmetry always generates exactly one copy (rep must be 1).");
    if (rep == 0) {return [](Vector3<double> v) {return v;};}

    // final transform is v' = R*(v - cm) + cm + d
    //                       = R*v + (cm + d - R*cm)
    auto R = matrix::rotation_matrix<double>(rotation);
    auto T = cm + translation - R*cm;
    return [R=std::move(R), T=std::move(T)](Vector3<double> v) {
        return R*v + T;
    };
}

unsigned int PointSymmetry::repetitions() const {return 1;}
std::span<double> PointSymmetry::span_translation() {return std::span<double>(translation.begin(), translation.end());}
std::span<double> PointSymmetry::span_rotation() {return std::span<double>(rotation.begin(), rotation.end());}

ISymmetry& PointSymmetry::add(observer_ptr<const ISymmetry> other) {
    auto cast = dynamic_cast<const PointSymmetry*>(other);
    assert(cast != nullptr && "Can only add PointSymmetry with another PointSymmetry.");
    this->translation += cast->translation;
    this->rotation += cast->rotation;
    return *this;
}