/* WCEGraphics2D.java
   Copyright (C) 2005, 2006 Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */

package gnu.java.awt.peer.wce;

import gnu.java.awt.peer.wce.font.WCEFontPeer;
import gnu.java.awt.peer.wce.font.WCEFontMetrics;
import gnu.java.awt.peer.wce.font.WCEGlyphVector;

import java.awt.AWTError;
import java.awt.AWTPermission;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.SystemColor;
import java.awt.TexturePaint;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.DirectColorModel;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.awt.image.ImagingOpException;
import java.awt.image.MultiPixelPackedSampleModel;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.awt.image.renderable.RenderContext;
import java.awt.image.renderable.RenderableImage;
import java.text.AttributedCharacterIterator;
import java.util.HashMap;
import java.util.Map;

public class WCEGraphics2D extends Graphics2D {

	static {
		initIDs();
	}

	/**
	 * パラメトリック曲線描画用定数値
	 */
	private static final int[][] PARAMETRIC_CURVE_C = {
			{},
			{},
			{ 1, 2, 1 },
			{ 1, 3, 3, 1}
		};
		
	/**
	 * ネイティブ側で管理している情報
	 * コンストラクタと dispose() 以外ではこの値を変更してはいけない。
	 */
	private int nativeGraphics;

	/**
	 * 描画対象となるウインドウハンドル(HWND)
	 */
	private final int windowHandle;

	/**
	 * 描画対象となるPeer。
	 * 対応するPeerが存在しない場合はnull
	 */
	private final WCEComponentPeer peer;

	/**
	 * 描画対象となるオフスクリーンイメージ
	 * オフスクリーンイメージに描画しない場合はnull.
	 */
	private final WCEOffscreenImage offscreenImage;
	
	/**
	 * 描画対象となるBufferedImage
	 * BufferedImageに描画しない場合は null
	 */
	private final BufferedImage bufferedImage;
	
	/**
	 * BufferedImageに対応するDIBSectionのビットマップハンドル
	 * BufferedImageに描画しない場合は０（=NULL）
	 */
	private final int dibSectionForBufferedImage;
	
	/**
	 * オフセット
	 */
	private final Point offset = new Point();

	/**
	 * バックグラウンドカラー
	 */
	private Color bgcolor;
	
	
	/**
	 * 現在のフォント
	 */
	private Font font;
	
	/**
	 * 現在のクリッピング領域
	 */
	private Rectangle clip;
	
	/**
	 * XORモードの場合の指定色
	 * このフィールドがnullの場合は、setPaintMode()が指定されていると見なす
	 */
	private Color xorColor;
	
	/**
	 * setStroke(Stroke) で設定された Stroke
	 */
	private Stroke stroke;
	
	/**
	 * setPaint(Paint) で設定された情報
	 */
	private Paint paint;
	
	/**
	 * The transformation for this Graphics2D instance
	 */
	private AffineTransform currentTransform = new AffineTransform();

	/**
	 * 指定されたpeerのスクリーンデバイスに描画する
	 * インスタンスを作成する
	 */
	public WCEGraphics2D(WCEComponentPeer peer) {
		this.windowHandle   = peer.getWindowHandle();
		this.peer           = peer;
		this.offscreenImage = null;
		this.bufferedImage  = null;
		this.dibSectionForBufferedImage = 0;
		
		this.nativeGraphics = createNativeGraphicsForScreen(this.windowHandle);
		initialize();
	}
	
	/**
	 * 指定されたオフスクリーンイメージ描画用のインスタンスを作成する
	 */
	public WCEGraphics2D(WCEOffscreenImage wceImage) {
		WCEComponentPeer peer = wceImage.getWCEComponentPeer();
		
		this.windowHandle   = peer.getWindowHandle();
		this.peer           = peer;
		this.offscreenImage = wceImage;
		this.bufferedImage  = null;
		this.dibSectionForBufferedImage = 0;
		
		int bitmapHandle = wceImage.getBitmapHandle();
		if (bitmapHandle == 0) {
			throw new IllegalArgumentException("wceImage.getBitmapHandle() returns invalid bitmap handle");
		}
		this.nativeGraphics = createNativeGraphicsForBitmap(this.windowHandle,
															bitmapHandle);
		initialize();
	}

	/**
	 * 指定されたBufferedImage描画用のインスタンスを作成する。
	 */
	public WCEGraphics2D(BufferedImage bufferedImage) {
		this.windowHandle = 0;
		this.peer = null;
		this.offscreenImage = null;
		this.bufferedImage = bufferedImage;
		this.dibSectionForBufferedImage = createDIBSectionFor(bufferedImage);
		
		this.nativeGraphics = createNativeGraphicsForBufferedImage(this.dibSectionForBufferedImage);

		initialize();
	}
	
	/**
	 * ネイティブライブラリ側を初期化する
	 */
	private static native void initIDs();
	
	
	/**
	 * インスタンスを初期化する
	 * このメソッドを呼び出す前に、必ず nativeGraphics を初期化しておく必要がある。
	 */
	private void initialize() {
		assert nativeGraphics != 0 : "nativeGraphics == 0";
		Color foreground, background;
		Font font;

		if (this.peer != null) {
			// peerが存在するときには、peerから情報を取得する
			Component component = this.peer.getComponent();
			foreground = component.getForeground();
			background = component.getBackground();
			font = component.getFont();
		} else {
			foreground = null;
			background = null;
			font = null;
		}
		
		// 初期化処理を行う
		if (foreground == null) {
			foreground = SystemColor.windowText;
		}
		setColor(foreground);

		if (bgcolor == null) {
			bgcolor = SystemColor.window;
		}
		setBackground(bgcolor);
		
		if (font == null) {
			// GNU Classpath 0.18までのComponent.getFont()のデフォルト値を使用する
			font = new Font ("Dialog", Font.PLAIN, 12);
		}
		setFont(font);
	}
	
