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 org.jetbrains.annotations.Nullable;

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

public final class DirectionalMaterial implements Material {
    private final @Nullable Material front;
    private final @Nullable Material back;

    private final @NotNull Texture texture;

    public DirectionalMaterial(@Nullable Material front, @Nullable Material back) {
        this.front = front;
        this.back = back;
        this.texture = new DirectionalTexture(
                front != null ? front.texture() : null,
                back != null ? back.texture() : null
        );
    }

    @Override
    public @NotNull Texture texture() {
        return texture;
    }

    @Override
    public @NotNull Optional<ScatterResult> scatter(@NotNull Ray ray, @NotNull HitResult hit, @NotNull RandomGenerator random) {
        if (hit.isFrontFace()) {
            if (front != null) return front.scatter(ray, hit, random);
        } else {
            if (back != null) return back.scatter(ray, hit, random);
        }
        // let the ray pass through without obstruction
        return Optional.of(new ScatterResult(new Ray(ray.at(hit.t()), ray.direction()), Color.WHITE));
    }

    @Override
    public @NotNull Color emitted(@NotNull HitResult hit) {
        if (hit.isFrontFace()) {
            if (front != null) return front.emitted(hit);
        } else {
            if (back != null) return back.emitted(hit);
        }
        return Color.BLACK;
    }

    private record DirectionalTexture(@Nullable Texture front, @Nullable Texture back) implements Texture {

        @Override
        public @NotNull Color get(double u, double v, @NotNull Vec3 p) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean isUVRequired() {
            return front() != null && front().isUVRequired() || back() != null && back().isUVRequired();
        }
    }
}
