package jp.nyatla.nyartoolkit.jogl.utils;

import java.awt.image.BufferedImage;

import javax.media.opengl.GL;

import jp.nyatla.nyartoolkit.core.NyARException;
import jp.nyatla.nyartoolkit.core.param.NyARFrustum;
import jp.nyatla.nyartoolkit.core.raster.INyARGrayscaleRaster;
import jp.nyatla.nyartoolkit.core.raster.rgb.INyARRgbRaster;
import jp.nyatla.nyartoolkit.core.raster.rgb.NyARRgbRaster;
import jp.nyatla.nyartoolkit.core.types.NyARDoublePoint2d;
import jp.nyatla.nyartoolkit.core.types.NyARIntPoint2d;
import jp.nyatla.nyartoolkit.jogl.utils.NyARGLDrawUtil;
import jp.nyatla.nyartoolkit.markersystem.NyARMarkerSystem;
import jp.nyatla.nyartoolkit.utils.j2se.NyARBufferedImageRaster;

/**
 * このクラスは、{@link NyARGlmarkerSystem}クラスの出力する値を、OpenGL関数へショートカットする関数を定義します。
 * {@link MarkerSystem}からの通知を受け取ります。
 */
public class NyARGlRender implements NyARMarkerSystem.IObserver
{
	private NyARMarkerSystem _ms;
	private int[] __wk=new int[1];
	private final int getGlMatrixMode(GL i_gl)
	{
		i_gl.glGetIntegerv(GL.GL_MATRIX_MODE,this.__wk,0);
		return this.__wk[0];
	}
	/**
	 * コンストラクタです。マーカシステムに対応したレンダラを構築します。
	 * @param i_ms
	 */
	public NyARGlRender(NyARMarkerSystem i_ms)
	{
		this._ms=i_ms;
		this._ms.addObserver(this);		
		syncMarkerSystem();
	}
	/**
	 * {@link NyARMarkerSystem}からの通知を受け取ります。
	 */
	public void notify(Object i_ms,int i_id)
	{
		if(i_ms instanceof NyARMarkerSystem){
			if(i_id==NyARMarkerSystem.EV_UPDATE){
				syncMarkerSystem();
			}
		}
	}
	private void syncMarkerSystem()
	{
		NyARFrustum.FrustumParam f=this._ms.getFrustum().getFrustumParam(new NyARFrustum.FrustumParam());
		NyARGLUtil.toCameraFrustumRH(this._ms.getARParam(),1,f.near,f.far,this._projection_mat);
	}
	
	/**
	 * OpenGLスタイルカメラパラメータのワーク変数
	 */
	private double[] _projection_mat=new double[16];
	
	
	/**
	 * i_glにAR向けのprojectionMatrixを、PROJECTIONスタックへロードします。
	 * @param i_gl
	 */
	public void loadARProjectionMatrix(GL i_gl)
	{
		int old_mode=this.getGlMatrixMode(i_gl);
		i_gl.glMatrixMode(GL.GL_PROJECTION);
		i_gl.glLoadMatrixd(this._projection_mat, 0);
		i_gl.glMatrixMode(GL.GL_MODELVIEW);
		i_gl.glLoadIdentity();
		i_gl.glMatrixMode(old_mode);
	}
	public void loadScreenProjectionMatrix(GL i_gl,int i_width,int i_height)
	{
		int old_mode=this.getGlMatrixMode(i_gl);
		i_gl.glMatrixMode(GL.GL_PROJECTION);
		i_gl.glLoadIdentity();
		i_gl.glOrtho(0.0,i_width,i_height,0,-1,1);
		i_gl.glMatrixMode(GL.GL_MODELVIEW);
		i_gl.glLoadIdentity();
		i_gl.glMatrixMode(old_mode);
		return;		
	}
	/**
	 * OpenGLスタイルカメラパラメータのワーク変数
	 */
	private double[] _mv_mat=new double[16];
	
	/**
	 * i_glに、i_matをロードします。
	 * @param i_gl
	 * @param i_id
	 * @throws NyARException 
	 */
	public void loadMarkerMatrix(GL i_gl,int i_id) throws NyARException
	{
		int old_mode=this.getGlMatrixMode(i_gl);
		if(old_mode!=GL.GL_MODELVIEW){
			i_gl.glMatrixMode(GL.GL_MODELVIEW);
			NyARGLUtil.toCameraViewRH(this._ms.getMarkerMatrix(i_id),1,this._mv_mat);			
			i_gl.glLoadMatrixd(this._mv_mat, 0);
			i_gl.glMatrixMode(old_mode);
		}else{
			NyARGLUtil.toCameraViewRH(this._ms.getMarkerMatrix(i_id),1,this._mv_mat);
			i_gl.glLoadMatrixd(this._mv_mat, 0);
		}
	}
	//
	// Graphics toolkit
	//
	public void drawBackground(GL i_gl,INyARRgbRaster i_bg_image) throws NyARException
	{
		i_gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // Clear the buffers for new frame.
		NyARGLDrawUtil.drawBackGround(i_gl,i_bg_image, 1.0);
	}
	public void drawBackground(GL i_gl,INyARGrayscaleRaster i_bg_image) throws NyARException
	{
		i_gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // Clear the buffers for new frame.
		NyARGLDrawUtil.drawBackGround(i_gl,i_bg_image, 1.0);
	}
	