	/**
	 * 指定されたウインドウハンドルに直接描画するための nativeGraphics を生成する
	 *
	 * @param window
	 */
	private native int createNativeGraphicsForScreen(int windowHandle);

	/**
	 * 指定されたビットマップに描画するための nativeGraphics を生成する
	 *
	 * @param windowHandle	ウインドウハンドル
	 * @param bitmapHandle	ビットマップハンドル（windowHandleと互換性のあるビットマップであることを前提にしている）
	 */
	private native int createNativeGraphicsForBitmap(int windowHandle, int bitmapHandle);

	/**
	 * BufferedImageに描画するための nativeGraphics を生成する。
	 * 描画処理が行われるたびに、ネイティブ側から updateBufferedImage() メソッドが呼び出されることになる。
	 *
	 * @param bitmapHandle	dibSectionのビットマップハンドル。this.bufferedImageと同じ大きさでなければならない。
	 */
	private native int createNativeGraphicsForBufferedImage(int bitmapHandle);
	
	public Graphics create() {
		// コピーを作成する
		WCEGraphics2D g;
		
		if (this.bufferedImage != null) {
			// BufferedImage描画用Graphics
			g = new WCEGraphics2D(this.bufferedImage);
			
		} else if (this.offscreenImage != null) {
			// オフスクリーン描画用Graphics
			g = new WCEGraphics2D(this.offscreenImage);
			
		} else {
			// スクリーン描画用Graphics
			g = new WCEGraphics2D(this.peer);
		}
		
		// 原点をコピーする
		g.offset.setLocation(this.offset);
		
		// クリッピング領域をコピーする
		if (this.clip != null) {
			g.setClip((Rectangle) this.clip.clone());
		} else {
			g.setClip(null);
		}

		g.setPaint(getPaint());
		g.setBackground(getBackground());
		if (this.xorColor != null) {
			g.setXORMode(this.xorColor);
		} else {
			g.setPaintMode();
		}
		
		// カレントフォント
		g.setFont(this.font);
		
		return g;
	}
	
	public Graphics create(int x, int y, int width, int height) {
		WCEGraphics2D g = (WCEGraphics2D) create();
		// 原点とクリップ領域を変更する
		g.translate(x, y);
		g.clipRect(0, 0, width, height);
		return g;
	}
	
	public void translate(int x, int y) {
		this.offset.translate(x, y);
		// クリッピング領域も変更されたことになる。
		if (this.clip != null) {
			this.clip.x -= x;
			this.clip.y -= y;
			setClip(this.clip.x, this.clip.y, this.clip.width, this.clip.height);
		}
	}
	
	/**
	 * ネイティブの原点を設定する
	 */
	private native void translateNative(int nativeGraphics, int x, int y);
	
	public Color getColor() {
		if (this.paint instanceof Color) {
			return (Color) this.paint;
		} else {
			return Color.BLACK;
		}
	}
	
	public void setColor(Color c) {
		if (c == null) {
			// nullが指定された場合はBLACKを設定する
			c = Color.BLACK;
		}
		this.paint = c;
		setNativeColor(this.nativeGraphics, c.getRed(), c.getGreen(), c.getBlue());
	}
	
	private native void setNativeColor(int nativeGraphics, int red, int green, int blue);
	
	public void setPaintMode() {
		this.xorColor = null;
		setNativePaintMode(this.nativeGraphics);
	}
	
	private native void setNativePaintMode(int nativeGraphics);
	
	public void setXORMode(Color c1) {
		this.xorColor = c1;
		setNativeXORMode(this.nativeGraphics, c1.getRed(), c1.getGreen(), c1.getBlue());
	}
	
	private native void setNativeXORMode(int nativeGraphics, int red, int green, int blue);
	
	public Font getFont() {
		return this.font;
	}

	public void setFont(Font font) {
		if (font != null) {
			this.font = font;
			WCEFontPeer peer = (WCEFontPeer) this.font.getPeer();
			int fontHandle = peer.getFontHandle();
			setNativeFont(this.nativeGraphics, fontHandle);
		}
	}
	
	private native void setNativeFont(int nativeGraphics, int fontHandle);
	
	public FontMetrics getFontMetrics(Font f) {
		return new WCEFontMetrics(f);
	}
	
	public Rectangle getClipBounds() {
		if (this.clip == null) {
			return null;
		} else {
			// コピーを返す
			return (Rectangle) this.clip.clone();
		}
	}
	
	public void clipRect(int x, int y, int width, int height) {
		if (this.clip == null) {
			this.clip = new Rectangle(x, y, width, height);
		} else {
			// 現在のクリップ領域と交差させる
			this.clip = this.clip.intersection(new Rectangle(x, y, width, height));
		}
		setNativeClip(this.nativeGraphics,
					  true,
					  this.clip.x + this.offset.x,
					  this.clip.y + this.offset.y,
					  this.clip.width,
					  this.clip.height);
	}
	
	
	public void setClip(int x, int y, int width, int height) {
		this.clip = new Rectangle(x, y, width, height);
		setNativeClip(this.nativeGraphics,
					  true,
					  this.clip.x + this.offset.x,
					  this.clip.y + this.offset.y,
					  this.clip.width,
					  this.clip.height);
	}
	
	public Shape getClip() {
		if (this.clip == null) {
			return null;
		} else {
			return (Shape) this.clip.clone();
		}
	}
	
