/**
 * Similer program wiht simpleLite of ARToolkit
 * (c)2008 nyatla.jp
 * airmail(at)ebony.plala.or.jp
 * http://nyatla.jp/
 */
package jp.nyatla.nyartoolkit.jogl.sample;

import java.awt.Component;
import java.awt.GridLayout;

import javax.media.Buffer;
import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLCanvas;
import javax.media.opengl.GLEventListener;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.WindowConstants;

import jp.ac.kyutech.ai.ylab.shiva.nyatools.utils.DeviceDetectionUtil;
import jp.ac.kyutech.ai.ylab.shiva.utils.nativecapable.NativeCapableModule;
import jp.nyatla.nyartoolkit.core.NyARCode;
import jp.nyatla.nyartoolkit.jmf.utils.JmfCameraCapture;
import jp.nyatla.nyartoolkit.jmf.utils.JmfCaptureListener;
import jp.nyatla.nyartoolkit.jogl.utils.GLNyARParam;
import jp.nyatla.nyartoolkit.jogl.utils.GLNyARRaster_RGB;
import jp.nyatla.nyartoolkit.jogl.utils.GLNyARSingleDetectMarker;
import jp.nyatla.nyartoolkit.jogl.utils.NyARGLUtil;

import com.sun.opengl.util.Animator;