	/**
	 * 指定位置にカラーキューブを書き込みます。
	 * @param i_gl
	 * @param i_size_per_mm
	 * @param i_x
	 * @param i_y
	 * @param i_z
	 */
	public void colorCube(GL i_gl,float i_size_per_mm,double i_x,double i_y,double i_z)
	{
		int old_mode=this.getGlMatrixMode(i_gl);
		i_gl.glMatrixMode(GL.GL_MODELVIEW);
		i_gl.glPushMatrix();
		i_gl.glTranslated(i_x,i_y,i_z);
		NyARGLDrawUtil.drawColorCube(i_gl,i_size_per_mm);
		i_gl.glPopMatrix();
		i_gl.glMatrixMode(old_mode);
	}
	
	public void setColor(GL i_gl,float r,float g,float b)
	{
		i_gl.glColor3f(r,g,b);
	}
	public void setStrokeWeight(GL i_gl,float i_width)
	{
		i_gl.glLineWidth(i_width);
	}
	/**
	 * この関数は、現在のカラーで
	 * @param i_gl
	 * @param i_x
	 * @param i_y
	 * @param i_x2
	 * @param i_y2
	 */
	public void line(GL i_gl,float i_x,double i_y,double i_x2,double i_y2)
	{
		int old_mode=this.getGlMatrixMode(i_gl);
		i_gl.glMatrixMode(GL.GL_MODELVIEW);
		i_gl.glBegin(GL.GL_LINE);
		
		i_gl.glVertex2d(i_x,i_y);
		i_gl.glEnd();
		i_gl.glMatrixMode(old_mode);
	}
	public void polygon(GL i_gl,NyARDoublePoint2d[] i_vertex)
	{
		int old_mode=this.getGlMatrixMode(i_gl);
		i_gl.glMatrixMode(GL.GL_MODELVIEW);
		i_gl.glBegin(GL.GL_LINE_LOOP);
		for(int i=0;i<i_vertex.length;i++)
		{
			i_gl.glVertex2d(i_vertex[i].x,i_vertex[i].y);
		}
		i_gl.glEnd();
		i_gl.glMatrixMode(old_mode);
	}
	public void polygon(GL i_gl,NyARIntPoint2d[] i_vertex)
	{
		int old_mode=this.getGlMatrixMode(i_gl);
		i_gl.glMatrixMode(GL.GL_MODELVIEW);
		i_gl.glBegin(GL.GL_LINE_LOOP);
		for(int i=0;i<i_vertex.length;i++)
		{
			i_gl.glVertex2d(i_vertex[i].x,i_vertex[i].y);
		}
		i_gl.glEnd();
		i_gl.glMatrixMode(old_mode);
	}
	/**
	 * ラスタを現在の表面に描画します。
	 * @param i_gl
	 * @param i_x
	 * @param i_y
	 * @param i_raster
	 * @throws NyARException
	 */
	public void drawImage2d(GL i_gl,double i_x, double i_y, NyARRgbRaster i_raster) throws NyARException
	{
		i_gl.glPushMatrix();
		try{
			i_gl.glMatrixMode(GL.GL_MODELVIEW);
			i_gl.glLoadIdentity();
			i_gl.glTranslated(i_x,i_y,0);
			NyARGLDrawUtil.drawRaster(i_gl, i_raster);
		}finally{
			i_gl.glPopMatrix();
		}
	}
	/**
	 * {@link #drawImage2d(GL, double, double, NyARRgbRaster)}のラッパーです。引数はこの関数を参照してください。
	 * 
	 * @param i_gl
	 * @param i_x
	 * @param i_y
	 * @param i_bitmap
	 * @throws NyARException
	 */
	
	public void drawImage2d(GL i_gl,double i_x, double i_y, BufferedImage i_bitmap) throws NyARException
	{
		this.drawImage2d(i_gl, i_x, i_y,new NyARBufferedImageRaster(i_bitmap));
	}


	
}