	public void setClip(Shape clip) {
		if (clip == null) {
			this.clip = null;
			setNativeClip(this.nativeGraphics, false, 0, 0, 0, 0);
			
		} else {
			// 現在の実装ではRectangleのみサポートする
			this.clip = clip.getBounds();
			setNativeClip(this.nativeGraphics,
						  true,
						  this.clip.x + this.offset.x,
						  this.clip.y + this.offset.y,
						  this.clip.width,
						  this.clip.height);
		}
	}
	
	public void copyArea(int x,
                         int y,
                         int width,
                         int height,
                         int dx,
                         int dy) {
		copyNativeArea(this.nativeGraphics,
					   x + this.offset.x,
					   y + this.offset.y,
					   width,
					   height,
					   dx,
					   dy);
	}
	
	/**
	 * 指定された矩形領域をコピーする
	 */
	private native void copyNativeArea(int nativeGraphics,
									   int x,
									   int y,
									   int width,
									   int height,
									   int dx, 
									   int dy);
	/**
	 * ネイティブクリッピング領域を設定する
	 */
	private native void setNativeClip(int nativeGraphics,
									  boolean clipEnabled,
									  int clipx,
									  int clipy,
									  int clipwidth,
									  int clipheight);
	
	/**
	 * このオブジェクトの状態を、ネイティブデバイスコンテキストに反映する
	 */
//	private native void setNativeParameters(int deviceContext,
//											boolean clipEnabled,
//											int clipX,
//											int clipY,
//											int clipWidth,
//											int clipHeight,
//											int rgb,
//											boolean xorMode,
//											int xorRGB,
//											int fontHandle);
	
	public void drawLine(int x1, int y1, int x2, int y2) {
		drawNativeLine(this.nativeGraphics,
					   x1 + offset.x,
					   y1 + offset.y,
					   x2 + offset.x,
					   y2 + offset.y);
	}
	
	/**
	 * デバイスコンテキストを使用して線を引く
	 */
	private native void drawNativeLine(int nativeGraphics,
									   int x1,
									   int y1,
									   int x2,
									   int y2);
	
	public void drawRect(int x, int y, int width, int height) {
		drawNativeRect(this.nativeGraphics,
					   x + offset.x,
					   y + offset.y,
					   width,
					   height);
	}

	private native void drawNativeRect(int nativeGraphics,
									   int x,
									   int y,
									   int width, 
									   int height);
									   
	public void fillRect(int x, int y, int width, int height) {
		fillNativeRect(this.nativeGraphics,
					   x + offset.x,
					   y + offset.y,
					   width,
					   height);
	}
	
	/**
	 * デバイスコンテキストを用いて矩形を塗りつぶす
	 */
	private native void fillNativeRect(int nativeGraphics,
									   int x,
									   int y,
									   int width,
									   int height);
	
	public void clearRect(int x, int y, int width, int height) {
		clearNativeRect(this.nativeGraphics,
						x + this.offset.x,
						y + this.offset.y,
						width,
						height,
						this.bgcolor.getRGB());
	}
	
	private native void clearNativeRect(int nativeGraphics,
									    int x,
									    int y,
									    int width,
									    int height,
									    int rgb);

	public void drawRoundRect(int x,
                              int y,
                              int width,
                              int height,
                              int arcWidth,
                              int arcHeight) {
//		initParameters();
		nativeRoundRect(this.nativeGraphics,
						x + this.offset.x,
						y + this.offset.y,
						width,
						height,
						arcWidth,
						arcHeight,
						false);
	}
	
	public void fillRoundRect(int x,
                              int y,
                              int width,
                              int height,
                              int arcWidth,
                              int arcHeight) {
//		initParameters();
		nativeRoundRect(this.nativeGraphics,
						x + this.offset.x,
						y + this.offset.y,
						width,
						height,
						arcWidth,
						arcHeight,
						true);
	}
	
	/**
	 * 丸コーナー付き矩形を描画する
	 */
	private native void nativeRoundRect(int nativeGraphics,
										int x,
										int y,
										int width,
										int height,
										int arcWidth,
										int arcHeight,
										boolean fill);
	
	public void drawOval(int x,
                         int y,
                         int width,
                         int height) {
//		initParameters();
		nativeOval(this.nativeGraphics,
				   x + this.offset.x,
				   y + this.offset.y,
				   width,
				   height,
				   false);
	}
	
	public void fillOval(int x,
                         int y,
                         int width,
                         int height) {
//		initParameters();
		nativeOval(this.nativeGraphics,
				   x + this.offset.x,
				   y + this.offset.y,
				   width,
				   height,
				   true);
	}
	
	/**
	 * 楕円を描画する
	 */
	private native void nativeOval(int deviceContext,
								   int x,
								   int y,
								   int width,
								   int height,
								   boolean fill);
	
	public void drawArc(int x,
                        int y,
                        int width,
                        int height,
                        int startAngle,
                        int arcAngle) {
		draw(new Arc2D.Float(x, y, width, height, startAngle, arcAngle, Arc2D.OPEN));
//		nativeArc(this.nativeGraphics,
//				  x + this.offset.x,
//				  y + this.offset.y,
//				  width,
//				  height,
//				  startAngle,
//				  arcAngle,
//				  false);
	}
	
	public void fillArc(int x,
                        int y,
                        int width,
                        int height,
                        int startAngle,
                        int arcAngle) {
//		initParameters();
		nativeArc(this.nativeGraphics,
				  x + this.offset.x,
				  y + this.offset.y,
				  width,
				  height,
				  startAngle,
				  arcAngle,
				  true);
	}
	
	private native void nativeArc(int nativeGraphics,
								  int x,
								  int y,
								  int width,
								  int height,
								  int startAngle,
								  int arcAngle,
								  boolean fill);
	
