package org.sunflow.core.shader;

import org.sunflow.SunflowAPI;
import org.sunflow.core.ParameterList;
import org.sunflow.core.Ray;
import org.sunflow.core.Shader;
import org.sunflow.core.ShadingState;
import org.sunflow.image.Color;
import org.sunflow.math.Vector3;

public class MirrorShader implements Shader {
    private Color color;

    public MirrorShader() {
        color = Color.WHITE;
    }

    public boolean update(ParameterList pl, SunflowAPI api) {
        color = pl.getColor("color", color);
        return true;
    }

    public Color getRadiance(ShadingState state) {
        if (!state.includeSpecular())
            return Color.BLACK;
        state.faceforward();
        float cos = state.getCosND();
        float dn = 2 * cos;
        Vector3 refDir = new Vector3();
        refDir.x = (dn * state.getNormal().x) + state.getRay().getDirection().x;
        refDir.y = (dn * state.getNormal().y) + state.getRay().getDirection().y;
        refDir.z = (dn * state.getNormal().z) + state.getRay().getDirection().z;
        Ray refRay = new Ray(state.getPoint(), refDir);

        // compute Fresnel term
        cos = 1 - cos;
        float cos2 = cos * cos;
        float cos5 = cos2 * cos2 * cos;
        Color ret = Color.white();
        ret.sub(color);
        ret.mul(cos5);
        ret.add(color);
        return ret.mul(state.traceReflection(refRay, 0));
    }

    public void scatterPhoton(ShadingState state, Color power) {
        float avg = color.getAverage();
        double rnd = state.getRandom(0, 0, 1);
        if (rnd >= avg)
            return;
        state.faceforward();
        float cos = state.getCosND();
        power.mul(color).mul(1.0f / avg);
        // photon is reflected
        float dn = 2 * cos;
        Vector3 dir = new Vector3();
        dir.x = (dn * state.getNormal().x) + state.getRay().getDirection().x;
        dir.y = (dn * state.getNormal().y) + state.getRay().getDirection().y;
        dir.z = (dn * state.getNormal().z) + state.getRay().getDirection().z;
        state.traceReflectionPhoton(new Ray(state.getPoint(), dir), power);
    }
}