
import java.io.*;

import javax.vecmath.*;

import org.bouncycastle.graphics.rt.*;
import org.bouncycastle.graphics.rt.util.*;
import org.bouncycastle.graphics.rt.tools.io.*;
import org.bouncycastle.graphics.rt.shaders.ct.Plastic;

/**
 * a wave based shader
 */
public class WaveShader
    extends Plastic
{
    protected WaveSourceList    waveList = null;
    protected Color3f           base = new Color3f();

    public WaveShader(
        World world)
    {
        super(world);
    }

    public static Shader getInstance(
        World         world,
        RMLReader       in)
        throws IOException
    {
        WaveShader  w = new WaveShader(world);

        w.readObject(in);

        return w;
    }

    protected void processObject(
        Object   obj)
    {
        if (obj instanceof WaveSourceList)
        {
            waveList = (WaveSourceList)obj;
        }
        else
        {
            super.processObject(obj);
        }
    }

    /**
     * the shade routine. If you want more than the below, this is
     * the method to override.
     */
    public void shade(
        Hit        hit,
        Pixel      p)
    {
        p.set(0, 0, 0, 1);

        base.set(hit.color());

        //
        // role in the wave function
        //
        Vector3f            total = new Vector3f(0, 0, 0);
        WaveSourceIterator  it = waveList.iterator();
        
        while (it.hasNext())
        {
            WaveSource  w = it.next();

            total.add(w.perturbationAt(hit.oP()));
        }

        hit.perturbN(total);

        it.dispose();

        p.scaledAdd(base, ambientContribution(world));

        LightDataList   contribs = hit.visibleLights(world);

        p.scaledAdd(base, diffuse, diffuseContribution(contribs, hit));

        if (isSpecular())
        {
            p.scaledAdd(specular, specularContribution(contribs, hit, roughness));
        }

        contribs.dispose();

        if (hit.level() >= maxLevel)
        {
            p.scale(alpha);

            return;
        }

        if (reflectionRoughness != 0)
        {
            hit.perturbN(reflectionRoughness);
        }

        //
        // fire off a reflection ray (if necessary).
        //
        if (isReflecting())
        {
            Ray           ir = hit.reflectedRay();
            Pixel         pix = new Pixel();

            ir.trace(world, pix);

            p.scaledAdd(reflectance, pix);

            ir.dispose();
        }

        p.scale(alpha);
    }
}