	public void drawPolyline(int[] xPoints,
                             int[] yPoints,
                             int nPoints) {
//		initParameters();

		// オフセットの計算を行う
		int[] xp = new int[xPoints.length];
		int[] yp = new int[yPoints.length];
		for (int i = 0; i < xp.length; ++i) {
			xp[i] = xPoints[i] + this.offset.x;
		}
		for (int i = 0; i < yp.length; ++i) {
			yp[i] = yPoints[i] + this.offset.y;
		}
		drawNativePolyline(this.nativeGraphics,
						   xp,
						   yp,
						   nPoints);
	}
	
	private native void drawNativePolyline(int nativeGraphics,
										   int[] xPoints,
										   int[] yPoints,
										   int nPoints);
	
	public void drawPolygon(int[] xPoints,
                            int[] yPoints,
                            int nPoints) {
//		initParameters();
		// オフセットの計算を行う
		int[] xp = new int[xPoints.length];
		int[] yp = new int[yPoints.length];
		for (int i = 0; i < xp.length; ++i) {
			xp[i] = xPoints[i] + this.offset.x;
		}
		for (int i = 0; i < yp.length; ++i) {
			yp[i] = yPoints[i] + this.offset.y;
		}
		drawNativePolygon(this.nativeGraphics,
						   xp,
						   yp,
						   nPoints);
	}
	
	private native void drawNativePolygon(int nativeGraphics,
										   int[] xPoints,
										   int[] yPoints,
										   int nPoints);
	
	public void fillPolygon(int[] xPoints,
                            int[] yPoints,
                            int nPoints) {
		// オフセットの計算を行う
		int[] xp = new int[xPoints.length];
		int[] yp = new int[yPoints.length];
		for (int i = 0; i < xp.length; ++i) {
			xp[i] = xPoints[i] + this.offset.x;
		}
		for (int i = 0; i < yp.length; ++i) {
			yp[i] = yPoints[i] + this.offset.y;
		}
		fillNativePolygon(this.nativeGraphics,
						  xp,
						  yp,
						  nPoints);
	}
	
	private native void fillNativePolygon(int nativeGraphics,
										  int[] xPoints,
										  int[] yPoints,
										  int nPoints);
	
	public void drawString(String str, int x, int y) {
		drawNativeString(this.nativeGraphics,
						 str,
						 x + offset.x,
						 y + offset.y,
						 xorColor != null);
	}
	
	/**
	 * 文字列を描画する
	 */
	private native void drawNativeString(int nativeGraphics,
								   		 String str,
								   		 int x,
								   		 int y,
								   		 boolean xorMode);
	
	public void drawString(AttributedCharacterIterator iterator,
                           int x,
                           int y) {
		throw new UnsupportedOperationException("Not implemented");
	}
	
	public boolean drawImage(Image img,
                             int x,
                             int y,
                             ImageObserver observer) {
		if (img == null) {
			// 何もせずにリターンする
			return false;
		}
		
		boolean result = false;
		if (img instanceof WCEImage) {
			// イメージの転送処理を行う
			WCEImage wceImage = (WCEImage) img;
			wceImage.addImageObserver(observer);
			int width = wceImage.getWidth();
			int height = wceImage.getHeight();
			int bitmapHandle = wceImage.getBitmapHandle();
			if (bitmapHandle != 0) {
				switch (wceImage.getPreferredBltAPI()) {
					case WCEImage.BIT_BLT:
						nativeBitBlt(this.nativeGraphics,
								 x + this.offset.x,
								 y + this.offset.y,
								 width,
								 height,
								 wceImage.getWindowHandle(),
								 bitmapHandle);
						break;
						
					case WCEImage.TRANSPARENT_BLT:
						nativeTransparentBlt(this.nativeGraphics,
								x + this.offset.x,
								y + this.offset.y,
								width,
								height,
								wceImage.getWindowHandle(),
								bitmapHandle,
								wceImage.getTransparentColor());
						break;
					
					case WCEImage.ALPHA_BLEND:
						// ToDo: 実装する
					default:
						nativeBitBlt(this.nativeGraphics,
								 x + this.offset.x,
								 y + this.offset.y,
								 width,
								 height,
								 wceImage.getWindowHandle(),
								 bitmapHandle);
						break;
				}
				result = true;
			}
		} else if (img instanceof BufferedImage) {
			// 対応するDIBSectionを作る
			BufferedImage bimg = (BufferedImage) img;
			int bitmapHandle = createDIBSectionFor(bimg);
			nativeBitBlt(this.nativeGraphics,
						 x + this.offset.x,
						 y + this.offset.y,
						 bimg.getWidth(),
						 bimg.getHeight(),
						 0,	// ToDo: 適切なHWNDを指定する
						 bitmapHandle);
			// DIBSectionを削除する
			deleteNativeObject(bitmapHandle);
			result = true;
			
		} else {
			// 未サポート
			throw new AWTError("Unsupported image type:" + img);
		}
		return result;
	}
	
	/**
	 * ネイティブデバイスコンテキストに対してビット転送を行う。
	 */
	private native void nativeBitBlt(int nativeGraphics,
									 int destX,
									 int destY,
									 int destWidth,
									 int destHeight,
									 int sourceWindowHandle,
									 int sourceBitmapHandle);
	/**
	 * TransparentBlt()を呼び出す。
	 */
	private native void nativeTransparentBlt(int nativeGraphics,
											 int destX,
											 int destY,
											 int destWidth,
											 int destHeight,
											 int sourceWindowHandle,
											 int sourceBitmapHandle,
											 int transparentColor);

