package eu.jonahbauer.raytracing.scene.transform;

import eu.jonahbauer.raytracing.math.AABB;
import eu.jonahbauer.raytracing.math.Ray;
import eu.jonahbauer.raytracing.math.Vec3;
import eu.jonahbauer.raytracing.scene.HitResult;
import eu.jonahbauer.raytracing.scene.Hittable;
import org.jetbrains.annotations.NotNull;

public final class RotateY extends Transform {
    private final double cos;
    private final double sin;

    private final @NotNull AABB bbox;

    public RotateY(@NotNull Hittable object, double angle) {
        super(object);
        this.cos = Math.cos(angle);
        this.sin = Math.sin(angle);

        var bbox = object.getBoundingBox();

        var min = new Vec3(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE);
        var max = new Vec3(- Double.MAX_VALUE, - Double.MAX_VALUE, - Double.MAX_VALUE);

        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                for (int k = 0; k < 2; k++) {
                    var x = i * bbox.max().x() + (1 - i) * bbox.min().x();
                    var y = j * bbox.max().y() + (1 - j) * bbox.min().y();
                    var z = k * bbox.max().z() + (1 - k) * bbox.min().z();

                    var newx =  cos * x + sin * z;
                    var newz = -sin * x + cos * z;

                    var temp = new Vec3(newx, y, newz);

                    min = Vec3.min(min, temp);
                    max = Vec3.max(max, temp);
                }
            }
        }

        this.bbox = new AABB(min, max);
    }

    @Override
    protected @NotNull Ray transform(@NotNull Ray ray) {
        var origin = ray.origin();
        var direction = ray.direction();

        var newOrigin = new Vec3(
                cos * origin.x() - sin * origin.z(),
                origin.y(),
                sin * origin.x() + cos * origin.z()
        );
        var newDirection = new Vec3(
                cos * direction.x() - sin * direction.z(),
                direction.y(),
                sin * direction.x() + cos * direction.z()
        );

        return new Ray(newOrigin, newDirection);
    }

    @Override
    protected @NotNull HitResult transform(@NotNull HitResult result) {
        var position = result.position();
        var newPosition = new Vec3(
                cos * position.x() + sin * position.z(),
                position.y(),
                - sin * position.x() + cos * position.z()
        );

        var normal = result.normal();
        var newNormal = new Vec3(
                cos * normal.x() + sin * normal.z(),
                normal.y(),
                -sin * normal.x() + cos * normal.z()
        );

        return new HitResult(result.t(), newPosition, newNormal, result.material(), result.frontFace());
    }

    @Override
    public @NotNull AABB getBoundingBox() {
        return bbox;
    }
}