public class JavaSimpleLiteModule implements GLEventListener,
		JmfCaptureListener, NativeCapableModule {
	private final String CARCODE_FILE = "patt.hiro";
	private final String PARAM_FILE = "camera_para.dat";

	private final static int SCREEN_X = 320;
	private final static int SCREEN_Y = 240;
	private Animator animator;
	private GLNyARRaster_RGB cap_image;

	private JmfCameraCapture capture;
	private GL gl;
	private NyARGLUtil glnya;

	// NyARToolkit関�?
	private GLNyARSingleDetectMarker nya;
	private GLNyARParam ar_param;

	private GLCanvas canvas;

	/**
	 * write cube
	 * 
	 */
	private void drawCube() {
		// Colour cube data.
		int polyList = 0;
		float fSize = 0.5f;
		int f, i;
		float[][] cube_vertices = new float[][] { { 1.0f, 1.0f, 1.0f },
				{ 1.0f, -1.0f, 1.0f }, { -1.0f, -1.0f, 1.0f },
				{ -1.0f, 1.0f, 1.0f }, { 1.0f, 1.0f, -1.0f },
				{ 1.0f, -1.0f, -1.0f }, { -1.0f, -1.0f, -1.0f },
				{ -1.0f, 1.0f, -1.0f } };
		float[][] cube_vertex_colors = new float[][] { { 1.0f, 1.0f, 1.0f },
				{ 1.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f },
				{ 0.0f, 1.0f, 1.0f }, { 1.0f, 0.0f, 1.0f },
				{ 1.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f },
				{ 0.0f, 0.0f, 1.0f } };
		int cube_num_faces = 6;
		short[][] cube_faces = new short[][] { { 3, 2, 1, 0 }, { 2, 3, 7, 6 },
				{ 0, 1, 5, 4 }, { 3, 0, 4, 7 }, { 1, 2, 6, 5 }, { 4, 5, 6, 7 } };

		if (polyList == 0) {
			polyList = gl.glGenLists(1);
			gl.glNewList(polyList, GL.GL_COMPILE);
			gl.glBegin(GL.GL_QUADS);
			for (f = 0; f < cube_num_faces; f++)
				for (i = 0; i < 4; i++) {
					gl.glColor3f(cube_vertex_colors[cube_faces[f][i]][0],
							cube_vertex_colors[cube_faces[f][i]][1],
							cube_vertex_colors[cube_faces[f][i]][2]);
					gl.glVertex3f(cube_vertices[cube_faces[f][i]][0] * fSize,
							cube_vertices[cube_faces[f][i]][1] * fSize,
							cube_vertices[cube_faces[f][i]][2] * fSize);
				}
			gl.glEnd();
			gl.glColor3f(0.0f, 0.0f, 0.0f);
			for (f = 0; f < cube_num_faces; f++) {
				gl.glBegin(GL.GL_LINE_LOOP);
				for (i = 0; i < 4; i++)
					gl.glVertex3f(cube_vertices[cube_faces[f][i]][0] * fSize,
							cube_vertices[cube_faces[f][i]][1] * fSize,
							cube_vertices[cube_faces[f][i]][2] * fSize);
				gl.glEnd();
			}
			gl.glEndList();
		}

		gl.glPushMatrix(); // Save world coordinate system.
		gl.glTranslatef(0.0f, 0.0f, 0.5f); // Place base of cube on marker
		// surface.
		gl.glRotatef(0.0f, 0.0f, 0.0f, 1.0f); // Rotate about z axis.
		gl.glDisable(GL.GL_LIGHTING); // Just use colors.
		gl.glCallList(polyList); // Draw the cube.
		gl.glPopMatrix(); // Restore world coordinate system.

	}

	public void init(GLAutoDrawable drawable) {
		gl = drawable.getGL();
		gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		System.out.println("init");
		// Preparing NyARToolkit
		try {
			// Preparing for capture
			capture = new JmfCameraCapture(320, 240, 15f,
					JmfCameraCapture.PIXEL_FORMAT_RGB);
			capture.setCaptureListener(this);
			// Preparing NyARToolkit
			ar_param = new GLNyARParam();
			NyARCode ar_code = new NyARCode(16, 16);
			ar_param.loadFromARFile(getClass().getResourceAsStream(PARAM_FILE));
			ar_param.changeSize(SCREEN_X, SCREEN_Y);
			nya = new GLNyARSingleDetectMarker(ar_param, ar_code, 80.0);
			ar_code
					.loadFromARFile(getClass()
							.getResourceAsStream(CARCODE_FILE));
			// Utility class for NyARToolkit
			glnya = new NyARGLUtil(gl, ar_param);
			// RGB raster object for GL
			cap_image = new GLNyARRaster_RGB(gl, ar_param);
			// start capturing
			capture.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
		animator = new Animator(drawable);
		animator.start();

	}

	public void reshape(GLAutoDrawable drawable, int x, int y, int width,
			int height) {
		gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
		gl.glViewport(0, 0, width, height);

		// set projection
		gl.glMatrixMode(GL.GL_PROJECTION);
		gl.glLoadIdentity();
		// set viewing position
		gl.glMatrixMode(GL.GL_MODELVIEW);
		gl.glLoadIdentity();
	}

	public void display(GLAutoDrawable drawable) {

		try {
			if (!cap_image.hasData()) {
				return;
			}
			gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // Clear
			// the
			// buffers
			// for
			// new
			// frame.
			boolean is_marker_exist;
			synchronized (cap_image) {
				is_marker_exist = nya.detectMarkerLite(cap_image, 100);
				// write background
				glnya.drawBackGround(cap_image, 1.0);
			}
			// write cube if marker is detected
			if (is_marker_exist) {
				// when checking detection confidence, call nya.getConfidence()
				// Projection transformation.
				gl.glMatrixMode(GL.GL_PROJECTION);
				gl.glLoadMatrixd(ar_param.getCameraFrustumRH(), 0);
				gl.glMatrixMode(GL.GL_MODELVIEW);
				// Viewing transformation.
				gl.glLoadIdentity();
				gl.glLoadMatrixd(nya.getCameraViewRH(), 0);

				// All other lighting and geometry goes here.
				drawCube();
			}
			Thread.sleep(1);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void onUpdateBuffer(Buffer i_buffer) {
		try {
			synchronized (cap_image) {
				cap_image.setBuffer(i_buffer, true);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
			boolean deviceChanged) {
	}

	@Override
	public Component getRootComponent() {
		return canvas;
	}

	@Override
	public boolean initModule() {

		// component to drawing 3D
		canvas = new GLCanvas();
		canvas.addGLEventListener(this);

		return true;

	}

	@Override
	public boolean startModule() {

		SecurityManager sm = DeviceDetectionUtil.disableSecurityManager();

		// copy jmf.properties
		DeviceDetectionUtil.copyJMFProperties();

		if (!DeviceDetectionUtil.isDeviceAvailable(320, 240, 30f,
				JmfCameraCapture.PIXEL_FORMAT_RGB)) {

			JFrame dialog = new JFrame();
			dialog.setResizable(false);
			dialog.setAlwaysOnTop(true);
			dialog.setTitle("Device Detection");
			dialog.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

			dialog.setLayout(new GridLayout(3, 1));

			dialog.add(new JLabel(" Detecting capturing devices... "));
			JProgressBar b = new JProgressBar();
			b.setIndeterminate(true);
			dialog.add(b);
			dialog.add(new JLabel(" (this will take a couple of minutes) "));
			dialog.pack();

			dialog.setVisible(true);
			DeviceDetectionUtil.detect();
			dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
			dialog.setVisible(false);
		}

		if (!DeviceDetectionUtil.isDeviceAvailable(320, 240, 30f,
				JmfCameraCapture.PIXEL_FORMAT_RGB)) {
			JOptionPane.showMessageDialog(JOptionPane.getRootFrame(),
					"Couldn't find any capturing device.",
					"No Capturing Device Error", JOptionPane.ERROR_MESSAGE);
			return false;
		}

		DeviceDetectionUtil.enableSecurityManamger(sm);
		return true;
	}

	@Override
	public boolean destroyModule() {
		if (capture != null) {
			capture.stop();
			if (animator != null) {
				animator.stop();
				return true;
			}
		}
		return false;
	}
}