	/**
	 * ネイティブデバイスコンテキストに対してビット転送を行う。
	 */
	private native void nativeStretchBlt(int nativeGraphics,
								 		int destX,
								 		int destY,
								 		int destWidth,
								 		int destHeight,
								 		int sourceWindowHandle,
								 		int sourceBitmapHandle,
								 		int sourceX,
								 		int sourceY,
								 		int sourceWidth,
								 		int sourceHeight);
	
	public boolean drawImage(Image img,
                             int x,
                             int y,
                             int width,
                             int height,
                             ImageObserver observer) {
		if (img == null) {
			// 何もせずにリターンする
			return false;
		}
		
		boolean result = false;
		if (img instanceof WCEImage) {
			// デバイスコンテキストを利用した転送処理を行う
			WCEImage wceImage = (WCEImage) img;
			wceImage.addImageObserver(observer);
			int sourceWidth = wceImage.getWidth();
			int sourceHeight = wceImage.getHeight();
			int sourceBitmapHandle = wceImage.getBitmapHandle();
			if (sourceBitmapHandle != 0) {
				nativeStretchBlt(this.nativeGraphics,
						 x + this.offset.x,
						 y + this.offset.y,
						 width,
						 height,
						 wceImage.getWindowHandle(),
						 sourceBitmapHandle,
						 0,
						 0,
						 sourceWidth,
						 sourceHeight);
				result = true;
			}
		} else if (img instanceof BufferedImage) {
			// 対応するDIBSectionを作る
			BufferedImage bimg = (BufferedImage) img;
			int bitmapHandle = createDIBSectionFor(bimg);
			nativeStretchBlt(this.nativeGraphics,
						 x + this.offset.x,
						 y + this.offset.y,
						 width,
						 height,
						 0,	// ToDo: 適切なHWNDを指定する
						 bitmapHandle,
						 0,
						 0,
						 bimg.getWidth(),
						 bimg.getHeight());
			// DIBSectionを削除する
			deleteNativeObject(bitmapHandle);
			result = true;
			
		} else {
			// WCEImage以外は未サポート
			throw new AWTError("Unsupported image type:" + img);
		}
		return result;
	}
	
	public boolean drawImage(Image img,
                             int x,
                             int y,
                             Color bgcolor,
                             ImageObserver observer) {
		// ToDo: 透明色をサポートする
		return drawImage(img, x, y, observer);
	}
	
	public boolean drawImage(Image img,
                             int x,
                             int y,
                             int width,
                             int height,
                             Color bgcolor,
                             ImageObserver observer) {
		// ToDo: 透明色をサポートする
		return drawImage(img, x, y, width, height, observer);
	}
	
	public boolean drawImage(Image img,
                             int dx1,
                             int dy1,
                             int dx2,
                             int dy2,
                             int sx1,
                             int sy1,
                             int sx2,
                             int sy2,
                             ImageObserver observer) {
		if (img == null) {
			// 何もせずにリターンする
			return false;
		}
		
		boolean result = false;
		if (img instanceof WCEImage) {
			// デバイスコンテキストを利用した転送処理を行う
			WCEImage wceImage = (WCEImage) img;
			wceImage.addImageObserver(observer);
			int sourceWidth = wceImage.getWidth();
			int sourceHeight = wceImage.getHeight();
			int sourceBitmapHandle = wceImage.getBitmapHandle();
			if (sourceBitmapHandle != 0) {
				// デバイスコンテキスト間でデータコピーを行う
				nativeStretchBlt(this.nativeGraphics,
						 dx1 + this.offset.x,
						 dy1 + this.offset.y,
						 dx2 - dx1,
						 dy2 - dy1,
						 wceImage.getWindowHandle(),
						 sourceBitmapHandle,
						 sx1,
						 sy1,
						 sx2 - sx1,
						 sy2 - sy1);
				result = true;
			}
		} else {
			// WCEImage以外は未サポート
			throw new AWTError("Unsupported image type:" + img);
		}
		return result;
	}
	
	public boolean drawImage(Image img,
                             int dx1,
                             int dy1,
                             int dx2,
                             int dy2,
                             int sx1,
                             int sy1,
                             int sx2,
                             int sy2,
                             Color bgcolor,
                             ImageObserver observer) {
		// ToDo: 透明色をサポートする
		return drawImage(img,
						 dx1, dy1, dx2, dy2,
						 sx1, sy1, sx2, sy2,
						 observer);
	}
	
	public synchronized void dispose() {
		if (this.nativeGraphics != 0) {
			disposeNative(this.nativeGraphics);
		}
		this.nativeGraphics = 0;
	}
	
	/**
	 * デバイスコンテキストに関連付けられたオブジェクト（Region, Pen等）を削除
	 * した後、デバイスコンテキストを削除する
	 */
	private native void disposeNative(int nativeGraphics);
	
	
	/**
	 * パラメトリック曲線描画用の B(n, m) を計算する
	 */
	private static void calcB(int n, double t, double[] b) {
		int[] c = PARAMETRIC_CURVE_C[n];
		for (int m = 0; m <= n; ++m) {
			b[m] = c[m] * Math.pow(t, m) * Math.pow((1 - t), (n - m));
		}
	}
	
	/**
	 * ３次パラメトリック曲線を描画する
	 */
	private void drawCubicCurve(double[] currentPoint,
	                            double[] points) {
		double[] nextPoint = new double[2];
		double currentX = currentPoint[0];
		double currentY = currentPoint[1];
		double delta = 0.05; // 適当
		double[] b = new double[4];
		for (double t = 0.0; t <= 1.0; t += delta) {
			calcB(3, t, b);
		    double nextX
		        = b[0] * currentPoint[0]
		        + b[1] * points[0]
		        + b[2] * points[2]
		        + b[3] * points[4];
		    double nextY
		        = b[0] * currentPoint[1]
		        + b[1] * points[1]
		        + b[2] * points[3]
		        + b[3] * points[5];
		    drawLine(
		      (int) currentX,
		      (int) currentY,
		      (int) nextX,
		      (int) nextY);
		    currentX = nextX;
		    currentY = nextY;
		  }
		  // 最後の点をつなぐ
		  drawLine(
		    (int) currentX,
		    (int) currentY,
		    (int) points[4],
		    (int) points[5]);
	}

