/* 
 * PROJECT: NyARToolkit Java3D utilities.
 * --------------------------------------------------------------------------------
 * The MIT License
 * Copyright (c) 2008 nyatla
 * airmail(at)ebony.plala.or.jp
 * http://nyatla.jp/nyartoolkit/
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 */
package jp.nyatla.nyartoolkit.java3d.utils;

import java.util.Enumeration;

import javax.media.Buffer;
import javax.media.j3d.*;
import javax.vecmath.*;

import jp.nyatla.nyartoolkit.jmf.utils.*;
import jp.nyatla.nyartoolkit.core.*;
import jp.nyatla.nyartoolkit.core.param.NyARParam;
import jp.nyatla.nyartoolkit.core.transmat.NyARTransMatResult;
import jp.nyatla.nyartoolkit.detector.*;
import jp.nyatla.nyartoolkit.core.types.*;

/**
 * NyARToolkitと連動したBehaviorを返却するクラスです。
 * 提供できるBehaviorは、BackgroundとTransformgroupです。
 *
 */
public class NyARSingleMarkerBehaviorHolder implements JmfCaptureListener
{
	private NyARParam _cparam;

	private JmfCaptureDevice _capture;

	private J3dNyARRaster_RGB _nya_raster;//最大3スレッドで共有されるので、排他制御かけること。

	private NyARSingleDetectMarker _nya;

	//Behaviorホルダ
	private NyARBehavior _nya_behavior;

	public NyARSingleMarkerBehaviorHolder(NyARParam i_cparam, float i_rate, NyARCode i_ar_code, double i_marker_width) throws NyARException
	{
		this._nya_behavior = null;
		final NyARIntSize scr_size = i_cparam.getScreenSize();
		this._cparam = i_cparam;
		//キャプチャの準備
		JmfCaptureDeviceList devlist=new JmfCaptureDeviceList();
		this._capture=devlist.getDevice(0);
		this._capture.setCaptureFormat(scr_size.w, scr_size.h,15f);
		this._capture.setOnCapture(this);		
		this._nya_raster = new J3dNyARRaster_RGB(this._cparam,this._capture.getCaptureFormat());
		this._nya =NyARSingleDetectMarker.createInstance(this._cparam, i_ar_code, i_marker_width);
		this._nya_behavior = new NyARBehavior(this._nya, this._nya_raster, i_rate);
	}

	public Behavior getBehavior()
	{
		return this._nya_behavior;
	}

	/**
	 * i_back_groundにキャプチャ画像を転送するようにBehaviorを設定します。
	 * i_back_groungはALLOW_IMAGE_WRITE属性を持つものである必要があります。
	 * @param i_back_groung
	 * @return
	 */
	public void setBackGround(Background i_back_ground)
	{
		//コール先で排他制御
		this._nya_behavior.setRelatedBackGround(i_back_ground);
	}

	/**
	 * i_trgroupの座標系をマーカーにあわせるようにBehaviorを設定します。
	 *
	 */
	public void setTransformGroup(TransformGroup i_trgroup)
	{
		//コール先で排他制御
		this._nya_behavior.setRelatedTransformGroup(i_trgroup);
	}

	/**
	 * 座標系再計算後に呼び出されるリスナです。
	 * @param i_listener
	 */
	public void setUpdateListener(NyARSingleMarkerBehaviorListener i_listener)
	{
		//コール先で排他制御
		this._nya_behavior.setUpdateListener(i_listener);
	}

	/**
	 * ラスタを更新 コールバック関数だから呼んじゃらめえ
	 */
	public void onUpdateBuffer(Buffer i_buffer)
	{
		try {
			synchronized (this._nya_raster) {
				this._nya_raster.setBuffer(i_buffer);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void start() throws NyARException
	{
		//開始
		this._capture.start();
	}

	public void stop()
	{
		this._capture.stop();
	}
}

class NyARBehavior extends Behavior
{
	private NyARTransMatResult trans_mat_result = new NyARTransMatResult();

	private NyARSingleDetectMarker related_nya;

	private TransformGroup trgroup;

	private Background back_ground;

	private J3dNyARRaster_RGB raster;

	private WakeupCondition wakeup;

	private NyARSingleMarkerBehaviorListener listener;

	public void initialize()
	{
		wakeupOn(wakeup);
	}

	/**
	 * i_related_ic2dの内容で定期的にi_back_groundを更新するBehavior
	 * @param i_back_ground
	 * @param i_related_ic2d
	 */
	public NyARBehavior(NyARSingleDetectMarker i_related_nya, J3dNyARRaster_RGB i_related_raster, float i_rate)
	{
		super();
		wakeup = new WakeupOnElapsedTime((int) (1000 / i_rate));
		related_nya = i_related_nya;
		trgroup = null;
		raster = i_related_raster;
		back_ground = null;
		listener = null;
		this.setSchedulingBounds(new BoundingSphere(new Point3d(), 100.0));
	}

	public void setRelatedBackGround(Background i_back_ground)
	{
		synchronized (raster) {
			back_ground = i_back_ground;
		}
	}

	public void setRelatedTransformGroup(TransformGroup i_trgroup)
	{
		synchronized (raster) {
			trgroup = i_trgroup;
		}
	}

	public void setUpdateListener(NyARSingleMarkerBehaviorListener i_listener)
	{
		synchronized (raster) {
			listener = i_listener;
		}
	}

	/**
	 * いわゆるイベントハンドラ
	 */
	public void processStimulus(Enumeration criteria)
	{
		try {
			synchronized (raster) {
				Transform3D t3d = null;
				boolean is_marker_exist = false;
				if (back_ground != null) {
					raster.renewImageComponent2D();/*DirectXモードのときの対策*/
					back_ground.setImage(raster.getImageComponent2D());
				}
				if (raster.hasBuffer()) {
					is_marker_exist = related_nya.detectMarkerLite(raster, 100);
					if (is_marker_exist)
					{
						final NyARTransMatResult src = this.trans_mat_result;
						related_nya.getTransmationMatrix(src);
//						Matrix4d matrix = new Matrix4d(src.m00, -src.m10, -src.m20, 0, -src.m01, src.m11, src.m21, 0, -src.m02, src.m12, src.m22, 0, -src.m03, src.m13, src.m23, 1);
/*						Matrix4d matrix = new Matrix4d(
								 src.m00, src.m01, src.m02, src.m03,
								-src.m10, -src.m11, -src.m12, -src.m13,
								-src.m20, -src.m21, -src.m22, -src.m23,
								0,0,0, 1.0);*/
/*						Matrix4d matrix2 = new Matrix4d(
						-src.m00, -src.m01, -src.m02, -src.m03,
						-src.m10, -src.m11, -src.m12, -src.m13,
						 src.m20,  src.m21,  src.m22,  src.m23,
					           0,        0,        0,        1);
						*/
						Matrix4d matrix = new Matrix4d(
								-src.m00, -src.m10, src.m20, 0,
								-src.m01, -src.m11, src.m21, 0,
								-src.m02, -src.m12, src.m22, 0,
							    -src.m03,-src.m13, src.m23, 1);
						matrix.transpose();
						t3d = new Transform3D(matrix);
						if (trgroup != null) {
							trgroup.setTransform(t3d);
						}
					}
				}
				if (listener != null) {
					listener.onUpdate(is_marker_exist, t3d);
				}
			}
			wakeupOn(wakeup);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
