import java.awt.Dimension;
import java.awt.Frame;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.GLCanvas;

import com.sun.opengl.util.Animator;
import com.sun.opengl.util.GLUT;

public class Robot implements GLEventListener {
    private int WIDTH = 200;
    private int HEIGHT = 200;
    private float CAMERA = -10.0f;

    // ライト
    private float[] LPOSITION = { -10.0f, 10.0f, 10.0f, 0.0f };
    private float[] LSPECULAR = { 1.0f, 0.5f, 0.5f, 1.0f };
    private float[] LDIFFUSE = { 1.0f, 0.5f, 0.5f, 1.0f };
    private float[] LAMBIENT = { 0.8f, 0.8f, 0.8f, 1.0f };

    // 物体の反射率
    private float[] MSPECULAR = { 0.5f, 0.5f, 0.8f, 1.0f };
    private float[] MDIFFUSE = { 0.5f, 0.5f, 0.8f, 1.0f };
    private float[] MAMBIENT = { 0.2f, 0.2f, 0.4f, 1.0f };
    private float MSHININESS = 10.0f;

    // 単位
    private float UNIT = 1.0f;

    // 胴体のサイズ
    private float BODY_WIDTH = 1.0f;
    private float BODY_HEIGHT = 1.0f;
    private float BODY_DEPTH = 0.5f;

    // 腕のサイズと位置
    private float ARM_WIDTH = 0.2f;
    private float ARM_HEIGHT = 1.2f;
    private float ARM_DEPTH = 0.2f;
    private float ARM_X = 0.65f;
    private float ARM_Y = 0.5f;

    // 足のサイズと位置
    private float LEG_WIDTH = 0.2f;
    private float LEG_HEIGHT = 1.0f;
    private float LEG_DEPTH = 0.2f;
    private float LEG_X = 0.3f;
    private float LEG_Y = -0.55f;

    // 頭のサイズと位置
    private float HEAD_RADIUS = 0.4f;
    private int HEAD_SLICES = 20;
    private int HEAD_STACKS = 20;
    private float HEAD_Y = 0.95f;

    private GL gl;
    private GLUT glut;
    private Animator animator;
    
    private int prevMouseX;
    private int prevMouseY;
    private float angleX = 0.0f;
    private float angleY = 0.0f;
    private float distanceX = 0.0f;
    private float distanceY = 0.0f;


    public Robot() {
        Frame frame = new Frame("Robot");

        // 3Dを描画するコンポーネント
        GLCanvas canvas = new GLCanvas();
        canvas.addGLEventListener(this);

        frame.add(canvas);
        frame.setSize(WIDTH, HEIGHT);

        canvas.addMouseListener(new MouseAdapter() {
                public void mousePressed(MouseEvent e) {
                    prevMouseX = e.getX();
                    prevMouseY = e.getY();
                }
            });

        canvas.addMouseMotionListener(new MouseMotionAdapter() {
                public void mouseDragged(MouseEvent e) {
                    int x = e.getX();
                    int y = e.getY();

                    if (e.isShiftDown()) {
                        // 移動量の算出
                        float diffX = (float)(x - prevMouseX)/10.0f;
                        float diffY = (float)(prevMouseY - y)/10.0f;
		    
                        // 移動量の更新
                        distanceX += diffX;
                        distanceY += diffY;
                    } else {	    
                        Dimension size = e.getComponent().getSize();

                        // 回転量の算出
                        // ウィンドウの端から端までで、360度回転するようにする
                        float thetaY = 360.0f * ((float)(x-prevMouseX)/size.width);
                        float thetaX = 360.0f * ((float)(prevMouseY-y)/size.height);
		    
                        // 角度の更新
                        angleX -= thetaX;
                        angleY += thetaY;
                    }

                    // 現在のマウスの位置を保存
                    prevMouseX = x;
                    prevMouseY = y;
                }
            });

        animator = new Animator(canvas);

        frame.addWindowListener(new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    animator.stop();
                    System.exit(0);
                }
            });
        frame.setVisible(true);
        animator.start();
    }

    public void init(GLAutoDrawable drawable) {
        gl = drawable.getGL();
        glut = new GLUT();

        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

        gl.glEnable(GL.GL_DEPTH_TEST);
        gl.glEnable(GL.GL_CULL_FACE);

        gl.glEnable(GL.GL_LIGHTING);
        gl.glEnable(GL.GL_LIGHT0);

        gl.glEnable(GL.GL_NORMALIZE);

        gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, LPOSITION, 0);
        gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, LSPECULAR, 0);
        gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, LDIFFUSE, 0);
        gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, LAMBIENT, 0);

        gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, MSPECULAR, 0);
        gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, MDIFFUSE, 0);
        gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, MAMBIENT, 0);
        gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, MSHININESS);
    }

    public void reshape(GLAutoDrawable drawable,
                        int x, int y,
                        int width, int height) {
        float ratio = (float)height / (float)width;
        
        gl.glViewport(0, 0, width, height);

        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        gl.glFrustum(-1.0f, 1.0f, -ratio, ratio,
                     5.0f, 40.0f);

        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
        gl.glTranslatef(0.0f, 0.0f, CAMERA);
    }

    public void display(GLAutoDrawable drawable) {
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);

        gl.glPushMatrix();

        // マウスの移動量に応じて移動
        gl.glTranslatef(distanceX, distanceY, 0.0f);

        // マウスの移動量に応じて回転
        gl.glRotatef(angleX, 1.0f, 0.0f, 0.0f);
        gl.glRotatef(angleY, 0.0f, 1.0f, 0.0f);
 
        drawRobot();
 
        gl.glPopMatrix();
    }

    public void displayChanged(GLAutoDrawable drawable,
                               boolean modeChanged,
                               boolean deviceChanged) {}
 
    private void drawRobot() {

        // 胴体
        gl.glPushMatrix();
        gl.glScalef(BODY_WIDTH, BODY_HEIGHT, BODY_DEPTH);
        glut.glutSolidCube(UNIT);
        gl.glPopMatrix();

        // 頭
        gl.glPushMatrix();
        gl.glTranslatef(0.0f, HEAD_Y, 0.0f);
        glut.glutSolidSphere(HEAD_RADIUS, HEAD_SLICES, HEAD_STACKS);
        gl.glPopMatrix();

        // 左手
        drawLimb(ARM_X, ARM_Y, 
                 ARM_WIDTH, ARM_HEIGHT, ARM_DEPTH);

        // 右手
        drawLimb(-ARM_X, ARM_Y,
                 ARM_WIDTH, ARM_HEIGHT, ARM_DEPTH);

        // 左足
        drawLimb(LEG_X, LEG_Y, 
                 LEG_WIDTH, LEG_HEIGHT, LEG_DEPTH);

        // 右足
        drawLimb(-LEG_X, LEG_Y, 
                 LEG_WIDTH, LEG_HEIGHT, LEG_DEPTH);
    }

    private void drawLimb(float x, float y,
                          float width, float height, float depth) {
        gl.glPushMatrix();
        gl.glTranslatef(x, y, 0.0f);
        drawPart(width, height, depth);
        gl.glPopMatrix();
    }

    private void drawPart(float width, float height, float depth) {
        gl.glPushMatrix();
        gl.glTranslatef(0.0f, -height/2.0f, 0.0f);
        gl.glScalef(width, height, depth);
        glut.glutSolidCube(UNIT);
        gl.glPopMatrix();
    }

    public static void main(String[] args) {
        new Robot();
    }
}