	/**
	 * ２次パラメトリック曲線を描画する。
	 */
	private void drawQuadCurve(double[] currentPoint,
	                           double[] points) {
		double[] nextPoint = new double[2];
		double currentX = currentPoint[0];
		double currentY = currentPoint[1];
		double delta = 0.05; // 適当
		double[] b = new double[3];
		for (double t = 0.0; t <= 1.0; t += delta) {
			calcB(2, t, b);
			double nextX
			        = b[0] * currentPoint[0]
			        + b[1] * points[0]
			        + b[2] * points[2];
			double nextY
			        = b[0] * currentPoint[1]
			        + b[1] * points[1]
			        + b[2] * points[3];
			drawLine(
			      (int) currentX,
			      (int) currentY,
			      (int) nextX,
			      (int) nextY);
			    currentX = nextX;
			    currentY = nextY;
		}
		// 最後の点をつなぐ
		drawLine(
		    (int) currentX,
		    (int) currentY,
		    (int) points[2],
		    (int) points[3]);
	}

	public void draw(Shape s) {
		if (s instanceof Line2D) {
			Line2D line = (Line2D) s;
			drawLine((int) line.getX1(), (int) line.getY1(),
					 (int) line.getX2(), (int) line.getY2());
		
		} else if (s instanceof Rectangle2D) {
			Rectangle2D rect = (Rectangle2D) s;
			drawRect((int) rect.getMinX(), (int) rect.getMinY(),
					 (int) rect.getWidth(), (int) rect.getHeight());
		
		} else if (s instanceof Ellipse2D.Double) {
			Ellipse2D.Double ellipse = (Ellipse2D.Double) s;
			drawOval((int) ellipse.getX(), (int) ellipse.getY(),
					 (int) ellipse.getWidth(), (int) ellipse.getHeight());
		
		} else if (s instanceof Ellipse2D.Float) {
			Ellipse2D.Float ellipse = (Ellipse2D.Float) s;
			drawOval((int) ellipse.getX(), (int) ellipse.getY(),
					 (int) ellipse.getWidth(), (int) ellipse.getHeight());
		
		} else if (s instanceof RoundRectangle2D.Double) {
			RoundRectangle2D.Double round = (RoundRectangle2D.Double) s;
			drawRoundRect((int) round.getX(), (int) round.getY(),
						  (int) round.getWidth(), (int) round.getHeight(),
						  (int) round.getArcWidth(), (int) round.getArcHeight());
		
		} else if (s instanceof RoundRectangle2D.Float) {
			RoundRectangle2D.Float round = (RoundRectangle2D.Float) s;
			drawRoundRect((int) round.getX(), (int) round.getY(),
						  (int) round.getWidth(), (int) round.getHeight(),
						  (int) round.getArcWidth(), (int) round.getArcHeight());
			
		} else {
			// PathIteratorを用いてパス情報を取得する
		
			PathIterator pi = s.getPathIterator(null);
			double[] points = new double[6];
			double[] start = new double[2];
			double[] current = new double[2];
			while (! pi.isDone()) {
				switch (pi.currentSegment(points)) {
					case PathIterator.SEG_MOVETO:
						start[0] = points[0];
						start[1] = points[1];
						current[0] = start[0];
						current[1] = start[1];
						break;
						
					case PathIterator.SEG_LINETO:
						drawLine((int) current[0],
								   (int) current[1],
								   (int) points[0],
								   (int) points[1]);
						current[0] = points[0];
						current[1] = points[1];
						break;
						
					case PathIterator.SEG_QUADTO:
						drawQuadCurve(current,
									  points);
						current[0] = points[2];
						current[1] = points[3];
						break;
					
					case PathIterator.SEG_CUBICTO:
						drawCubicCurve(current,
									   points);
						current[0] = points[4];
						current[1] = points[5];
						break;
					
					case PathIterator.SEG_CLOSE:
						drawLine((int) current[0],
								 (int) current[1],
								 (int) start[0],
								 (int) start[1]);
						break;
						
					default:
						throw new IllegalStateException();
				}
				pi.next();
			}
		}
	}
	
