package eu.jonahbauer.raytracing.render.material;

import eu.jonahbauer.raytracing.math.Ray;
import eu.jonahbauer.raytracing.math.Vec3;
import eu.jonahbauer.raytracing.render.texture.Color;
import eu.jonahbauer.raytracing.render.texture.Texture;
import eu.jonahbauer.raytracing.scene.HitResult;
import org.jetbrains.annotations.NotNull;

import java.util.Objects;
import java.util.Optional;
import java.util.random.RandomGenerator;

public record DielectricMaterial(double refractionIndex, @NotNull Texture texture) implements Material {
    public DielectricMaterial(double refractionIndex) {
        this(refractionIndex, Color.WHITE);
    }

    public DielectricMaterial {
        Objects.requireNonNull(texture, "texture");
    }

    @Override
    public @NotNull Optional<ScatterResult> scatter(@NotNull Ray ray, @NotNull HitResult hit, @NotNull RandomGenerator random) {
        var ri = hit.isFrontFace() ? (1 / refractionIndex) : refractionIndex;

        var cosTheta = Math.min(- ray.direction().unit().times(hit.normal()), 1.0);
        var reflectance = reflectance(cosTheta);
        var reflect = reflectance > random.nextDouble();

        var newDirection = (reflect ? Optional.<Vec3>empty() : Vec3.refract(ray.direction(), hit.normal(), ri))
                .orElseGet(() -> Vec3.reflect(ray.direction(), hit.normal()));

        var attenuation = texture.get(hit);
        return Optional.of(new ScatterResult(new Ray(hit.position(), newDirection), attenuation));
    }

    private double reflectance(double cos) {
        // use schlick's approximation for reflectance
        var r0 = (1 - refractionIndex) / (1 + refractionIndex);
        r0 = r0 * r0;
        return r0 + (1 - r0) * (1 - cos) * (1 - cos) * (1 - cos) * (1 - cos) * (1 - cos);
    }
}
