package eu.jonahbauer.raytracing.scene.util;

import eu.jonahbauer.raytracing.math.BoundingBox;
import eu.jonahbauer.raytracing.math.Range;
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;

import java.util.Collection;
import java.util.Objects;
import java.util.Optional;

public abstract class HittableCollection implements Hittable {

    @Override
    public final @NotNull Optional<HitResult> hit(@NotNull Ray ray, @NotNull Range range) {
        var state = new State(range);
        hit(ray, state);
        return state.getResult();
    }

    public abstract void hit(@NotNull Ray ray, @NotNull State state);

    protected static @NotNull Optional<BoundingBox> getBoundingBox(@NotNull Collection<? extends @NotNull Hittable> objects) {
        var bbox = new BoundingBox(Vec3.ZERO, Vec3.ZERO);
        for (var object : objects) {
            var b = object.getBoundingBox();
            if (b.isPresent()) {
                bbox = bbox.expand(b.get());
            } else {
                bbox = null;
                break;
            }
        }
        return Optional.ofNullable(bbox);
    }

    protected static boolean hit(@NotNull State state, @NotNull Ray ray, @NotNull Hittable object) {
        var r = object.hit(ray, state.range);
        if (r.isPresent()) {
            if (state.range.surrounds(r.get().t())){
                state.result = r.get();
                state.range = new Range(state.range.min(), state.result.t());
            }
            return true;
        } else {
            return false;
        }
    }

    public static class State {
        private @NotNull Range range;
        private HitResult result;

        private State(@NotNull Range range) {
            this.range = Objects.requireNonNull(range);
        }

        private @NotNull Optional<HitResult> getResult() {
            return Optional.ofNullable(result);
        }
    }
}
