/* WCEToolkit.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.classpath.Configuration;
import gnu.java.awt.ClasspathToolkit;
import gnu.java.awt.EmbeddedWindow;
import gnu.java.awt.peer.ClasspathFontPeer;
import gnu.java.awt.peer.EmbeddedWindowPeer;
import gnu.java.awt.peer.wce.font.WCEFontPeer;
import gnu.java.awt.peer.wce.font.WCEFontMetrics;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.*;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.im.*;
import java.awt.image.*;
import java.awt.peer.*;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.AttributedString;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;

public class WCEToolkit extends ClasspathToolkit implements Runnable {
	/**
	 * タイトルバーフラグの定数
	 * @see	#getFloatingFlags()
	 */
	public static final int FRAME_IS_FLOATING		= 1;
	public static final int DIALOG_IS_FLOATING	= 2;

	private static int titleBarFlags;
	
	static {
		if (Configuration.INIT_LOAD_LIBRARY) {
			System.loadLibrary("wcepeer");
		}
		
		// タイトルバーフラグを設定する
		StringTokenizer st
			= new StringTokenizer(
				System.getProperties().getProperty("gnu.java.awt.peer.wce.floating", ""),
				",");
		while (st.hasMoreTokens()) {
			String value = st.nextToken().trim();
			if ("frame".equalsIgnoreCase(value)) {
				titleBarFlags |= FRAME_IS_FLOATING;
			} else if ("dialog".equalsIgnoreCase(value)) {
				titleBarFlags |= DIALOG_IS_FLOATING;
			}
		}
	}
	
	
	/**
	 * システムイベントキュー
	 */
	static final EventQueue queue = new EventQueue();
	
	/**
	 * 初期化完了フラグ。ネイティブ実装側から更新する
	 */
	private static boolean initialized;
	
	/**
	 * システムカラーキャッシュ
	 */
	int[] systemColorsCache;
	

	private class LRUCache extends LinkedHashMap {
		int max_entries;
		public LRUCache(int max) {
			super(max, 0.75f, true);
			max_entries = max;
		}
		protected boolean removeEldestEntry(Map.Entry eldest) {
			return size() > max_entries;
		}
	}

	private LRUCache imageCache = new LRUCache(50);

	private static native void init();
	
	public WCEToolkit() {
		init();
		// OSメッセージ受信処理を開始する
		new Thread(this).start();
		synchronized (WCEToolkit.class) {
			while (! initialized) {
				// 初期化が完了するのを待つ
				try {
					WCEToolkit.class.wait();
				} catch (InterruptedException ie) {
					ie.printStackTrace();
				}
			}
		}
	}

	/**
	 * ビープ音を発生させる
	 */
	public native void beep();

	public int checkImage (Image image, int width, int height, ImageObserver observer) {
		if (image instanceof WCEImage) {
			// ToDo: width と height を考慮する
			return ((WCEImage) image).checkImage(observer);
		} else if (image instanceof BufferedImage) {
			// BufferedImageは常に描画可能
			return ImageObserver.ALLBITS | ImageObserver.HEIGHT | ImageObserver.WIDTH;
		} else {
			throw new AWTError("Unsupported image:" + image);
		}
	}

	public Image getImage (String filename) {
		try {
			File file = new File(filename);
			return getImage(file.toURL());
		} catch (MalformedURLException muex) {
			throw (IllegalArgumentException) new IllegalArgumentException(filename).initCause(muex);
		}
	}

	public Image getImage (URL url) {
		if (imageCache.containsKey(url)) {
			return (Image) imageCache.get(url);
		} else {
			Image im = createImage(url);
		    imageCache.put(url, im);
			return im;
		}
	}

	public Image createImage(String path) {
		try {
			return createImage(new File(path).toURL());
		} catch (MalformedURLException muex) {
			throw (IllegalArgumentException) new IllegalArgumentException(path).initCause(muex);
		}
	}

	public Image createImage (URL url) {
		return new WCEURLImage(url);
	}
	
	public Image createImage (ImageProducer producer) {
		if (producer instanceof WCEImageProducer) {
			// 元になるイメージ本体を返す
			return ((WCEImageProducer) producer).getImage();
		} else {
			// WCEMemoryImageのインスタンスを作成する
			WCEMemoryImage image = new WCEMemoryImage(producer);
			return image;
		}
	}
	
	public Image createImage (byte[] imagedata, int imageoffset, int imagelength) {
		// テンポラリファイルに書き出して、WCEURLImage()クラスとして渡す
		// ToDo:メモリ上のデータをそのまま処理できるようにする
		WCEURLImage image = null;
		File file = null;
		try {
			file = File.createTempFile("AWT", null);
			
			OutputStream os = new BufferedOutputStream(new FileOutputStream(file));
			os.write(imagedata, imageoffset, imagelength);
			os.close();
			
			image = new WCEURLImage(file.toURL());
			
			// イメージのロード完了を待つ
			MediaTracker mt = new MediaTracker(new Component() {});
			mt.addImage(image, 0);
			mt.waitForAll();

		} catch (IOException ioe) {
			ioe.printStackTrace();
		} catch (InterruptedException ie) {
			ie.printStackTrace();
		} finally {
			if (file != null) {
				file.delete();
			}
		}
		return image;
	}
	
	public ImageProducer createImageProducer(URL url) {
		throw new UnsupportedOperationException("Not implemented");
	}
  
	public ColorModel getColorModel () {
		return ColorModel.getRGBdefault ();
	}

	public String[] getFontList () {
	    return (new String[] { "Dialog", 
				   "DialogInput", 
				   "Monospaced", 
				   "Serif", 
				   "SansSerif" });
	}

	public FontMetrics getFontMetrics (Font font)  {
		return new WCEFontMetrics(font);
	}
	
	public PrintJob getPrintJob (Frame frame, String jobtitle, Properties props) {
		throw new UnsupportedOperationException("Not implemented");
	}
	
	/**
	 * 画面の解像度（DPI)を返す。
	 */
	public native int getScreenResolution();
	
	/**
	 * 画面のサイズをピクセル単位で返す
	 */
	public Dimension getScreenSize () {
		Rectangle bounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().getBounds();
		return new Dimension(bounds.width, bounds.height);
	}
	
	/**
	 * システムクリップボードを返す
	 */
	public Clipboard getSystemClipboard() {
		return new WCEClipboard();
	}

	public boolean prepareImage (Image image, int width, int height, ImageObserver observer) {
		if (image instanceof WCEImage) {
			// ToDo: width と height を考慮する
			WCEImage wimg = (WCEImage) image;
			boolean result = (wimg.getFlags() & ImageObserver.ALLBITS) != 0;
			if (! result) {
				// ビットマップハンドルの取得は、イメージ生成処理を引き起こす
				wimg.getBitmapHandle();
			}
			return result;
		} else if (image instanceof BufferedImage) {
			// BufferedImageは常に描画可能
			return true;
			
		} else {
			// その他のイメージの場合はエラー
			throw new AWTError("Unsupported image:" + image);
		}
	}
	
 	public void sync () {
	}

	protected ButtonPeer createButton (Button b) {
		return new WCEButtonPeer(b);
	}

	protected CanvasPeer createCanvas (Canvas c) {
		return new WCECanvasPeer(c);
	}
	
	protected CheckboxPeer createCheckbox (Checkbox cb) {
		return new WCECheckboxPeer(cb);
	}
	
	protected CheckboxMenuItemPeer createCheckboxMenuItem (CheckboxMenuItem cmi) {
		return new WCECheckboxMenuItemPeer(cmi);
	}
 	
 	protected ChoicePeer createChoice (Choice c) {
		return new WCEChoicePeer(c);
	}

	protected DialogPeer createDialog (Dialog d) {
		return new WCEDialogPeer(d);
	}

	protected FileDialogPeer createFileDialog (FileDialog fd) {
		return new WCEFileDialogPeer(fd);
	}

	protected FramePeer createFrame (Frame f) {
		return new WCEFramePeer(f);
	}

	protected LabelPeer createLabel (Label label) {
		return new WCELabelPeer(label);
	}

	protected ListPeer createList (List list) {
		return new WCEListPeer(list);
	}

	protected MenuPeer createMenu (Menu m) {
		return new WCEMenuPeer(m);
	}

	protected MenuBarPeer createMenuBar (MenuBar mb) {
		return new WCEMenuBarPeer(mb);
	}

	protected MenuItemPeer createMenuItem (MenuItem mi) {
		return new WCEMenuItemPeer(mi);
	}
	
	protected PanelPeer createPanel (Panel p) {
		return new WCEPanelPeer(p);
	}

	protected PopupMenuPeer createPopupMenu (PopupMenu target) {
		return new WCEPopupMenuPeer(target);
	}

	protected ScrollPanePeer createScrollPane (ScrollPane sp) {
		return new WCEScrollPanePeer(sp);
	}

	protected ScrollbarPeer createScrollbar (Scrollbar sb) {
		return new WCEScrollbarPeer(sb);
	}

	protected TextAreaPeer createTextArea (TextArea ta) {
		return new WCETextAreaPeer(ta);
	}

	protected TextFieldPeer createTextField (TextField tf) {
		return new WCETextFieldPeer(tf);
	}

	protected WindowPeer createWindow (Window w) {
		return new WCEWindowPeer(w);
	}

  /** 
   * @deprecated part of the older "logical font" system in earlier AWT
   * implementations. Our newer Font class uses getClasspathFontPeer.
   */
	protected FontPeer getFontPeer (String name, int style) {
		return new WCEFontPeer(name, style, 1);
	}

	public ClasspathFontPeer getClasspathFontPeer (String name, Map attrs) {
		return new WCEFontPeer(name, attrs);
	}

	protected EventQueue getSystemEventQueueImpl() {
		return queue;
	}
	
	private native void loadNativeSystemColors(int[] systemColors);
	
	protected void loadSystemColors (int[] systemColors) {
		if (this.systemColorsCache != systemColors) {
			// 呼び出し元でint[]の内容を変更しないことを前提にしている
			// (GNU ClasspathのSystemColor実装が変更された時点で
			//  この実装も見直す必要がある）
			loadNativeSystemColors(systemColors);
			this.systemColorsCache = systemColors;
		}
	}

	public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent e) {
		throw new UnsupportedOperationException("Not implemented");
	}

	public Map mapInputMethodHighlight(InputMethodHighlight highlight) {
		throw new UnsupportedOperationException("Not implemented");
	}

  // ClasspathToolkit methods

	public GraphicsEnvironment getLocalGraphicsEnvironment() {
		return new WCEGraphicsEnvironment();
	}
	
	public Font createFont(int format, InputStream stream) {
		throw new UnsupportedOperationException();
	}

	public RobotPeer createRobot (GraphicsDevice screen) throws AWTException {
		throw new UnsupportedOperationException("Not implemented");
	}
	
	/**
	 * Windowsメッセージ受信処理を行う
	 */
	public native void run();
	
	/**
	 * Windowsのメッセージを処理する(run()から呼び出される）
	 */
	private native void handleNativeMessage(int hwnd, int msg, int wparam, int lparam);
	
	/**
	 * Frame/Dialogが「フローティング」であるかを返す
	 */
	public static int getFloatingFlags() {
		return titleBarFlags;
	}

  /**
   * Creates an embedded window peer, and associates it with an
   * EmbeddedWindow object.
   *
   * @param w The embedded window with which to associate a peer.
   */
	public EmbeddedWindowPeer createEmbeddedWindow(EmbeddedWindow w) {
		// EmbeddedWindowとは何か？
		throw new UnsupportedOperationException("Not implemented");
	}

  @Override
  public boolean isModalExclusionTypeSupported
  (Dialog.ModalExclusionType modalExclusionType)
  {
    return false;
  }

  @Override
  public boolean isModalityTypeSupported(Dialog.ModalityType modalityType)
  {
    return false;
  }
}
