/*
 * The MIT License
 *
 * Copyright 2013 Dra0211.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package kinugasa.ui;

import java.awt.Color;
import java.awt.Graphics2D;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import kinugasa.util.FrameTimeCounter;
import kinugasa.util.TimeCounter;

/**
 * 1つ以上の文字セットから文字列を自由に構築できる入力ウインドウです.
 * <br>
 * 入力メッセージウインドウは指定された文字数までの文字列を、指定された文字セットから1文字ずつ自由に入力できる
 * ウインドウの実装です。<br>
 * ウインドウから入力された文字列は1行のラベルとして表示可能です。<br>
 * <br>
 * このウインドウには0行以上の「質問文」、1行の「入力確認ラベル」、いくつかの「文字セット」および、
 * 使用する場合には「コマンドラベル」が表示されます。<br>
 * <br>
 * 質問文とはプレイヤーに何を入力してもらいたいのかを指示するラベルのことです。このラベルはスーパークラスの機能を使用して
 * 作成、描画されます。このラベルは複数行表示することができるので、テキスト中に"\n"を使用することで
 * 改行することができます。<br>
 * <br>
 * 入力確認ラベルとは入力を確定した文字がリアルタイムに表示されるラベルです。このラベルには
 * デフォルトで"＿"が最大文字数分表示されており、文字列を取得する際にはこの"＿"が""に置換されます。<br>
 * したがって、入力文字として全角のアンダースコア"＿"を使用することはできません。<br>
 * <br>
 * 文字セットとは入力可能な文字のセットです。これはCharSetクラスを使用して定義されます。<br>
 * 文字セットは、通常、操作性をよくするためにいくつかのスペース" "をセット中に含ませることがあります。<br>
 * したがって、文字セット中の文字は必ずしも重複してはならないというルールはありません。<br>
 * 文字セットはフォントが提供する文字を自由に定義可能です。たとえば漢字や他の文字であっても
 * 入力フォームを作成することができます。<br>
 * <br>
 * このウインドウにはラベルのプリセットとしてコマンドというラベルを構築する機能が備わっています。<br>
 * このラベルは、入力を確定したりキャンセルして戻る処理を判定するためのもので、使用する場合は
 * 文字セットの下部に表示されます。<br>
 * <br>
 * <br>
 * このウインドウの実装では、独自のラベル配置アルゴリズムを用いるため、スーパークラスに設定されるラベルは
 * 「質問文」だけです。それ以外のすべてのラベルはこのクラスに独自に格納されます。<br>
 * <br>
 * このウインドウは、通常2つ以上表示することはないと考えられるので、
 * クローニングはシャローコピーとなります。すべてのラベル情報はその参照がコピーされます。<br>
 * <br>
 * <Br>
 * 入力メッセージウインドウは、入力された文字が挿入される位置を示すためのキャレットを持ちます。<br>
 * キャレットは単色の矩形として入力確認ラベルの次に文字が挿入される位置に描画されます。<br>
 * 最大の文字数まで入力されている場合はキャレットは表示されません。<br>
 * キャレットの色はデフォルトでは透過度50%の白が設定されています。<br>
 * <br>
 *
 * @version 1.0.0 - 2012/11/25_14:13:24.<br>
 * @author Dra0211<br>
 * <br>
 */
public class InputMessageWindow extends MessageWindow {

	private static final long serialVersionUID = -3348163241388833782L;
	/** コマンド用ラベルの"OK"処理を定義するラベルの文字列です. */
	public static String COMMAND_OK = "決定";
	/** コマンド用ラベルの"Cancel"処理を定義するラベルの文字列です. */
	public static String COMMAND_CANCEL = "戻る";

