package framework.RWT;
import java.awt.AWTEvent;
import java.awt.Canvas;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

import javax.media.j3d.Canvas3D;
import javax.media.j3d.ImageComponent2D;
import javax.media.j3d.View;
import javax.swing.event.MouseInputListener;

import com.sun.j3d.utils.universe.SimpleUniverse;

import framework.model3D.IViewer3D;
import framework.model3D.Universe;
import framework.schedule.ScheduleManager;
import framework.schedule.TaskController;
import framework.view3D.Camera3D;
import framework.view3D.Viewer3D;

/**
 * GUI部品（RWTWidget）を配置可能なCanvas3D、親コンテナに対して相対座標で配置できる
 * @author 新田直也
 *
 */
@SuppressWarnings("serial")
public class RWTCanvas3D extends Canvas3D {
	private ArrayList<RWTWidget> widgetList = new ArrayList<RWTWidget>();
	private float relativeX = 0.0f;
	private float relativeY = 0.0f;
	private float relativeWidth = 0.0f;
	private float relativeHeight = 0.0f;
	private int x = 0;
	private int y = 0;
	private int width = 0;
	private int height = 0;	
	
	private Camera3D camera = null;
	private IViewer3D viewer = null;
	
	private DisplayCanvas displayCanvas = null;
	private BufferedImage image = null;
	private static TaskController renderingController = ScheduleManager.getInstance().registerTask("rendering");

	public RWTCanvas3D() {
		this(SimpleUniverse.getPreferredConfiguration());
	}
	
	public RWTCanvas3D(GraphicsConfiguration gc) {
		this(gc, false);
	}
	
	public RWTCanvas3D(boolean offScreen) {
		this(SimpleUniverse.getPreferredConfiguration(), offScreen);
	}
	
	public RWTCanvas3D(GraphicsConfiguration gc, boolean offScreen) {
		super(gc, offScreen);
		setFocusable(false);
		if (offScreen) {
			displayCanvas = new DisplayCanvas();
		} else {
			enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);			
		}
		renderingController.deactivate();
	}
	
	public Canvas getDisplayCanvas() {
		if (isOffScreen()) {
			return displayCanvas;
		}
		return this;
	}

	public void attachCamera(Camera3D camera) {
		viewer = new Viewer3D(camera);
		View oldView = this.getView();
		if (oldView != null) oldView.removeCanvas3D(this);
		camera.getView().addCanvas3D(this);
		this.camera = camera;
	}
	
	/**
	 * Widgetを配置する。
	 * @param widget
	 */
	public void addWidget(RWTWidget widget) {
		widgetList.add(widget);
	}
	
	/**
	 * RWTCanvas3Dの相対位置を決定する。
	 * @param x x座標値(0.0f〜1.0f)
	 * @param y y座標値(0.0f〜1.0f)
	 */
	public void setRelativePosition(float x, float y) {
		relativeX = x;
		relativeY = y;
	}

	/**
	 * RWTCanvas3Dの相対サイズを決定する。
	 * @param w 幅(0.0f〜1.0f)
	 * @param h 高さ(0.0f〜1.0f)
	 */
	public void setRelativeSize(float w, float h) {
		relativeWidth = w;
		relativeHeight = h;
	}

	/**
	 * 親のコンテナに応じて位置とサイズを合わせる。
	 * @param parent 親コンテナ
	 */
	public void adjust(Container parent) {
		int sx = parent.getWidth();
		int sy = parent.getHeight();
		x = (int) (sx * relativeX);
		y = (int) (sy * relativeY);
		width = (int) (sx * relativeWidth);
		height = (int) (sy * relativeHeight);
		if (isOffScreen()) {
			image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
			ImageComponent2D ic = new ImageComponent2D(ImageComponent2D.FORMAT_RGBA, image, true, false);
			ic.setCapability(ImageComponent2D.ALLOW_IMAGE_READ);
			ic.setCapability(ImageComponent2D.ALLOW_IMAGE_WRITE);
			setOffScreenBuffer(ic);
			displayCanvas.setBounds(x, y, width, height);
			getScreen3D().setSize(width, height);
			getScreen3D().setPhysicalScreenWidth(0.0254/90.0 * (double)width);
			getScreen3D().setPhysicalScreenHeight(0.0254/90.0 * (double)height);
		} else {
			setBounds(x, y, width, height);			
		}
	}
	
	@Override
	public void preRender() {
		if (viewer == null || camera == null) return;
		viewer.setGraphicsContext3D(this.getGraphicsContext3D());
		camera.getUniverse().preRender(viewer);
	}
	
	@Override
	public void renderField(int fieldDesc) {
		if (viewer == null || camera == null) return;
		viewer.setGraphicsContext3D(this.getGraphicsContext3D());
		camera.getUniverse().renderField(viewer);
	}

	@Override
	public void postRender() {
		if (viewer == null || camera == null) return;
		viewer.setGraphicsContext3D(this.getGraphicsContext3D());
		camera.getUniverse().postRender(viewer);
		if (isOffScreen()) {
			//　オフスクリーンバッファ上の画像取得
			ImageComponent2D ic = getOffScreenBuffer();
			image = ic.getImage();
			Graphics src = image.getGraphics();
			//　キャンバス上の　Widget を合成
			drawWidgets(src);
			src.dispose();
			//　ディスプレイバッファに描画
			Graphics dst = displayCanvas.getGraphics();
			dst.drawImage(image, 0, 0, null);
			dst.dispose();
		}
	}
	
	@Override
	public void postSwap() {
		super.postSwap();
		
		if (!isOffScreen()) {
			//　キャンバス上の　Widget の描画
			Graphics g = getGraphics();
			drawWidgets(g);
			g.dispose();
		}
		renderingController.deactivate();
	}
	
	/**
	 * オフスクリーンの場合のみ、レンダリングを明示的にスケジューリングする
	 */
	public void doRender() {
		if (isOffScreen()) {
			if (renderingController.activate()) {
				// 前回のレンダリングが終了していた場合
				renderOffScreenBuffer();
			}
		}
	}

	private void drawWidgets(Graphics g) {
		for(int i = 0; i < widgetList.size(); i++) {
			RWTWidget widget = widgetList.get(i);
			if(widget.isVisible()) {
				widget.adjust(this);
				widget.paint(g);
			}
		}
	}
	
	public void processEvent(AWTEvent e) {
		// イベントをコンテナ側に返す
		Container c = this.getParent();
		if (c != null) c.dispatchEvent(e);
	}
	
	private class DisplayCanvas extends Canvas {
		
		public DisplayCanvas() {
			setFocusable(false);
			enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
		}
		
		public void paint(Graphics g) {
			super.paint(g);
			if (image != null) {
				synchronized (image) {
					g.drawImage(image, 0, 0, null);
				}
			}
		}
		
		public void processEvent(AWTEvent e) {
			// イベントをコンテナ側に返す
			Container c = this.getParent();
			if (c != null) c.dispatchEvent(e);
		}		
	}
}
