#include "Group.h"

#include "../material/Material.h"
#include "../tools/Vec3.h"

namespace shapes {
Group::Group(const util::Transformation& transform)
    : shapeList(std::vector<std::shared_ptr<Shape>>()), transform(transform) {
}
Group::Group(const util::Mat4& matrix)
    : shapeList(std::vector<std::shared_ptr<Shape>>()),
      transform(util::Transformation(matrix)) {
}

std::optional<cam::Hit> Group::intersect(const cam::Ray& r) const {
	cam::Ray imagR(transform.fromWorld.transformPoint(r.x0),
	               transform.fromWorld.transformDir(r.d), r.tmin, r.tmax,
	               r.normalize);

	std::optional<cam::Hit> result = std::nullopt;

	for (std::shared_ptr<Shape> s : shapeList) {
		if (s->bounds().intersects(imagR)) {
			std::optional<cam::Hit> temp = s->intersect(imagR);
			if (temp) {
				if (!result) {
					result = temp;
				} else if (result->scalar() > temp->scalar()) {
					result = temp;
				}
			}
		}
	}

	if (result) {
		result = std::optional<cam::Hit>(
		    {transform.toWorld.transformPoint(result->hitpoint()),
		     transform.toWorldN.transformDir(result->normal()),
		     result->scalar(), result->material});
	}
	return result;
}
util::AxisAlignedBoundingBox Group::bounds() const {
	return boundingVolume;
}
void Group::add(const Group& group) {
	shapeList.push_back(std::make_shared<Group>(group));
	rebuildBoundingVolume();
}
void Group::add(const std::shared_ptr<Shape>& shape) {
	shapeList.push_back(shape);
	rebuildBoundingVolume();
}

void Group::rebuildBoundingVolume() {
	util::AxisAlignedBoundingBox bb = shapeList[0]->bounds();
	for (auto shape_bb : shapeList) {
		bb = bb + shape_bb->bounds();
	}
	boundingVolume = bb * transform.toWorld;
}
Group shapeGroup(util::Mat4& matrix, std::shared_ptr<Shape> shape) {
	Group g(matrix);
	g.add(shape);
	return g;
}

}  // namespace shapes