package charactermanaj.util;

import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;

import javax.imageio.ImageIO;
import javax.swing.AbstractButton;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JMenu;


public final class UIHelper {

	private static final UIHelper singleton = new UIHelper();

	/**
	 * クラスパスからのみ探索するリソースローダー
	 */
	private final ResourceLoader resourceLoader = new ResourceLoader(ResourceLoader.getDefaultClassLoader());

	private UIHelper() {
		super();
	}

	public static final UIHelper getInstance() {
		return singleton;
	}

	private double scaleX = 1;

	private double scaleY = 1;

	public void setScaleX(double scaleX) {
		this.scaleX = scaleX;
	}

	public double getScaleX() {
		return scaleX;
	}

	public void setScaleY(double scaleY) {
		this.scaleY = scaleY;
	}

	public double getScaleY() {
		return scaleY;
	}

	/**
	 * 指定したコンテナに含まれる指定したコンポーネント型のすべてのコンポーネントを返す.<br>
	 * 一つも該当するものがなければ空を返す
	 * @param <T> 対象のコンポーネントのクラス型
	 * @param clz クラス
	 * @param container 対象のコンテナ
	 * @return コンポーネントのコレクション、もしくは空
	 */
	@SuppressWarnings("unchecked")
	public <T> Collection<T> getDescendantOfClass(Class<T> clz, Container container) {
		if (container == null || clz == null) {
			throw new IllegalArgumentException();
		}
		Collection<Component> components = new ArrayList<Component>();
		getDescendantOfClass(clz, container, components);
		return (Collection<T>) components;
	}

	private void getDescendantOfClass(Class<?> clz, Container container, Collection<Component> results) {
		if (container == null) {
			return;
		}
		Component[] children = (container instanceof JMenu) ?
				((JMenu) container).getMenuComponents() : container.getComponents();
		int mx = children.length;
		for (int idx = 0; idx < mx; idx++) {
			Component comp = children[idx];
			if (clz.isInstance(comp)) {
				results.add(comp);
			} else if (comp instanceof Container) {
				getDescendantOfClass(clz, (Container) comp, results);
			}
		}
	}

	/**
	 * 2つのステートをもつアイコンを作成します.<br>
	 * このアイコンは、使用するコンポーネントがAbstractButton派生クラスであれば、isSelectedの結果が
	 * trueである場合は2番目のアイコンイメージを表示します.<br>
	 * isSelectedの結果がfalseであるか、もしくはAbstractButton派生クラスでなければ
	 * 最初のアイコンイメージを表示します.<br>
	 * @param iconName1 アイコン1
	 * @param iconName2 アイコン2
	 * @return アイコン
	 */
	public Icon createTwoStateIcon(String iconName1, String iconName2) {
		if (iconName1 == null || iconName2 == null || iconName1.length() == 0
				|| iconName2.length() == 0) {
			throw new IllegalArgumentException();
		}
		final BufferedImage pinIcon1 = getImage(iconName1);
		final BufferedImage pinIcon2 = getImage(iconName2);

		Icon icon = new Icon() {
			public void paintIcon(Component c, Graphics g, int x, int y) {
				boolean selected = false;
				if (c instanceof AbstractButton) {
					AbstractButton btn = (AbstractButton) c;
					selected = btn.isSelected();
				}
				BufferedImage iconImage;
				if ( !selected) {
					iconImage = pinIcon1;
				} else {
					iconImage = pinIcon2;
				}
				int w = iconImage.getWidth();
				int h = iconImage.getHeight();
				g.drawImage(iconImage, x, y,
						(int)(w * scaleX), (int)(h * scaleY), 0, 0, w, h, null);
			}
			public int getIconHeight() {
				return (int)(pinIcon1.getHeight() * scaleY);
			}
			public int getIconWidth() {
				return (int)(pinIcon1.getWidth() * scaleX);
			}
		};

		return icon;
	}

	/**
	 * アイコンボタン(非透過)を作成して返す.<br>
	 * リソースが取得できない場合は実行時例外が返される.<br>
	 * @param iconName 画像リソース名
	 * @return アイコンボタン
	 */
	public JButton createIconButton(String iconName) {
		if (iconName == null || iconName.length() == 0) {
			throw new IllegalArgumentException();
		}
		JButton btn = new JButton();
		btn.setIcon(createImageIcon(iconName));

		return btn;
	}

	/**
	 * 通常時の画像のみをもつ透過ボタンを作成して返す.<br>
	 * リソースが取得できない場合は実行時例外が返される.<br>
	 * @param normal 通常時の画像リソース
	 * @return 透過ボタン
	 */
	public JButton createTransparentButton(String normal) {
		return createTransparentButton(normal, null);
	}

	/**
	 * リソースから通常とホバー時の画像をもつ透過ボタンを作成して返す.<br>
	 * リソースが取得できない場合は実行時例外が返される.<br>
	 * @param normal 通常時の画像リソース
	 * @param rollover ホバー時の画像リソース
	 * @return 透過ボタン
	 */
	public JButton createTransparentButton(String normal, String rollover) {
		if (normal == null || normal.length() == 0) {
			throw new IllegalArgumentException();
		}
		ImageIcon normIcon = createImageIcon(normal);
		JButton btn = new JButton(normIcon);

		if (rollover != null && rollover.length() != 0) {
			ImageIcon rolloverIcon = createImageIcon(rollover);
			btn.setRolloverEnabled(true);
			btn.setRolloverIcon(rolloverIcon);
			btn.setPressedIcon(rolloverIcon);
		}

		btn.setOpaque(false);
		btn.setBorderPainted(false);
		btn.setContentAreaFilled(false);

		return btn;
	}

	/**
	 * イメージアイコンを取得する。
	 * 設定されているスケールに拡大されたイメージでアイコンが設定される。
	 * @param name アイコンリソース名
	 * @return スケール済みのイメージアイコン
	 */
	public ImageIcon createImageIcon(String name) {
		Image img;
		if (scaleX != 1 || scaleY != 1) {
			BufferedImage bimg = getImage(name);
			img = bimg.getScaledInstance(
					(int)(bimg.getWidth() * scaleX),
					(int)(bimg.getHeight() * scaleY),
					Image.SCALE_DEFAULT);
		} else {
			img = getImage(name);
		}
		return new ImageIcon(img);
	}

	/**
	 * リソースから画像を取得する.<br>
	 * 画像が取得できない場合は実行時例外を返す.<br>
	 * @param name リソース
	 * @return 画像
	 */
	public BufferedImage getImage(String name) {
		URL url = resourceLoader.getResource(name);
		if (url == null) {
			throw new RuntimeException("resource not found. " + name);
		}
		try {
			return ImageIO.read(url);

		} catch (IOException ex) {
			throw new RuntimeException("image load error." + ex.getMessage(), ex);
		}
	}

}