	/**
	 * 新しい入力メッセージウインドウを作成します.
	 *
	 * @param x ウインドウのX座標を指定します。<br>
	 * @param y ウインドウのY標を指定します。<br>
	 * @param width ウインドウの幅指定します。<br>
	 * @param height ウインドウの高さを指定します。<br>
	 * @param windowModel メッセージウインドウのモデルを指定します。<br>
	 * @param messageLabelModel 質問文と入力確認ラベルのモデルを指定します。<br>
	 * @param choicesLabelModel 選択肢とコマンドラベルのモデルを指定します。<br>
	 * @param message 質問文です。改行"\n"を使用して複数行にできます。<br>
	 * @param choiceEffect 選択肢の選択エフェクトを指定します。<br>
	 * @param maxLength 入力可能な最大の文字数を指定します。<br>
	 * @param useCommandLabels コマンドラベルを使用する場合はtrue、使用しない場合はfalseを送信します。<br>
	 * @param charSet 使用する文字セットを1つ以上送信します。<br>
	 *
	 * @throws IllegalArgumentException charSetかmessageがnullの場合に投げられます。<br>
	 * @throws NullPointerException 文字セットが0個であるか、最大の文字数が0未満である場合に投げられます。<br>
	 */
	public InputMessageWindow(
			float x, float y, float width, float height,
			MessageWindowModel windowModel, LabelModel messageLabelModel,
			LabelModel choicesLabelModel, String message,
			ChoiceEffect choiceEffect, int maxLength, boolean useCommandLabels,
			CharSet... charSet)
			throws IllegalArgumentException, NullPointerException {
		super(x, y, width, height, windowModel, messageLabelModel, message);
		if (charSet == null) {
			throw new NullPointerException("! > InputMessageWindow : ctor : charSet is null : message=[" + message + "]");
		}
		if (message == null) {
			throw new NullPointerException("! > InputMessageWindow : ctor : message is null : message=[" + message + "]");
		}
		if (charSet.length == 0) {
			throw new IllegalArgumentException("! > InputMessageWindow : ctor : charSet.length == 0 : message=[" + message + "]");
		}
		if (maxLength <= 0) {
			throw new IllegalArgumentException("! > InputMessageWindow : ctor maxLength <= 0 : maxLength=[" + maxLength + "]");
		}
		this.choiceEffect = choiceEffect;
		this.selectedIndexX = this.selectedIndexY = 0;
		this.caretIndex = 0;
		this.choicesLabelModel = choicesLabelModel;
		this.maxLength = maxLength;
		this.charSets = charSet;
		this.caretBlinkRate = new FrameTimeCounter(20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
		initLabels(useCommandLabels);
		this.caretY = inputValueLabel.getY();
		this.caretX = inputValueLabel.getX();
	}

	/**
	 * 質問文、入力確認、選択肢の各ラベルを初期化して配置します.
	 *
	 * @param useCommand コマンドラベルを使用する場合にtrueを送信します。<br>
	 */
	private void initLabels(boolean useCommand) {
		float charSize = choicesLabelModel.getFontSize() + 2;
		float x = labels[labels.length - 1].getX() + 8;
		float y = labels[labels.length - 1].getY() + charSize * 2;
		String defaultText = "";
		for (int i = 0; i < maxLength; i++) {
			defaultText += "＿";
		}
		this.inputValueLabel = new TextLabel(x, y, defaultText, labels[0].getLabelModel());
		y += charSize * 2;

		List<TextLabel[]> csChoiceLabels = new ArrayList<TextLabel[]>(512);
		int maxWidth = 0;
		for (CharSet cs : charSets) {
			for (TextLabel[] l : cs.parse(x, y, choicesLabelModel)) {
				csChoiceLabels.add(l);
				if (maxWidth < l.length) {
					maxWidth = l.length;
				}
			}
			y += charSize * cs.rowNum() + charSize;
		}
		choiceLabels = new TextLabel[csChoiceLabels.size() + (useCommand ? 1 : 0)][];
		for (int i = 0, size = csChoiceLabels.size(); i < size; i++) {
			TextLabel[] line = csChoiceLabels.get(i);
			choiceLabels[i] = new TextLabel[line.length];
			System.arraycopy(line, 0, choiceLabels[i], 0, line.length);
		}
		if (useCommand) {
			initCommandLabels();
		}
	}

	/**
	 * コマンドラベルを使用する場合に、座標などの初期化を行うメソッドです.
	 * コマンドラベルは選択肢の配列にも追加されます。コマンドラベルの配列と選択肢の配列に含まれる
	 * コマンドラベルのインスタンスは同じです。<br>
	 * このメソッドはinitLabelsからコールされます。<br>
	 */
	private void initCommandLabels() {
		choiceLabels[choiceLabels.length - 1] = new TextLabel[2];
		TextLabel mostUnder = choiceLabels[choiceLabels.length - 2][0];
		assert mostUnder != null : "[先天]あなたのプログラムは間違っている mostUnder == null";
		float x = mostUnder.getX();
		float y = mostUnder.getY();
		y += mostUnder.getLabelModel().getFontSize() + 2;
		commandLabels = new TextLabel[]{
			new TextLabel(x, y, COMMAND_OK, choicesLabelModel),
			new TextLabel(x + choicesLabelModel.getFontSize() * 2 + 12, y, COMMAND_CANCEL, choicesLabelModel),};
		System.arraycopy(commandLabels, 0, choiceLabels[choiceLabels.length - 1], 0, commandLabels.length);
	}

	/**
	 * このウインドウが使用している文字セットの中から文字cが含まれる文字セットを検索します.
	 *
	 * @param c 検索する文字を指定します。<br>
	 *
	 * @return 文字cが最初に発見された文字セットを返します。<br>
	 *
	 * @throws AssertionError 文字が含まれていない場合に投げられます。通常の使用方法ではこの例外が投げられることはありません。<br>
	 */
	private CharSet searchCharSet(char c) throws AssertionError {
		for (CharSet cs : charSets) {
			if (cs.contains(Character.toString(c))) {
				return cs;
			}
		}
		throw new AssertionError("! > InputMessageWindow : character not found :" + c);
	}
	/** 選択肢の配列です. コマンドラベルを含みます. */
	private TextLabel[][] choiceLabels;
	/** 入力可能な最大文字数です. */
	private int maxLength;
	/** 選択肢のモデルです. */
	private LabelModel choicesLabelModel;
	/** 使用する文字セットです. */
	private CharSet[] charSets;
	/** 文字セットの選択ラベルです. */
	private ChoiceEffect choiceEffect;
	/** 選択中ラベルのX軸座標です. */
	private int selectedIndexX;
	/** 選択中ラベルのY軸座標です. */
	private int selectedIndexY;
	/** 入力確認ラベルです. */
	private TextLabel inputValueLabel;
	/** キャレットを表示する入力確認ラベルに対する文字インデックスです. */
	private int caretIndex;
	/** キャレットの点滅間隔です. */
	private TimeCounter caretBlinkRate;
	/** キャレットを描画するX軸座標です. */
	private float caretX;
	/** キャレットを描画するY軸座標です. */
	private float caretY;
	/** キャレットの色です. */
	private Color caretColor = new Color(255, 255, 255, 128);
	/** コマンドラベルの配列です. */
	private TextLabel[] commandLabels;

	@Override
	public void draw(Graphics2D g) {
		super.draw(g);
		inputValueLabel.draw(g);
		for (TextLabel[] line : choiceLabels) {
			for (TextLabel label : line) {
				label.draw(g);
			}
		}
		if (choiceEffect != null) {
			choiceEffect.draw(g, getSelectedLabel());
		}
		if (caretBlinkRate.isReaching() && !isMaxInput()) {
			drawCaret(g);
		}
		if (commandLabels != null) {
			for (TextLabel label : commandLabels) {
				label.draw(g);
			}
		}
	}

	/**
	 * キャレットを描画します.
	 *
	 * @param g 書き込むグラフィックスコンテキストを送信します。<br>
	 */
	private void drawCaret(Graphics2D g) {
		g.setColor(caretColor);
		float size = inputValueLabel.getLabelModel().getFontSize();
		g.fillRect((int) caretX, (int) caretY, (int) size / 2, (int) size);
	}

	/**
	 * コマンドラベルを選択中であるかを調べます.
	 *
	 * @return コマンドラベルを使用しており、決定かキャンセルのコマンドを選択している場合にtrueを返します。<br>
	 */
	public boolean isCommandSelect() {
		return isOkSelected() | isCancelSelected();
	}

	/**
	 * 決定コマンドを選択中であるかを調べます.
	 *
	 * @return コマンドラベルを使用しており、選択されているラベルが決定ラベルである場合にtrueを返します。<br>
	 */
	public boolean isOkSelected() {
		if (commandLabels == null) {
			return false;
		}
		for (TextLabel label : commandLabels) {
			if (getSelectedLabel().equals(label) && label.getText().equals(COMMAND_OK)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * キャンセルコマンドを選択中であるかを調べます.
	 *
	 * @return コマンドラベルを使用しており、選択されているラベルがキャンセルラベルである場合にtrueを返します。<br>
	 */
	public boolean isCancelSelected() {
		for (TextLabel label : commandLabels) {
			if (getSelectedLabel().equals(label) && label.getText().equals(COMMAND_CANCEL)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 選択中の文字を入力し、1文字追加します.
	 * 入力が最大文字数である場合は何も行いません。<br>
	 *
	 * @return 入力が確定している文字列を返します。今回の呼び出しまでに入力された
	 * 文字列を連結したもので、この呼び出しによって新しく追加された文字を含みます。<br>
	 */
	public String enter() {
		if (isCommandSelect()) {
			return getSelectedLabel().getText();
		}
		if (isMaxInput()) {
			return inputValueLabel.getText().replaceAll("＿", "");
		}
		char[] inputData = inputValueLabel.getText().toCharArray();
		inputData[caretIndex] = getSelectedLabel().getText().charAt(0);
		caretX += searchCharSet(inputData[caretIndex]).
				convertSize(inputValueLabel.getLabelModel().getFontSize());
		if (inputValueLabel.getLabelModel().getFont().isBold()) {
			caretX += 1;
		}
		inputValueLabel.setText(String.valueOf(inputData));
		caretIndex++;
		return inputValueLabel.getText().replaceAll("＿", "");
	}

	/**
	 * 直前の入力をキャンセルし、1文字削除します.
	 * 入力が0文字である場合は何も行いません。<br>
	 */
	public void backSpace() {
		if (isNotYetInput()) {
			return;
		}
		caretIndex--;
		char[] inputData = inputValueLabel.getText().toCharArray();
		caretX -= searchCharSet(inputData[caretIndex]).
				convertSize(inputValueLabel.getLabelModel().getFontSize());
		if (inputValueLabel.getLabelModel().getFont().isBold()) {
			caretX -= 1;
		}
		inputData[caretIndex] = '＿';
		inputValueLabel.setText(String.valueOf(inputData));
	}

	/**
	 * 入力を確定し、ウインドウを閉じます.
	 * このメソッドを呼び出すとウインドウの生存状態と可視状態をfalseにした後、
	 * getInputTextを返します。<br>
	 *
	 * @return 入力された文字列を返します。<br>
	 */
	public String ok() {
		setExist(false);
		setVisible(false);
		return getInputText();
	}

	/**
	 * 入力をキャンセルし、ウインドウを閉じます.
	 * このメソッドを呼び出すと入力確認ラベルのテキストが空(最大文字数分の"＿")になり、
	 * ウインドウの生存状態と可視状態をfalseに設定します。<br>
	 */
	public void cancel() {
		String text = "";
		for (int i = 0; i < maxLength; i++) {
			text += "＿";
		}
		inputValueLabel.setText(text);
		setExist(false);
		setVisible(false);
	}

	/**
	 * プレイヤーによる現在の入力値が最大の文字数であるかを検査します.
	 *
	 * @return プレイヤーの入力が最大の文字数である場合はtrueを、そうでない場合はfalseを返します。<br>
	 */
	public boolean isMaxInput() {
		return caretIndex == maxLength;
	}

	/**
	 * プレイヤーによる現在の入力値が0文字であるかを検査します.
	 *
	 * @return プレイヤーが入力していない場合はtrueを、1文字以上入力している場合はfalseを返します。<br>
	 */
	public boolean isNotYetInput() {
		return caretIndex == 0;
	}

	/**
	 * 選択を左に移動します.
	 * 左に選択肢がない場合(最も左の選択肢の場合)は移動しません。<br>
	 */
	public void toLeft() {
		selectedIndexX--;
		clamp();
	}

	/**
	 * 選択を右に移動します.
	 * 右に選択肢がない場合(最も右の選択肢の場合)は移動しません。<br>
	 */
	public void toRight() {
		selectedIndexX++;
		clamp();
	}

	/**
	 * 選択を下に移動します.
	 * 下に選択肢がない場合(最も下の選択肢の場合)は移動しません。<br>
	 */
	public void toDown() {
		selectedIndexY++;
		clamp();
	}

	/**
	 * 選択を上に移動します.
	 * 上に選択肢がない場合(最も上の選択肢の場合)は移動しません。<br>
	 */
	public void toUp() {
		selectedIndexY--;
		clamp();
	}

	/**
	 * 選択中ラベルインデックスを有効範囲内に切り詰めます.
	 */
	private void clamp() {
		if (selectedIndexY < 0) {
			selectedIndexY = 0;
		} else if (selectedIndexY > choiceLabels.length - 1) {
			selectedIndexY = choiceLabels.length - 1;
		}
		if (selectedIndexX < 0) {
			selectedIndexX = 0;
		} else if (selectedIndexX > choiceLabels[selectedIndexY].length - 1) {
			selectedIndexX = choiceLabels[selectedIndexY].length - 1;
		}
	}

	/**
	 * キャレットの点滅間隔を設定します.
	 *
	 * @param cursorBlink 新しいキャレットの点滅間隔を指定します。<br>
	 */
	public void setCaretBlinkRate(TimeCounter cursorBlink) {
		this.caretBlinkRate = cursorBlink;
	}

	/**
	 * キャレットの点滅間隔を取得します.
	 *
	 * @return キャレットの点熱間隔に使用されてるタイムカウンタを返します。<br>
	 */
	public TimeCounter getCaretBlinkRate() {
		return caretBlinkRate;
	}

	/**
	 * キャレットの色を取得します.
	 *
	 * @return このウインドウのキャレットの色を取得します。デフォルトでは透過度50%の白をです。<br>
	 */
	public Color getCaretColor() {
		return caretColor;
	}

	/**
	 * キャレットの色を変更します.
	 *
	 * @param caretColor 新しいキャレットの色を設定します。<br>
	 */
	public void setCaretColor(Color caretColor) {
		this.caretColor = caretColor;
	}

	/**
	 * 入力可能な最大の文字数を取得します.
	 *
	 * @return プレイヤーがこのウインドウに入力できる文字数の最大値を返します。<br>
	 */
	public int getMaxLength() {
		return maxLength;
	}

	/**
	 * 現在選択されているラベルを返します。
	 *
	 * @return 現在選択されているラベルのクローンを返します。<br>
	 */
	public TextLabel getSelectedLabel() {
		return choiceLabels[selectedIndexY][selectedIndexX].clone();
	}

	/**
	 * 選択中のラベルのテキストを取得します.
	 *
	 * @return 選択中のラベルのテキストを返します。getSelectedLabel().getText()と同じ結果です。<br>
	 */
	public String getSelectedLabelText() {
		return getSelectedLabel().getText();
	}

	/**
	 * 選択中ラベルのY軸のインデックスを取得します.
	 *
	 * @return 選択エフェクトが適用されるラベルのX軸のインデックスを返します。<br>
	 */
	public int getSelectedIndexX() {
		return selectedIndexX;
	}

	/**
	 * 選択中ラベルのX軸のインデックスを設定します.
	 *
	 * @param selectedIndexX 選択エフェクトが適用されるラベルのX軸のインデックスを送信します。<br>
	 */
	public void setSelectedIndexX(int selectedIndexX) {
		this.selectedIndexX = selectedIndexX;
	}

	/**
	 * 選択中ラベルのY軸のインデックスを取得します.
	 *
	 * @return 選択エフェクトが適用されるラベルのY軸のインデックスを返します。<br>
	 */
	public int getSelectedIndexY() {
		return selectedIndexY;
	}

	/**
	 * 選択中ラベルのY軸のインデックスを設定します.
	 *
	 * @param selectedIndexY 選択エフェクトが適用されるラベルのY軸のインデックスを送信します。<br>
	 */
	public void setSelectedIndexY(int selectedIndexY) {
		this.selectedIndexY = selectedIndexY;
	}

	/**
	 * このウインドウに表示されている全ての文字セットを取得します.
	 * このメソッドは参照を戻します。戻り値に対する操作はウインドウに影響を与えます。<br>
	 *
	 * @return このウインドウが使用中の全ての文字セットを返します。<br>
	 */
	public CharSet[] getCharSets() {
		return charSets;
	}

	/**
	 * 選択肢として表示されているすべてのラベルをリストとして取得します.
	 * このメソッドの戻り値には操作性のために確保されているスペース" "のラベルや
	 * コマンドラベルが含まれます。<br>
	 * このメソッドは参照を戻します。戻り値に対する操作はこのウインドウの選択肢ラベルに影響します。<br>
	 *
	 * @return 全ての選択肢をリストとして返します。<br>
	 */
	public List<TextLabel> getChoiceLabelsList() {
		List<TextLabel> result = new ArrayList<TextLabel>();
		for (TextLabel[] line : choiceLabels) {
			result.addAll(Arrays.asList(line));
		}
		return result;
	}

	/**
	 * 選択肢として表示されているすべてのラベルをその配置の2次元配列として取得します.
	 * このメソッドの戻り値には操作性のために確保されているスペース" "のラベルや
	 * コマンドラベルが含まれます。<br>
	 * このメソッドは参照を戻します。戻り値に対する操作はこのウインドウの選択肢ラベルに影響します。<br>
	 *
	 * @return 全ての選択肢を2次元配列として返します。<br>
	 */
	public TextLabel[][] getChoiceLabels() {
		return choiceLabels;
	}

	/**
	 * 選択肢として表示されているラベルのモデルを取得します.
	 *
	 * @return 選択肢ラベルのモデルを返します。<br>
	 */
	public LabelModel getChoicesLabelModel() {
		return choicesLabelModel;
	}

	/**
	 * このウインドウの文字セットに対して使用されている選択エフェクトを取得します.
	 *
	 * @return 現在の選択エフェクトを返します。設定されていない場合はnullを返します。<br>
	 */
	public ChoiceEffect getChoiceEffect() {
		return choiceEffect;
	}

	/**
	 * このウインドウの文字セットに対して使用されている選択エフェクトを変更します.
	 *
	 * @param choiceEffect 新しい選択エフェクトを指定します。表示しない場合はnullを使用できます。<br>
	 */
	public void setChoiceEffect(ChoiceEffect choiceEffect) {
		this.choiceEffect = choiceEffect;
	}

	/**
	 * プレイヤーが入力したテキストを表示するために使用されているラベルを取得します.
	 * このラベルはプレイヤーに入力可能な文字数を伝えるために未入力文字は"＿"
	 * に置換されています。<br>
	 * 純粋な入力された文字だけを取得するにはgetInputTextを使用します。<br>
	 *
	 * @return プレイヤーが入力したテキストを表示しているラベルを取得します。クローンを返します。<bR>
	 */
	public TextLabel getInputValueLabel() {
		return inputValueLabel.clone();
	}

	/**
	 * プレイヤーによって入力された文字列を取得します.
	 * このメソッドでは、実際に表示されているラベルのテキストの
	 * "＿"を""に置換して返します。<br>
	 * "＿"を含めた実際に表示されている文字列を取得するにはgetInputValueLabelを
	 * 使用します。<br>
	 *
	 * @return プレイヤーによって入力された文字列を取得します。<br>
	 */
	public String getInputText() {
		return inputValueLabel.getText().replaceAll("＿", "");
	}

	/**
	 * このウインドウに設定されているすべての文字セット用ラベルをストリームに発行します.
	 * このメソッドはデバッグ用です。<br>
	 *
	 * @param stream 発行するストリームを指定します。<br>
	 */
	public void printAll(PrintStream stream) {
		System.out.println("> InputMessageWindow : printAll : ");
		for (TextLabel[] l : choiceLabels) {
			for (TextLabel t : l) {
				System.out.print(t.getText() + ", ");
			}
			stream.println();
		}
		System.out.println("> InputMessageWindow : printAll : --------------------");
	}

	@Override
	public InputMessageWindow clone() {
		InputMessageWindow result = (InputMessageWindow) super.clone();
		return result;
	}

	@Override
	public String toString() {
		return "InputMessageWindow{" + "choiceLabels=" + choiceLabels
				+ ", maxLength=" + maxLength + ", choicesLabelModel="
				+ choicesLabelModel + ", charSets=" + charSets
				+ ", choiceEffect=" + choiceEffect + ", selectedIndexX="
				+ selectedIndexX + ", selectedIndexY=" + selectedIndexY
				+ ", inputValueLabel=" + inputValueLabel + ", caretIndex="
				+ caretIndex + ", caretBlinkRate=" + caretBlinkRate
				+ ", caretX=" + caretX + ", caretY=" + caretY
				+ ", caretColor=" + caretColor + ", commandLabels="
				+ commandLabels + '}';
	}
}