	public boolean drawImage(Image img,
							 AffineTransform xform,
							 ImageObserver obs) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}

	public void drawImage(BufferedImage img,
	                      BufferedImageOp op,
	                      int x,
	                      int y) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
	
	public void drawRenderedImage(RenderedImage img,
	                              AffineTransform xform) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
	
	public void drawRenderableImage(RenderableImage img,
                                    AffineTransform xform) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
	
	public  void drawString(String s,
                                float x,
                                float y) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
                                
	public  void drawString(AttributedCharacterIterator iterator,
	                                float x,
	                                float y) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
	                                
	public  void drawGlyphVector(GlyphVector g,
	                                     float x,
	                                     float y) {
		if (g instanceof WCEGlyphVector) {
			// ToDo: ちゃんと実装する
			Font font = getFont();
			setFont(g.getFont());
			drawString(((WCEGlyphVector) g).getText(), (int) x, (int) y);
			setFont(font);
			
		} else {
			throw new IllegalArgumentException("Unsupported GlyphVector:" + g);
		}
	}
	                                     
	public void fill(Shape s) {
		if (s instanceof Rectangle2D) {
			// 矩形の描画
			Rectangle r = s.getBounds();
			fillRect(r.x, r.y, r.width, r.height);
		} else if (s instanceof Ellipse2D) {
			Rectangle r = s.getBounds();
			fillOval(r.x, r.y, r.width, r.height);
		} else {
			// ToDo: 実装する
			throw new UnsupportedOperationException("Unsupported shape:" + s);
		}
	}

	public  boolean hit(Rectangle rect,
	                            Shape s,
	                            boolean onStroke) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
	
	public  GraphicsConfiguration getDeviceConfiguration() {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
	
	public  void setComposite(Composite comp) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
	
	public void setPaint(Paint paint) {
		if (paint == null) {
			// nullは無視する
			return;
		}
		
		this.paint = paint;
		String className = paint.getClass().getName();
		
		if (paint instanceof Color) {
			// Colorクラス
			setColor((Color) paint);

		} else if ("java.awt.GradientPaint".equals(className)) {
			// GradientPaintクラスに関してはネイティブ側で処理を行う
			// サブクラスに対して間違った処理をしないように
			// instanceof ではなく String.equals(String) で比較している
			GradientPaint gp = (GradientPaint) paint;
			Point2D p1 = gp.getPoint1();
			Point2D p2 = gp.getPoint2();
			Color c1 = gp.getColor1();
			Color c2 = gp.getColor2();
			setNativeGradientPaint(this.nativeGraphics,
								   (int) p1.getX() + this.offset.x,
								   (int) p1.getY() + this.offset.y,
								   c1.getRed(),
								   c1.getGreen(),
								   c1.getBlue(),
								   (int) p2.getX() + this.offset.x,
								   (int) p2.getY() + this.offset.y,
								   c2.getRed(),
								   c2.getGreen(),
								   c2.getBlue(),
								   gp.isCyclic());
		} else if ("java.awt.TexturePaint".equals(className)) {
			// TexturePaintクラスに関してはネイティブ側で処理を行う
			// サブクラスに対して間違った処理をしないように
			// instanceof ではなく String.equals(String) で比較している
			TexturePaint tp = (TexturePaint) paint;
			BufferedImage image = tp.getImage();
			Rectangle anchor = tp.getAnchorRect().getBounds();
			
			// DIBSectionを作成する
			int bitmapHandle = createDIBSectionFor(image);
			
			// TexturePaintを設定する
			setNativeTexturePaint(this.nativeGraphics,
								  bitmapHandle,
			                      anchor.x,
			                      anchor.y,
			                      anchor.width,
			                      anchor.height);
		} else {
			
			// その他のクラス
			throw new UnsupportedOperationException("Unsupported paint type:" + paint);
		}
	}
	
	/**
	 * 指定されたBufferedImageの内容とデータ転送を行うことを目的とした
	 * DIBSectionを作成し、そのハンドルを返す。
	 * 転送を高速に行うことができるように、できるだけ同じフォーマットを持つDIBSection
	 * を作成する。
	 */
	private int createDIBSectionFor(BufferedImage image) {
		ColorModel cm = image.getColorModel();
		SampleModel sm = image.getSampleModel();
		DataBuffer db = image.getRaster().getDataBuffer();
		final int width = image.getWidth();
		final int height = image.getHeight();
		int type = image.getType();
		int bitCount = 0;
		int result = 0;
		
		switch (type) {
		case BufferedImage.TYPE_INT_RGB:
		case BufferedImage.TYPE_INT_ARGB:
			{
				if (db instanceof DataBufferInt) {
					// 32ビットのDIBSectionを作成する
					bitCount = 32;
				}
			}
			break;
		
		case BufferedImage.TYPE_USHORT_565_RGB:
		case BufferedImage.TYPE_USHORT_555_RGB:
			{
				if (db instanceof DataBufferUShort) {
					// 16ビットのDIBSectionを作成する
					bitCount = 16;
				}
			}
			break;
		}
		
		if (bitCount == 0) {
			// 32ビットのDIBSectionを作成する
			bitCount = 32;
			type = BufferedImage.TYPE_INT_RGB;
		}
		result = createNativeDIBSection(width, height, bitCount, type);

		// BufferedImageの内容でDIBSectionを初期化する
		if (result != 0) {
			copyImage(image, result);
		}
		
		return result;
	}
	
	/**
	 * ネイティブDIBSectionを作成し、そのビットマップハンドルを返す
	 */
	private native int createNativeDIBSection(int width, int height, int bitCount, int type);
	
	/**
	 * ネイティブAPI DeleteObject(int)を呼び出す。
	 */
	private native void deleteNativeObject(int gdiObjectHandle);
	
	/**
	 * 指定されたBufferedImageの内容を返すピクセルに
	 * 直接アクセス可能なプリミティブ配列を返す
	 * 直接アクセスできない場合は null を返す。
	 */
	private Object getPixelsOf(BufferedImage image) {
		DataBuffer db = image.getRaster().getDataBuffer();
		Object result = null;
		
		switch (image.getType()) {
		case BufferedImage.TYPE_INT_RGB:
		case BufferedImage.TYPE_INT_ARGB:
			if (db instanceof DataBufferInt) {
				result = ((DataBufferInt) db).getData();
			}
			break;
		
		case BufferedImage.TYPE_USHORT_565_RGB:
		case BufferedImage.TYPE_USHORT_555_RGB:
			if (db instanceof DataBufferUShort) {
				result = ((DataBufferUShort) db).getData();
			}
			break;
		}
		
		return result;
	}

	/**
	 * 指定されたBufferedImageの内容をDIBSectionに設定する
	 */
	private void copyImage(BufferedImage image, int bitmapHandle) {
		Object pixels = getPixelsOf(image);
		if (pixels == null) {
			// コピーされたデータを得る
			pixels = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
		}
		
		if (pixels instanceof int[]) {
			writeNativeDIBSectionInt(bitmapHandle, (int[]) pixels, image.getType());
		} else if (pixels instanceof short[]) {
			writeNativeDIBSectionUShort(bitmapHandle, (short[]) pixels, image.getType());
		} else {
			// エラー
			throw new AWTError("Invalid pixel type:" + pixels.getClass().getName());
		}
		
	}
	
	/**
	 * 描画対象であるBufferedImageの内容を、DIBSectionの内容を用いて更新する。
	 * このメソッドは、ネイティブ実装側から呼び出される。
	 *
	 */
	void updateBufferedImage() {
		int bitmapHandle = this.dibSectionForBufferedImage;
		BufferedImage image = this.bufferedImage;
		boolean copied;
		
		Object pixels = getPixelsOf(image);
		if (pixels == null) {
			// 直接イメージデータにアクセスできない場合は、コピーを入手する
			pixels = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());
			copied = true;
		} else {
			copied = false;
		}
		
		if (pixels instanceof int[]) {
			readNativeDIBSectionInt(bitmapHandle, (int[]) pixels, image.getType());
		} else if (pixels instanceof short[]) {
			readNativeDIBSectionUShort(bitmapHandle, (short[]) pixels, image.getType());
		} else {
			throw new AWTError("Invalid pixel type:" + pixels.getClass().getName());
		}
		
		if (copied) {
			// データをコピーしていた場合は、BufferedImageに書き戻す
			image.setRGB(0, 0, image.getWidth(), image.getHeight(), (int[]) pixels, 0, image.getWidth());
		}
	}
		
	
	/**
	 * 指定されたピクセルを DIBSection に書き込む。（int[]型）
	 *
	 */
	private native void writeNativeDIBSectionInt(int bitmapHandle, int[] pixels, int type);
	
	/**
	 * 指定されたピクセルを DIBSection に書き込む。（short[]型）
	 *
	 */
	private native void writeNativeDIBSectionUShort(int bitmapHandle, short[] pixels, int type);
	
	/**
	 * DIBSection の全データを読み込む。（int[]型）
	 *
	 */
	private native void readNativeDIBSectionInt(int bitmapHandle, int[] pixels, int type);
	
	/**
	 * DIBSection の全データを読み込む。（short[]型）
	 *
	 */
	private native void readNativeDIBSectionUShort(int bitmapHandle, short[] pixels, int type);

	/**
	 * GradientPaintを設定する
	 */
	private native void setNativeGradientPaint(int nativeGraphics,
											   int x1,
											   int y1,
											   int red1,
											   int green1,
											   int blue1,
											   int x2,
											   int y2,
											   int re2,
											   int green2,
											   int blue2,
											   boolean cyclic);
	
	/**
	 * TexturePaintを設定する
	 */
	private native void setNativeTexturePaint(int nativeGraphics,
											int bitmapHandle,
											int anchorX,
											int anchorY, 
											int anchorWidth,
											int anchorHeight);

											   
	public  void setStroke(Stroke s) {
		if (s == null) {
			return;
		}
		if (s.getClass() == BasicStroke.class) {
			BasicStroke bs = (BasicStroke) s;
			setNativeBasicStroke(this.nativeGraphics,
								 bs.getLineWidth(),
								 bs.getEndCap(),
								 bs.getLineJoin(),
								 bs.getMiterLimit(),
								 bs.getDashArray(),
								 bs.getDashPhase());
		} else {
			// ToDo: 実装する
			throw new UnsupportedOperationException("Not a BasicStroke");
		}
		
		this.stroke = s;
	}
	
	private native void setNativeBasicStroke(int nativeGraphics,
											 float lineWidth,
											 int endCap,
											 int lineJoin,
											 float miterLimit,
											 float[] dashArray,
											 float dashPhase);
	
	public  void setRenderingHint(RenderingHints.Key hintKey,
	                                      Object hintValue) {
		// ToDo: 実装する
		// 現在の実装では無視
	}
	
	public  Object getRenderingHint(RenderingHints.Key hintKey) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
	
	public  void setRenderingHints(Map hints) {
		// ToDo: 実装する
		// 現在の実装では無視
	}
	
	public  void addRenderingHints(Map hints) {
		// ToDo: 実装する
		// 現在の実装では無視
	}
	
	public  RenderingHints getRenderingHints() {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}

	public  void translate(double tx,
	                               double ty) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}

	public  void rotate(double theta) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}

	public  void rotate(double theta,
	                            double x,
	                            double y) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
	
	public  void scale(double sx,
	                           double sy) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}

	public  void shear(double shx,
	                           double shy) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}

	public  void transform(AffineTransform Tx) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
	
	public  void setTransform(AffineTransform Tx) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
	
	public  AffineTransform getTransform() {
		if (this.currentTransform != null) {
			return (AffineTransform) this.currentTransform.clone();
		} else {
			return new AffineTransform();
		}
	}
	
	public  Paint getPaint() {
		return this.paint;
	}
	
	public  Composite getComposite() {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
	
	public  void setBackground(Color color) {
		this.bgcolor = color;
	}
	
	public  Color getBackground() {
		return this.bgcolor;
	}
	
	public  Stroke getStroke() {
		return this.stroke;
	}
	
	public  void clip(Shape s) {
		// ToDo: 実装する
		throw new UnsupportedOperationException("Not implemented");
	}
	
	public FontRenderContext getFontRenderContext() {
		return new FontRenderContext(this.currentTransform, false, true);
	}
}
