package org.sunflow.raytracer.light;

import org.sunflow.image.Color;
import org.sunflow.math.Point3;
import org.sunflow.math.Vector3;
import org.sunflow.raytracer.LightSample;
import org.sunflow.raytracer.LightSource;
import org.sunflow.raytracer.Ray;
import org.sunflow.raytracer.RenderState;

public class PointLight implements LightSource {
    private Point3 lightPoint;
    private Color power;

    public PointLight(Point3 lightPoint, Color power) {
        this.lightPoint = lightPoint;
        this.power = power;
    }

    public boolean isVisible(RenderState state) {
        Point3 p = state.getVertex().p;
        Vector3 n = state.getVertex().n;
        return (Vector3.dot(Point3.sub(lightPoint, p, new Vector3()), n) > 0.0);
    }

    public void getSample(double randX, double randY, RenderState state, LightSample dest) {
        dest.getVertex().p.set(lightPoint);
        Point3.sub(lightPoint, state.getVertex().p, dest.getDirection());
        dest.getDirection().normalize();
        dest.getVertex().n.set(dest.getDirection()).negate();
        dest.setValid(true);
        // prepare shadow ray
        dest.setShadowRay(new Ray(state.getVertex().p, dest.getVertex().p));
        // check to see if the geometric normal is pointing away from the light
        double cosNg = Vector3.dot(state.getGeoNormal(), dest.getDirection());
        if (cosNg < 0.0)
            // potential shadow problem
            // need to fix threshold on ray to avoid clipping
            dest.getShadowRay().setMin(0.3);
        Color.mul(1.0 / (4 * Math.PI * lightPoint.distanceToSquared(state.getVertex().p)), power, dest.getRadiance());
    }

    public void getPhoton(double randX1, double randY1, double randX2, double randY2, Point3 p, Vector3 dir, Color power) {
        p.set(lightPoint);
        double phi = 2.0 * Math.PI * randX1;
        double s = Math.sqrt(randY1 * (1.0 - randY1));
        dir.x = Math.cos(phi) * s;
        dir.y = Math.sin(phi) * s;
        dir.z = 1.0 - (2.0 * randY1);
        power.set(this.power);
    }

    public double getAveragePower() {
        return power.getAverage();
    }
}