import processing.core.*; 
import processing.xml.*; 

import java.applet.*; 
import java.awt.*; 
import java.awt.image.*; 
import java.awt.event.*; 
import java.io.*; 
import java.net.*; 
import java.text.*; 
import java.util.*; 
import java.util.zip.*; 
import java.util.regex.*; 

public class ConnectTheDots4 extends PApplet {


 //import processing.opengl.*;


View view = new View();

ModeManager manager = new ModeManager();
ModeManager preview = new ModeManager();


Shape currentShape;
Vector shapes = new Vector();
int shapeCycle = 0;

public void setup(){
  size(1200, 800, P3D);
  
  noStroke();
  
 // smooth();
  manager.setMode(new DrawMode());
  
  frameRate(30);


}

public void draw(){
  background(155,155,155);
  lights();
  
  view.run();
  
  //pointLight(51, 102, 126, 35, 40, 36);
  manager.mode.run();
  
 // String currentMode = manager.mode.getMode();
  
 
   
  
  Shape sp;
  for (int i = 0; i<shapes.size(); i++){
     sp = (Shape) shapes.get(i);
     
     noStroke(); 
      
     if (preview.mode.getMode() == "wireframe"){
       stroke(255, 255, 255);
       noFill();
     }else{
       fill(sp.fillColor);
     }
     
     // draw red selection stroke 
     if (sp == currentShape){
       stroke(255,0,0);
     }
    
     sp.render();
  }
  
}

public void cycleThroughShapes(){
   if (shapeCycle < 0){
     shapeCycle = shapes.size() - 1; 
   }
   currentShape = (Shape) shapes.get(shapeCycle % shapes.size());
}

public void keyPressed(){
  
  if (key == CODED){
   //
   // -- cycle through shapes for quick selection
   //
   if (manager.mode.getMode() == "select"){
     if (keyCode == RIGHT){
         shapeCycle++;
         cycleThroughShapes();
     } 
     
     if (keyCode == LEFT){
         shapeCycle--;
         cycleThroughShapes();
     } 
   }
  }
  
  
  //
  // -- change camera views
  //
  if (key == '1'){
   view.front(); 
  }else if (key == '2'){
    view.left();
  }else if (key == '3'){
    view.right();
  }
  
  //
  // -- switch through basic modes, draw, move, select, wireframe render, default
  //
  

  if (key == 'd'){
     manager.setMode(new DrawMode());
  }else if (key == 'm'){
     manager.setMode(new MoveMode()); 
  }else if (key == 's'){
     manager.setMode(new SelectMode()); 
  }else if (key == 'w'){
     preview.setMode(new WireframeMode()); 
  }else if (key == 'n'){
     preview.setMode(new DefaultMode());
  }else if (key == 'x' || key == 'y' || key == 'z'){
     manager.setMode(new RotateMode(key));
  }
  
  
  
}

public void mousePressed(){
  manager.mode.pressed();
}

public void mouseReleased(){
  manager.mode.released();
 // println(manager.mode.getMode());
}


class ModeManager {
 
  IMode mode;
  ModeManager(){
    mode = new DefaultMode();
  }
  
  public void setMode(IMode m){
    mode = m;
  }

}

interface IMode {
  public void run();
  public void pressed();
  public void released();
  public String getMode();
}

class DefaultMode implements IMode{
 
   public void pressed(){
   }
   
   public void released(){
   }
   
   public void run(){
     
   }
   
   public String getMode(){
     return "default"; 
   }
   
}

class RotateMode extends DefaultMode implements IMode {
  
  char axis;
  
  float startRot;
  int startMouse;
  
  
  RotateMode(char ax){
     axis = ax;
  }
  
  public void pressed(){
    if (axis == 'x'){
     startRot = currentShape.rotX; 
     startMouse = mouseY;
    }else if (axis == 'y'){
       startRot = currentShape.rotY; 
       startMouse = mouseX;
    }else if (axis == 'z'){
       startRot = currentShape.rotZ; 
       startMouse = mouseX;
    }
  }
  
  public void released(){
    // manager.setMode(new DefaultMode());
  }
  
