package modeler;

import javax.vecmath.Point3f;
import javax.vecmath.Vector2f;
import javax.vecmath.Vector3f;

import net.java.games.jogl.GL;
import net.java.games.jogl.GLDrawable;

/**
 * @author ags
 */
public class PerspectiveCamera extends Camera {

  float fovy;

  Vector3f vertical;

  public PerspectiveCamera() {

    this(new Point3f(0, 0, 5), new Point3f(0, 0, 0), new Vector3f(0, 1, 0), 0.01f, 1000f, (float) Math.toRadians(45), "Perspective");
  }

  public PerspectiveCamera(float newNear, float newFar, float newFovy, String newName) {

    this(new Point3f(5, 5, 3), new Point3f(0, 0, 0), new Vector3f(0, 1, 0), newNear, newFar, newFovy, newName);
  }

  public PerspectiveCamera(Point3f newEye, Point3f newTarget, Vector3f newUp, float newNear, float newFar, float newFovy, String newName) {

    super(newEye, newTarget, newUp, newNear, newFar, newName);
    fovy = (float) Math.toRadians(newFovy);
    vertical = new Vector3f(0, 1, 0);
    updateFrame();
  }

  public float getFovy() {

    return fovy;
  }

  public void setFovy(float f) {

    fovy = f;
  }

  public void updateFrame() {

    Vector3f gaze = new Vector3f(target);
    gaze.sub(eye);
    gaze.normalize();

    up.normalize();
    right.cross(gaze, vertical);
    right.normalize();

    up.cross(right, gaze);
  }

  public void orbit(Vector2f mouseDelta) {

    Vector3f negGaze = new Vector3f(eye);
    negGaze.sub(target);
    float dist = negGaze.length();
    negGaze.normalize();

    float azimuth = (float) Math.atan2(negGaze.x, negGaze.z);
    float elevation = (float) Math.atan2(negGaze.y, Math.sqrt(negGaze.x * negGaze.x + negGaze.z * negGaze.z));
    azimuth = (azimuth - mouseDelta.x) % (float) (2 * Math.PI);
    elevation = (float) Math.max(-Math.PI * 0.495, Math.min(Math.PI * 0.495, (elevation - mouseDelta.y)));

    negGaze.set((float) (Math.sin(azimuth) * Math.cos(elevation)), (float) Math.sin(elevation), (float) (Math.cos(azimuth) * Math.cos(elevation)));
    negGaze.normalize();

    eye.scaleAdd(dist, negGaze, target);

    updateFrame();
  }

  public void zoom(float d) {

    dolly(-d);
  }

  public void convertMotion(Vector2f delta, Vector3f output) {

    output.scale(-delta.x * aspect, right);
    output.scaleAdd(-delta.y, up, output);
  }

  public void dolly(float d) {

    Vector3f gaze = new Vector3f(target);
    gaze.sub(eye);
    double dist = gaze.length();
    gaze.normalize();
    d *= 6;

    if (dist + d > 0.01)
      eye.scaleAdd(-d, gaze, eye);
  }

  public void track(Vector2f mouseDelta) {

    Vector3f temp = new Vector3f();
    convertMotion(mouseDelta, temp);

    eye.add(temp);
    target.add(temp);
  }

  public void setProjection(GLDrawable d) {

    d.getGL().glMatrixMode(GL.GL_PROJECTION);
    d.getGL().glLoadIdentity();
    d.getGLU().gluPerspective(45, aspect, near, far);
  }
}
