package graphics.tracer;

import java.awt.*;
import java.util.Vector;

public class Scene {

  private Vector targetList = new Vector();
  private Light light = new Light();
  private Eye eye = new Eye();

  private Dimension screenSize;

  public static Sphere makeSphere(
      double x, double y, double z, double r) {
    return
        Sphere.makeSphere(x, y, z, r);
  }

  public Target getTargetAt(int i) {
    return (Target) targetList.elementAt(i);
  }

  public int getNumberOfTargets() {
    return targetList.size();
  }

  public Vec3f getLight() {
    return light.getPosition();
  }

  public Vec3f getEye() {
    return eye.getPosition();
  }

  /**
    renderABand - renders lines y0 through
    y1, returning the result.
  */

  public int[] render() {
    int h = screenSize.height;
    int w = screenSize.width;
    int hh = 2 * h;
    int pixels[] = new int[w * h];
    int line[] = new int[w];
    for (short pixY = 0; pixY < h; pixY++) {
      line = renderALine(pixY);
      for (int x = 0; x < line.length; x++)
        pixels[x + pixY * w] = line[x];
      //System.arraycopy(line,0,pixels,pixY*w,w-1);
    }
    return pixels;
  }
//This is the part that
//the rmi server executes...
//VC Note!!
  public int[] renderALine(
      short pixY) {
    int w = screenSize.width;
    Vec3f pp = new Vec3f(0, 0, 0);
    int line[] = new int[w];
    for (int pixX = 0; pixX < w; pixX++) {
      getProjPlane(pixX, pixY, pp);
      int c = trace(pp);
      line[pixX] = (255 << 24) | (c << 16) | (c << 8) | (c << 0);
    }
    return line;
  }

  public static void main(String args[]) {
    Scene scene = new Scene(new Dimension(64, 64));
    scene.render();
  }

  private void getProjPlane(int x, int y, Vec3f pp) {
    int h = screenSize.height;
    int w = screenSize.width;
    float ystep = 2.0f / h;
    float xstep = 2.0f / w;
    pp.x = -1 + xstep * x;
    pp.y = 1 - ystep * y;
  }

  public int[] render1() {
    int w = screenSize.width;
    int h = screenSize.height;
    int pixY = 0;
    int pixX = 0;
    float ystep = 1.0f / h;
    float xstep = 1.0f / w;
    int ww = 2 * w;
    int hh = 2 * h;
    Vec3f projPlane
        = new Vec3f(-1, 1, 0);

    int pixels[] = new int[w * h];
    int c = 0;
    int xi = 0;
    int yi = 0;
    for (pixY = 0; pixY < hh; pixY++) {
      projPlane.x = -1;
      projPlane.y -= ystep;

      for (pixX = 0; pixX < ww; pixX++) {
        projPlane.x += xstep;
        c = trace(projPlane);
        xi = pixX / 2;
        yi = pixY / 2;

        pixels[xi + w * yi] = (255 << 24) | (c << 16) | (c << 8) | (c << 0);
      }
    }
    return pixels;
  }

  public void preview() {
    ip.hak.Double4 d4[] = new ip.hak.Double4[targetList.size()];
    for (int i = 0; i < targetList.size(); i++) {
      Sphere st = (Sphere) targetList.elementAt(i);
      Vec3f v = st.getCenter();
      d4[i] = new ip.hak.Double4(v.x, v.y, v.z, st.getRadius());
    }

    ip.hak.Preview pv = new ip.hak.Preview(d4);
  }

  public Scene(Dimension _screenSize) {
    // set up the lights and pinhole

    screenSize = _screenSize;
    // Build targetList by hand for now. We should eventually let the
    // users build their objects interactively.
    targetList.addElement(makeSphere(0.05, -0.3, 0.0, 0.3));
    targetList.addElement(makeSphere(0.05, 0.06, 0.0, 0.24));
    targetList.addElement(makeSphere(0.05, 0.19, -0.205, 0.01));
    targetList.addElement(makeSphere(0.05, 0.09, -0.237, 0.01));
    targetList.addElement(makeSphere(0.05, 0.0, -0.230, 0.01));
    targetList.addElement(makeSphere(0.05, 0.32, -0.205, 0.018));
    targetList.addElement(makeSphere(0.05, 0.34, 0.0, 0.2));
    targetList.addElement(makeSphere(0.1, 0.4, -0.195, 0.02));
    targetList.addElement(makeSphere(-0.02, 0.393, -0.185, 0.02));

    targetList.addElement(makeSphere(0.0, 0.0, 10, 10));

    for (int i = 0; i < targetList.size(); i++)
      if (!(
          (Target) targetList.elementAt(i)).initScene())
        ((Target) targetList.elementAt(i)).initScene(this);
  }

  int trace(Vec3f projP) {
    float t[] = new float[1];
    int color = 0;
    Vec3f R1 = new Vec3f(projP);
    R1.sub(eye.getPosition());
    R1.normalize();

    t[0] = 0;
    int obj = intersectObjects(
        eye.getPosition(), R1, t, 0, false);

    /* if it does then find out how to paint the pixel */
    Target tgt = getTargetAt(obj);
    if (t[0] > 0)
      color = tgt.shade(obj, R1, t);

    color += 16;  // ambient light

    if (color > 255)
      color = 255;

    return color;
  }

  int intersectObjects(
      Vec3f R0, Vec3f R1,
      float result[], int object,
      boolean shadowCheck) {
    float minDist = 0, dist;
    int hit = -1;

    for (int i = 0; i < targetList.size(); i++) {
      if ((shadowCheck == true) && (object == i))
        continue;
      dist =
          getTargetAt(i).intersectTest(R0, R1, i);

      if (dist == 0)
        continue;

      /* save the first t */
      if ((minDist == 0) && (dist > 0)) {
        minDist = dist;
        hit = i;
      } else if ((dist > 0.0) && (dist < minDist)) {
        minDist = dist;
        hit = i;
      }
    }
    result[0] = minDist;
    return hit;
  }
}