  public void run(){
    
    if (mousePressed){
      if (axis == 'x'){
       // println((mouseX - startMouse));
        currentShape.rotX = startRot + radians((mouseY - startMouse));
      }else if (axis == 'y'){
        currentShape.rotY =  startRot + radians((mouseX - startMouse));
      }else if (axis == 'z'){
        currentShape.rotZ = startRot + radians((mouseX - startMouse));
      } 
    }
    
   
  }
  

  
  public String getMode(){
    return "rotate";
  }
  
}

class WireframeMode extends DefaultMode implements IMode {
 
 public String getMode(){
   return "wireframe";
 }
 
}

class SelectMode extends DefaultMode implements IMode {
 
 public String getMode(){
   return "select";
 }
 
}


class MoveMode extends DefaultMode implements IMode{
  
  public void released(){
     manager.setMode(new SelectMode());
  }
  
  public void run(){
    currentShape.x = mouseX;
    currentShape.y = mouseY;
  } 
  public String getMode(){
    return "move";
  }
}

class DrawMode extends DefaultMode implements IMode{
  
  DrawMode(){
    
  }
  
  public void pressed(){
    currentShape = new Shape();
    shapeCycle = shapes.size();
    shapes.add(currentShape);
  }
  
  public void released(){
    currentShape.center();
  }
  
  public void run(){
    if (mousePressed){
      if (shapes.size() > 0){
        currentShape.addPoint(mouseX, mouseY);
      }
    }
  }
  
  public String getMode(){
   return "draw"; 
  }
}






class Shape {
 
 Vector points;
 
 int x = 0;
 int y = 0;
 int z = 0;
 int fillColor = 0xFFFFFFFF;
 
 float rotX = 0;
 float rotY = 0;
 float rotZ = 0;
 
 Shape(){
   points = new Vector();
 } 
 
 public void addPoint(int x, int y){
   points.add(new Point(x, y));
 } 
 

 public void resetRotation(){
  rotX = rotY = rotZ = 0; 
 }
 
 public void render(){
   
   pushMatrix();
   translate(x - width / 2, y - height / 2, z);
  
   rotateX(rotX);
   rotateY(rotY);
   rotateZ(rotZ);
   
   beginShape();
    
   for (int i = 0; i<points.size(); i++){
    Point p  = (Point) points.get(i);
    vertex(p.x, p.y);
   }

   endShape(CLOSE);
   popMatrix();
 }
 
 public void center(){
   
   int xp=0;
   int yp=0;
   int pointNum = points.size();
   for (int i = 0; i<pointNum; i++){
    Point p  = (Point) points.get(i);
    xp += p.x;
    yp += p.y;
   }
   
   x = xp / pointNum;
   y = yp / pointNum;
   
   for (int i = 0; i<pointNum; i++){
     Point p  = (Point) points.get(i);
     p.x -= x;
     p.y -= y;
   }
 }
 
}


class Point {
   float x;
   float y;
   Point(int x, int y){
     this.x = x;
     this.y = y;
   }
   Point(){
     x = 0;
     y = 0;
   } 
}
class View{
  
  float rotX=0;
  float rotY=0;
  float rotZ=0;
  
  float rX = 0;
  float rZ = 0;
  float rY = 0;
  
  View(){
  }
  
  public void run(){
    translate(width / 2, height / 2, 0);
    
    rX += (rotX - rX) / 4;
    rY += (rotY - rY) / 4;
    rZ += (rotZ - rZ) / 4;
    
    rotateX(rX);
    rotateY(rY);
    rotateZ(rZ);
  }
  
  public void front(){
   rotX = 0;
   rotY = 0;
   rotZ = 0; 
  }
  
  public void left(){
     rotX = 0;
     rotY = HALF_PI;
     rotZ = 0; 
  }
  
  public void right(){
     rotX = 0;
     rotY = -HALF_PI;
     rotZ = 0; 
  }
  
  public void top(){
     rotX = 0;
     rotY = 0;
     rotZ = 0;
  }
  
  public void bottom(){
    
  }
  
  public void orbit(){
    
  }
  
}



  static public void main(String args[]) {
    PApplet.main(new String[] { "--bgcolor=#ffffff", "ConnectTheDots4" });
  }
}
