/*
 * Copyright (C) 2008,2009  OMRON SOFTWARE Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.gorry.android.input.nicownng;

import java.util.List;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.Keyboard.Key;
import android.inputmethodservice.KeyboardView;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.util.FloatMath;
import android.util.Log;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.EditorInfo;
import android.widget.PopupWindow;
import android.widget.TextView;


/**
 * The default software keyboard class.
 *
 * @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD.  All Rights Reserved.
 */
public class DefaultSoftKeyboard implements InputViewManager, KeyboardView.OnKeyboardActionListener, KeyboardView.OnTouchListener, OnGestureListener {
	/*
	 *----------------------------------------------------------------------
	 * key codes for a software keyboard
	 *----------------------------------------------------------------------
	 */
	/** Change the keyboard language */
	public static final int KEYCODE_CHANGE_LANG = -500;

	/* for Japanese 12-key keyboard */
	/** Japanese 12-key keyboard [1] */
	public static final int KEYCODE_JP12_1 = -201;
	/** Japanese 12-key keyboard [2] */
	public static final int KEYCODE_JP12_2 = -202;
	/** Japanese 12-key keyboard [3] */
	public static final int KEYCODE_JP12_3 = -203;
	/** Japanese 12-key keyboard [4] */
	public static final int KEYCODE_JP12_4 = -204;
	/** Japanese 12-key keyboard [5] */
	public static final int KEYCODE_JP12_5 = -205;
	/** Japanese 12-key keyboard [6] */
	public static final int KEYCODE_JP12_6 = -206;
	/** Japanese 12-key keyboard [7] */
	public static final int KEYCODE_JP12_7 = -207;
	/** Japanese 12-key keyboard [8] */
	public static final int KEYCODE_JP12_8 = -208;
	/** Japanese 12-key keyboard [9] */
	public static final int KEYCODE_JP12_9 = -209;
	/** Japanese 12-key keyboard [0] */
	public static final int KEYCODE_JP12_0 = -210;
	/** Japanese 12-key keyboard [#] */
	public static final int KEYCODE_JP12_SHARP = -211;
	/** Japanese 12-key keyboard [*] */
	public static final int KEYCODE_JP12_ASTER = -213;
	/** Japanese 12-key keyboard [DEL] */
	public static final int KEYCODE_JP12_BACKSPACE = -214;
	/** Japanese 12-key keyboard [SPACE] */
	public static final int KEYCODE_JP12_SPACE = -215;
	/** Japanese 12-key keyboard [ENTER] */
	public static final int KEYCODE_JP12_ENTER = -216;
	/** Japanese 12-key keyboard [RIGHT ARROW] */
	public static final int KEYCODE_JP12_RIGHT = -217;
	/** Japanese 12-key keyboard [LEFT ARROW] */
	public static final int KEYCODE_JP12_LEFT = -218;
	/** Japanese 12-key keyboard [REVERSE TOGGLE] */
	public static final int KEYCODE_JP12_REVERSE = -219;
	/** Japanese 12-key keyboard [CLOSE] */
	public static final int KEYCODE_JP12_CLOSE   = -220;
	/** Japanese 12-key keyboard [KEYBOARD TYPE CHANGE] */
	public static final int KEYCODE_JP12_KBD   = -221;
	/** Japanese 12-key keyboard [EMOJI] */
	public static final int KEYCODE_JP12_EMOJI      = -222;
	/** Japanese 12-key keyboard [FULL-WIDTH HIRAGANA MODE] */
	public static final int KEYCODE_JP12_ZEN_HIRA   = -223;
	/** Japanese 12-key keyboard [FULL-WIDTH NUMBER MODE] */
	public static final int KEYCODE_JP12_ZEN_NUM    = -224;
	/** Japanese 12-key keyboard [FULL-WIDTH ALPHABET MODE] */
	public static final int KEYCODE_JP12_ZEN_ALPHA  = -225;
	/** Japanese 12-key keyboard [FULL-WIDTH KATAKANA MODE] */
	public static final int KEYCODE_JP12_ZEN_KATA   = -226;
	/** Japanese 12-key keyboard [HALF-WIDTH KATAKANA MODE] */
	public static final int KEYCODE_JP12_HAN_KATA   = -227;
	/** Japanese 12-key keyboard [HALF-WIDTH NUMBER MODE] */
	public static final int KEYCODE_JP12_HAN_NUM    = -228;
	/** Japanese 12-key keyboard [HALF-WIDTH ALPHABET MODE] */
	public static final int KEYCODE_JP12_HAN_ALPHA  = -229;
	/** Japanese 12-key keyboard [MODE TOOGLE CHANGE] */
	public static final int KEYCODE_JP12_TOGGLE_MODE = -230;
	/** Japanese 12-key keyboard [UP ARROW] */
	public static final int KEYCODE_JP12_UP = -232;
	/** Japanese 12-key keyboard [DOWN ARROW] */
	public static final int KEYCODE_JP12_DOWN = -233;
	/** Japanese 12-key keyboard [FULL-WIDTH SYMBOL MODE] */
	public static final int KEYCODE_JP12_ZEN_SYM     = -234;
	/** Japanese 12-key keyboard [CONVERT TO PREDICT WORDS] */
	public static final int KEYCODE_JP12_CONVPREDICT = -235;
	/** Japanese 12-key keyboard [CONVERT TO PREDICT WORDS] */
	public static final int KEYCODE_JP12_CONVPREDICT_BACKWARD = -236;

	/** Japanese 12-key keyboard [ZENKAKU SPACE] */
	public static final int KEYCODE_JP12_ZEN_SPACE = -231;

	/** Key code for EISU-KANA conversion */
	public static final int KEYCODE_EISU_KANA = -305;

	/* for Qwerty keyboard */
	/** Qwerty keyboard [DEL] */
	public static final int KEYCODE_QWERTY_BACKSPACE = -100;
	/** Qwerty keyboard [ENTER] */
	public static final int KEYCODE_QWERTY_ENTER = -101;
	/** Qwerty keyboard [SHIFT] */
	public static final int KEYCODE_QWERTY_SHIFT = Keyboard.KEYCODE_SHIFT;
	/** Qwerty keyboard [ALT] */
	public static final int KEYCODE_QWERTY_ALT   = -103;
	/** Qwerty keyboard [KEYBOARD TYPE CHANGE] */
	public static final int KEYCODE_QWERTY_KBD   = -104;
	/** Qwerty keyboard [CLOSE] */
	public static final int KEYCODE_QWERTY_CLOSE = -105;
	/** Japanese Qwerty keyboard [EMOJI] */
	public static final int KEYCODE_QWERTY_EMOJI = -106;
	/** Japanese Qwerty keyboard [FULL-WIDTH HIRAGANA MODE] */
	public static final int KEYCODE_QWERTY_ZEN_HIRA   = -107;
	/** Japanese Qwerty keyboard [FULL-WIDTH NUMBER MODE] */
	public static final int KEYCODE_QWERTY_ZEN_NUM    = -108;
	/** Japanese Qwerty keyboard [FULL-WIDTH ALPHABET MODE] */
	public static final int KEYCODE_QWERTY_ZEN_ALPHA  = -109;
	/** Japanese Qwerty keyboard [FULL-WIDTH KATAKANA MODE] */
	public static final int KEYCODE_QWERTY_ZEN_KATA   = -110;
	/** Japanese Qwerty keyboard [HALF-WIDTH KATAKANA MODE] */
	public static final int KEYCODE_QWERTY_HAN_KATA   = -111;
	/** Qwerty keyboard [NUMBER MODE] */
	public static final int KEYCODE_QWERTY_HAN_NUM    = -112;
	/** Qwerty keyboard [ALPHABET MODE] */
	public static final int KEYCODE_QWERTY_HAN_ALPHA  = -113;
	/** Qwerty keyboard [MODE TOOGLE CHANGE] */
	public static final int KEYCODE_QWERTY_TOGGLE_MODE = -114;
	/** Qwerty keyboard [PINYIN MODE] */
	public static final int KEYCODE_QWERTY_PINYIN  = -115;

	/** IS01 key */
	public static final int KEYCODE_IS01_E_KAO_KI  = 92;
	public static final int KEYCODE_IS01_MOJI  = 93;

	/** IS11SH key */
	public static final int KEYCODE_IS11SH_E_KAO_KI  = 94;
	public static final int KEYCODE_IS11SH_MOJI  = 95;

	/** 007SH key */
	public static final int KEYCODE_007SH_E_KAO_KI  = 1;
	public static final int KEYCODE_007SH_MOJI  = 2;

	/** OpenWnn instance which hold this software keyboard*/
	protected NicoWnnG      mWnn;

	/** Current keyboard view */
	protected KeyboardView mKeyboardView;

	/** View objects (main side) */
	protected ViewGroup mMainView;
	/** View objects (sub side) */
	protected ViewGroup mSubView;

	/** Current keyboard definition */
	protected MyHeightKeyboard mCurrentKeyboard;

	/** Caps lock state */
	protected boolean mCapsLock;

	/** Input restraint */
	protected boolean mDisableKeyInput = true;

	/**
	 * flick mode works
	 */
	public boolean mNicoFirst = false;

	/** Previous input character code */
	public int mPrevInputKeyCode = 0;

	public int mPressedKeyCode = 0;

	/** flick nicoinput **/
	public static final int NICOFLICK_NONE       = 0;
	public static final int NICOFLICK_1STROKE    = 1;
	public static final int NICOFLICK_NICOSTROKE = 2;

	public int mFlickNicoInput = 0;
	public boolean mNicoFlick = false;

	/** gecture  **/
	private static final DisplayMetrics mMetrics = new DisplayMetrics();
	private GestureDetector   mDetector;
	private float             mStartX, mStartY;
	private float             mGestureX, mGestureY;
	private boolean           mIsActiveLongPress;
	private boolean           mIsLongPress;
	//private static final int ya_flick[] = { 0, 2, -1, 1, -1 };
	//private static final int wa_flick[] = { 0, -1, 1, 2, 5 };
	/**
	 * flick sensivity
	 */
	public static final int[] flickSensitivityModeTable = {
		15, 20, 25, 30, 35, 40, 50, 60
	};
	public int mFlickSensitivity = 0;
	public boolean mFlickGuide = true;
	/**
	 * input keyboard key height
	 */
	public int mInputViewHeightIndex = 0;
	public final static int mKeyboardPaddingTable[] = {
		13, 19, 25, 31, 37, 43, 49, 55
	};

	// KEY TYPE
	public static final int KEYTYPE_12KEY = 0;
	public static final int KEYTYPE_QWERTY = 1;
	public static final int KEYTYPE_NICO2 = 2;

	public int mQwertyKanaMode = 0;
	public boolean mQwertyMatrixMode = false;

	private static final NicoWnnGEvent mEventInputKeyDel      = new NicoWnnGEvent(NicoWnnGEvent.INPUT_SOFT_KEY, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
	
	public int mKeyRepeatFirstTimeout = ViewConfiguration.getLongPressTimeout();
	public int mKeyRepeatSecondTimeout = 100;
	public int mRegisterKeyRepeat = 0;
	public int mKeyRepeatCode = 0;
	public final Handler mHandlerKeyRepeat = new Handler();
	public final Runnable mActionKeyRepeat = new Runnable() {
		public void run() {
			synchronized (this) {
				{
					// キー入力取り消し
			  		final long now = SystemClock.uptimeMillis();
		            MotionEvent event = MotionEvent.obtain(
		              now, now,
		              MotionEvent.ACTION_CANCEL, 0, 0, 0
		            );
			  		mDetector.onTouchEvent(event);
			  	}
				switch (mKeyRepeatCode) {
				  case KEYCODE_JP12_EMOJI:
				  case KEYCODE_QWERTY_EMOJI:
				  case KEYCODE_EISU_KANA:
					final String str = mWnn.getComposingText(ComposingText.LAYER2);
					mWnn.onEvent(new NicoWnnGEvent(NicoWnnGEvent.INPUT_KEY, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK)));
					mWnn.invokeMushroom(str);
					break;

				  default:
					onKey(mKeyRepeatCode, null);
					if (mRegisterKeyRepeat > 0) {
						mHandlerKeyRepeat.postDelayed(mActionKeyRepeat, mKeyRepeatSecondTimeout);
						mRegisterKeyRepeat = 2;
					}
					break;
				}
			}
		}
	};

	public void setKeyRepeat(int code) {
		synchronized (this) {
			if (mRegisterKeyRepeat == 0) {
				mRegisterKeyRepeat = 1;
				mKeyRepeatCode = code;
				mHandlerKeyRepeat.postDelayed(mActionKeyRepeat, mKeyRepeatFirstTimeout);
			}
		}
	}

	public void resetKeyRepeat() {
		synchronized (this) {
			if (mRegisterKeyRepeat > 0) {
				mHandlerKeyRepeat.removeCallbacks(mActionKeyRepeat);
				mRegisterKeyRepeat = 0;
			}
		}
	}

	/**
	 * Keyboard surfaces
	 * <br>
	 * Keyboard[language][portrait/landscape][keyboard type][shift off/on][key-mode]
	 */
	public    MyHeightKeyboard[][][][][][] mKeyboard;
	protected boolean              mCreateKeyboard = false;
	protected boolean mNoAlphaMode = false;
	protected boolean mNoNumberMode = false;

	/* languages */
	/** Current language */
	protected int mCurrentLanguage;
	/** Language (English) */
	public static final int LANG_EN  = 0;
	/** Language (Japanese) */
	public static final int LANG_JA  = 1;
	/** Language (Chinese) */
	public static final int LANG_CN  = 2;
	/** */
	public static final int LANG_MAX  = 3;

	/* portrait/landscape */
	/** State of the display */
	protected int mDisplayMode = 0;
	/** Display mode (Portrait) */
	public static final int PORTRAIT  = 0;
	/** Display mode (Landscape) */
	public static final int LANDSCAPE = 1;
	/** */
	public static final int PORTRAIT_LANDSCAPE = 2;

	/* keyboard type */
	/** Current keyboard type */
	protected int mCurrentKeyboardType;
	/** Keyboard (QWERTY keyboard) */
	public static final int KEYBOARD_QWERTY  = 0;
	/** Keyboard (12-keys keyboard) */
	public static final int KEYBOARD_12KEY   = 1;
	/** */
	public static final int KEYBOARD_MAX   = 4;
	/** State of the shift key */
	protected int mShiftOn = KEYBOARD_SHIFT_OFF;
	/** Shift key off */
	public static final int KEYBOARD_SHIFT_OFF = 0;
	/** Shift key on */
	public static final int KEYBOARD_SHIFT_ON  = 1;
	/** */
	public static final int KEYBOARD_SHIFT_MAX  = 2;

	/* key-modes */
	/** Current key-mode */
	protected int mCurrentKeyMode;

	/** change eisu change mode **/
	protected boolean mKana12Key = false;
	protected boolean mAnum12Key = false;
	protected int[] m12keyTable = new int[KEYMODE_JA_MAX];

	protected boolean mGetNoFlipScreen = false;
	protected boolean mNoFlipScreen = false;


	/* Slide key(only Nicotouch */
	protected int  mCurrentSlide = 0;
	public static final int NICO_SLIDE_MODE_TOP = 0;
	public static final int NICO_SLIDE_MODE_A   = 1;
	public static final int NICO_SLIDE_MODE_K   = 2;
	public static final int NICO_SLIDE_MODE_S   = 3;
	public static final int NICO_SLIDE_MODE_T   = 4;
	public static final int NICO_SLIDE_MODE_N   = 5;
	public static final int NICO_SLIDE_MODE_H   = 6;
	public static final int NICO_SLIDE_MODE_M   = 7;
	public static final int NICO_SLIDE_MODE_Y   = 8;
	public static final int NICO_SLIDE_MODE_R   = 9;
	public static final int NICO_SLIDE_MODE_W   = 10;

	/* key-modes for Nicotouch */
	public static final int NICO_MODE_FULL_HIRAGANA = 0;
	public static final int NICO_MODE_FULL_KATAKANA = 1;
	public static final int NICO_MODE_HALF_KATAKANA = 2;
	public static final int NICO_MODE_MAX = 3;

	/* key-modes for English */
	/** English key-mode (alphabet) */
	public static final int KEYMODE_EN_ALPHABET = 0;
	/** English key-mode (number) */
	public static final int KEYMODE_EN_NUMBER   = 1;
	/** English key-mode (phone number) */
	public static final int KEYMODE_EN_PHONE    = 2;

	/* key-modes for Japanese */
	/** Japanese key-mode (Full-width Nicotouch) */
	public static final int KEYMODE_JA_FULL_NICO     = 0;
	/** Japanese key-mode (Full-width Hiragana) */
	public static final int KEYMODE_JA_FULL_HIRAGANA = 1;
	/** Japanese key-mode (Full-width alphabet) */
	public static final int KEYMODE_JA_FULL_ALPHABET = 2;
	/** Japanese key-mode (Full-width number) */
	public static final int KEYMODE_JA_FULL_NUMBER   = 3;
	/** Japanese key-mode (Full-width Katakana) */
	public static final int KEYMODE_JA_FULL_KATAKANA = 4;
	/** Japanese key-mode (Half-width alphabet) */
	public static final int KEYMODE_JA_HALF_ALPHABET = 5;
	/** Japanese key-mode (Half-width number) */
	public static final int KEYMODE_JA_HALF_NUMBER   = 6;
	/** Japanese key-mode (Half-width Katakana) */
	public static final int KEYMODE_JA_HALF_KATAKANA = 7;
	/** Japanese key-mode (Half-width phone number) */
	public static final int KEYMODE_JA_HALF_PHONE    = 8;
	/** Japanese key-mode (Full-width Nicotouch Katakana) */
	public static final int KEYMODE_JA_FULL_NICO_KATAKANA = 9;
	/** Japanese key-mode (Full-width Nicotouch Katakana) */
	public static final int KEYMODE_JA_HALF_NICO_KATAKANA = 10;
	/** */
	public static final int KEYMODE_JA_MAX    = 11;

	/* key-modes for Chinese */
	/** Chinese key-mode (pinyin) */
	public static final int KEYMODE_CN_PINYIN   = 0;
	/** Chinese key-mode (Full-width number) */
	public static final int KEYMODE_CN_FULL_NUMBER   = 1;
	/** Chinese key-mode (alphabet) */
	public static final int KEYMODE_CN_ALPHABET = 2;
	/** Chinese key-mode (phone) */
	public static final int KEYMODE_CN_PHONE    = 3;
	/** Chinese key-mode (Half-width number) */
	public static final int KEYMODE_CN_HALF_NUMBER   = 4;

	/* key-modes for HARD */
	/** HARD key-mode (SHIFT_OFF_ALT_OFF) */
	public static final int HARD_KEYMODE_SHIFT_OFF_ALT_OFF     = 2;
	/** HARD key-mode (SHIFT_ON_ALT_OFF) */
	public static final int HARD_KEYMODE_SHIFT_ON_ALT_OFF      = 3;
	/** HARD key-mode (SHIFT_OFF_ALT_ON) */
	public static final int HARD_KEYMODE_SHIFT_OFF_ALT_ON      = 4;
	/** HARD key-mode (SHIFT_ON_ALT_ON) */
	public static final int HARD_KEYMODE_SHIFT_ON_ALT_ON       = 5;
	/** HARD key-mode (SHIFT_LOCK_ALT_OFF) */
	public static final int HARD_KEYMODE_SHIFT_LOCK_ALT_OFF    = 6;
	/** HARD key-mode (SHIFT_LOCK_ALT_ON) */
	public static final int HARD_KEYMODE_SHIFT_LOCK_ALT_ON     = 7;
	/** HARD key-mode (SHIFT_LOCK_ALT_LOCK) */
	public static final int HARD_KEYMODE_SHIFT_LOCK_ALT_LOCK   = 8;
	/** HARD key-mode (SHIFT_OFF_ALT_LOCK) */
	public static final int HARD_KEYMODE_SHIFT_OFF_ALT_LOCK    = 9;
	/** HARD key-mode (SHIFT_ON_ALT_LOCK) */
	public static final int HARD_KEYMODE_SHIFT_ON_ALT_LOCK     = 10;

	/** Whether the H/W keyboard is hidden. */
	protected boolean mIsHardKeyboard   = false;
	protected boolean mHardKeyboardHidden = true;
	/** option softkeyboard on/off **/
	protected int mHiddenSoftKeyboard = 0;

	/**
	 * Status of the composing text
	 * <br>
	 * {@code true} if there is no composing text.
	 */
	protected boolean mNoInput = true;

	/** Vibratior for key click vibration */
	protected Vibrator mVibrator = null;

	/** key click sound */
	protected SoundPool mSoundPool = null;
	protected int mSound = 0;
	protected float mSoundVolume = 0.0F;

	/** Key toggle cycle table currently using */
	protected String[] mCurrentCycleTable;

	/** */
	protected boolean mTsuMode = false;

	private NicoWnnG mNicoWnnG;

	protected boolean mSpaceBelowKeyboard = false;

	protected boolean mUseOnetimeShift = true;
	protected boolean mUseFixedShift = true;
	protected int mShiftLockCount = 0;

	/**
	 * Constructor
	 */
	public DefaultSoftKeyboard() { }

	/**
	 * Create keyboard views
	 *
	 * @param parent   OpenWnn using the keyboards.
	 */
	protected void createKeyboards(final NicoWnnG parent) {
		mNicoWnnG = parent;
		/*
		 *  Keyboard[# of Languages][portrait/landscape][# of keyboard type]
		 *          [shift off/on][max # of key-modes][non-input/input]
		 */
		mKeyboard = new MyHeightKeyboard[LANG_MAX][PORTRAIT_LANDSCAPE][KEYBOARD_MAX][KEYBOARD_SHIFT_MAX][KEYMODE_JA_MAX][2];

	}

	/**
	 *
	 */
	public void checkHiddenKeyboard() {
	}

	/**
	 * Get the keyboard changed the specified shift state.
	 *
	 * @param shift     Shift state
	 * @return          Keyboard view
	 */
	protected MyHeightKeyboard getShiftChangeKeyboard(final int shift) {
		try {
			final MyHeightKeyboard[] kbd = mKeyboard[mCurrentLanguage][mDisplayMode][m12keyTable[mCurrentKeyMode]][shift][mCurrentKeyMode];

			if (!mNoInput && (kbd[1] != null)) {
				return kbd[1];
			}
			return kbd[0];
		} catch (final Exception ex) {
			return null;
		}
	}
	/**
	 * Get the keyboard changed the specified shift state.
	 *
	 * @param shift     Shift state
	 * @return          Keyboard view
	 */
	protected MyHeightKeyboard getSlideChangeKeyboard(final int slide) {
		try {
			mCurrentSlide = slide;
			final MyHeightKeyboard[] kbd = mKeyboard[mCurrentLanguage][mDisplayMode][m12keyTable[mCurrentKeyMode]][mCurrentSlide][mCurrentKeyMode];

			if (!mNoInput && (kbd[1] != null)) {
				return kbd[1];
			}
			return kbd[0];
		} catch (final Exception ex) {
			return null;
		}
	}

	/**
	 * Get the keyboard changed the specified input mode.
	 *
	 * @param mode      Input mode
	 * @return          Keyboard view
	 */
	protected MyHeightKeyboard getModeChangeKeyboard(final int mode) {
		try {
			final MyHeightKeyboard[] kbd = mKeyboard[mCurrentLanguage][mDisplayMode][m12keyTable[mCurrentKeyMode]][mShiftOn+mCurrentSlide][mode];

			if (!mNoInput && (kbd[1] != null)) {
				return kbd[1];
			}
			return kbd[0];
		} catch (final Exception ex) {
			return null;
		}
	}

	/**
	 * Get the keyboard changed the specified keyboard type
	 *
	 * @param type      Keyboard type
	 * @return          Keyboard view
	 */
	protected MyHeightKeyboard getTypeChangeKeyboard(final int type) {
		try {
			final MyHeightKeyboard[] kbd = mKeyboard[mCurrentLanguage][mDisplayMode][type][mShiftOn+mCurrentSlide][mCurrentKeyMode];

			if (!mNoInput && (kbd[1] != null)) {
				return kbd[1];
			}
			return kbd[0];
		} catch (final Exception ex) {
			return null;
		}
	}

	/**
	 * Get the keyboard when some characters are input or no character is input.
	 *
	 * @param inputed   {@code true} if some characters are inputed; {@code false} if no character is inputed.
	 * @return          Keyboard view
	 */
	protected MyHeightKeyboard getKeyboardInputed(final boolean inputed) {
		try {
			final MyHeightKeyboard[] kbd = mKeyboard[mCurrentLanguage][mDisplayMode][m12keyTable[mCurrentKeyMode]][mShiftOn+mCurrentSlide][mCurrentKeyMode];

			if (inputed && (kbd[1] != null)) {
				return kbd[1];
			}
			return kbd[0];
		} catch (final Exception ex) {
			return null;
		}
	}

	/**
	 * Change the circulative key-mode.
	 */
	protected void toggleKeyMode() {
		/* unlock shift */
		mShiftOn = KEYBOARD_SHIFT_OFF;
		mShiftLockCount = 0;
		mCurrentSlide = 0;

		/* search next defined key-mode */
		final MyHeightKeyboard[][] keyboardList = mKeyboard[mCurrentLanguage][mDisplayMode][m12keyTable[mCurrentKeyMode]][mShiftOn+mCurrentSlide];
		do {
			if (++mCurrentKeyMode >= keyboardList.length) {
				mCurrentKeyMode = 0;
			}
		} while (keyboardList[mCurrentKeyMode][0] == null);

		MyHeightKeyboard kbd;
		if (!mNoInput && (keyboardList[mCurrentKeyMode][1] != null)) {
			kbd = keyboardList[mCurrentKeyMode][1];
		} else {
			kbd = keyboardList[mCurrentKeyMode][0];
		}
		changeKeyboard(kbd);

		mWnn.onEvent(new NicoWnnGEvent(NicoWnnGEvent.CHANGE_MODE,
				NicoWnnGEvent.Mode.DEFAULT));
	}

	/**
	 * Toggle change the shift lock state.
	 */
	protected void toggleShiftLock(int sw) {
		switch (sw) {
		case 0:
			mShiftLockCount = 0;
			break;
		case 1:
			mShiftLockCount++;
			if (!mUseOnetimeShift) {
				if (mShiftLockCount == 1) {
					mShiftLockCount = 2;
				}
			}
			if (!mUseFixedShift) {
				if (mShiftLockCount == 2) {
					mShiftLockCount = 0;
				}
			}
			if (mShiftLockCount > 2) {
				mShiftLockCount = 0;
			}
			break;
		case 2:
			if (mShiftLockCount == 1) {
				mShiftLockCount = 0;
			}
			break;
		}
		if (mShiftLockCount > 0) {
			/* turn shift on */
			final MyHeightKeyboard newKeyboard = getShiftChangeKeyboard(KEYBOARD_SHIFT_ON);
			if (newKeyboard != null) {
				mShiftOn = KEYBOARD_SHIFT_ON;
				changeKeyboard(newKeyboard);
			}
			mCapsLock = true;
		} else {
			/* turn shift off */
			final MyHeightKeyboard newKeyboard = getShiftChangeKeyboard(KEYBOARD_SHIFT_OFF);
			if (newKeyboard != null) {
				mShiftOn = KEYBOARD_SHIFT_OFF;
				changeKeyboard(newKeyboard);
			}
			mCapsLock = false;
		}

		int mode = 0;
		switch (mShiftLockCount) {
		case 0:
			mode = HARD_KEYMODE_SHIFT_OFF_ALT_OFF;
			break;
		case 1:
			mode = HARD_KEYMODE_SHIFT_ON_ALT_OFF;
			break;
		case 2:
			mode = HARD_KEYMODE_SHIFT_LOCK_ALT_OFF;
			break;
		}
		updateIndicator(mode);
	}

	/**
	 * Handling Alt key event.
	 */
	protected void processAltKey() {
		/* invalid if it is not qwerty mode */
		if (m12keyTable[mCurrentKeyMode] != KEYBOARD_QWERTY) {
			return;
		}

		int mode = -1;
		final int keymode = mCurrentKeyMode;
		switch (mCurrentLanguage) {
			case LANG_EN:
				if (keymode == KEYMODE_EN_ALPHABET) {
					mode = KEYMODE_EN_NUMBER;
				} else if (keymode == KEYMODE_EN_NUMBER) {
					mode = KEYMODE_EN_ALPHABET;
				}
				break;

			case LANG_JA:
				if (keymode == KEYMODE_JA_HALF_ALPHABET) {
					mode = KEYMODE_JA_HALF_NUMBER;
				} else if (keymode == KEYMODE_JA_HALF_NUMBER) {
					mode = KEYMODE_JA_HALF_ALPHABET;
				} else if (keymode == KEYMODE_JA_FULL_ALPHABET) {
					mode = KEYMODE_JA_FULL_NUMBER;
				} else if (keymode == KEYMODE_JA_FULL_NUMBER) {
					mode = KEYMODE_JA_FULL_ALPHABET;
				}
				break;

			default:
				/* invalid */
		}

		if (mode >= 0) {
			final MyHeightKeyboard kbd = getModeChangeKeyboard(mode);
			if (kbd != null) {
				mCurrentKeyMode = mode;
				changeKeyboard(kbd);
			}
		}
	}

	/**
	 * Change the keyboard type.
	 *
	 * @param type  Type of the keyboard
	 * @see net.gorry.android.input.nicownng.DefaultSoftKeyboard#KEYBOARD_QWERTY
	 * @see net.gorry.android.input.nicownng.DefaultSoftKeyboard#KEYBOARD_12KEY
	 */
	public void changeKeyboardType(final int type) {
		/* ignore invalid parameter */
		if ((type != KEYBOARD_QWERTY) && (type != KEYBOARD_12KEY)) {
			return;
		}

		showPreview(NOT_A_KEY, -1);
		resetKeyRepeat();
		
		/* change keyboard view */
		final MyHeightKeyboard kbd = getTypeChangeKeyboard(type);
		if (kbd != null) {
			m12keyTable[mCurrentKeyMode] = type;
			changeKeyboard(kbd);
		}

		/* notice that the keyboard is changed */
		mWnn.onEvent(new NicoWnnGEvent(NicoWnnGEvent.CHANGE_MODE,
				NicoWnnGEvent.Mode.DEFAULT));
	}

	/**
	 * Change the keyboard.
	 *
	 * @param keyboard  The new keyboard
	 * @return          {@code true} if the keyboard is changed; {@code false} if not changed.
	 */
	protected boolean changeKeyboard(final MyHeightKeyboard keyboard) {
		resetKeyRepeat();

		if (true == isHideSoftKeyboardByHardKeyboard()) {
			return false;	// not change soft keyboard
		}

		if (keyboard == null) {
			return false;
		}

		showPreview(NOT_A_KEY, -1);

		if (mCurrentKeyboard != keyboard) {
			mKeyboardView.setKeyboard(keyboard);
			mKeyboardView.setShifted((mShiftOn == 0) ? false : true);
			mCurrentKeyboard = keyboard;
			mKeyboardView.setPadding(0, 0, 0, (mSpaceBelowKeyboard ? mKeyboardPaddingTable[mInputViewHeightIndex] : 0));
			initShowPreview();
			return true;
		} else {
			mKeyboardView.setShifted((mShiftOn == 0) ? false : true);
			return false;
		}
	}

	/*
	 *
	 */
	public void changeKeyMode(final int keyMode) {
	}

	/** @see net.gorry.android.input.nicownng.InputViewManager#initView */
	@Override
	public View initView(final NicoWnnG parent, final int width, final int height) {
		mWnn = parent;
		mDisplayMode =
			(parent.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
			? LANDSCAPE : PORTRAIT;
		mWnn.setOrientPrefKeyMode(mDisplayMode == PORTRAIT);
		// get hardkeyboard status
		final int hardkeyState = parent.getResources().getConfiguration().keyboard;
		final boolean hardkey  = (hardkeyState >= Configuration.KEYBOARD_QWERTY);
		final int hiddenState = parent.getResources().getConfiguration().hardKeyboardHidden;
		final boolean hidden  = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
		setHardKeyboardHidden(hidden, hardkey);

		final SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(parent);
		mInputViewHeightIndex = Integer.valueOf(mWnn.getOrientPrefString(pref, "mainview_height_mode2", "0"));
		mQwertyKanaMode = Integer.valueOf(mWnn.getOrientPrefString(pref, "qwerty_kana_mode", "0"));
		mQwertyMatrixMode = mWnn.getOrientPrefBoolean(pref, "qwerty_matrix_mode", false);
		mTsuMode = pref.getBoolean("tsu_du_ltu", false);
		mFlickNicoInput = Integer.valueOf(mWnn.getOrientPrefString(pref, "nicoflick_mode", "0"));
		mFlickSensitivity = Integer.valueOf(mWnn.getOrientPrefString(pref, "flick_sensitivity_mode", "2"));
		mFlickGuide = mWnn.getOrientPrefBoolean(pref, "flick_guide", true);
		mSpaceBelowKeyboard = mWnn.getOrientPrefBoolean(pref, "space_below_keyboard", false);
		{
			int f = Integer.valueOf(mWnn.getOrientPrefString(pref, "shiftkey_style", "0"));
			mUseOnetimeShift = false;
			mUseFixedShift = true;
			if (f == 1) {
				mUseOnetimeShift = true;
				mUseFixedShift = false;
			} else if (f == 2) {
				mUseOnetimeShift = true;
				mUseFixedShift = true;
			}
		}
		parent.reloadFlags();

		/*
		 * create keyboards & the view.
		 * To re-display the input view when the display mode is changed portrait <-> landscape,
		 * create keyboards every time.
		 */
		createKeyboards(parent);

		final String skin = pref.getString("keyboard_skin", mWnn.getResources().getString(R.string.keyboard_skin_id_default));
		final int id = parent.getResources().getIdentifier(skin, "layout", "net.gorry.android.input.nicownng");

		mKeyboardView = (KeyboardView) mWnn.getLayoutInflater().inflate(id, null);
		mKeyboardView.setOnKeyboardActionListener(this);
		mKeyboardView.setOnTouchListener(this);
		mCurrentKeyboard = null;

		mMainView = (ViewGroup) parent.getLayoutInflater().inflate(R.layout.keyboard_default_main, null);
		mSubView = (ViewGroup) parent.getLayoutInflater().inflate(R.layout.keyboard_default_sub, null);

		if (PORTRAIT == mDisplayMode) {
			mKeyboardView.setPadding(0, 0, 0, 12);
		}
		else{
			mKeyboardView.setPadding(0, 0, 0, 8);
		}
		// if (mDisplayMode == LANDSCAPE && true == mHardKeyboardHidden) {
		//     mMainView.addView(mSubView);
		// }
		if (mKeyboardView != null) {
			mMainView.addView(mKeyboardView);
		}
		// entry gesture detector
		mDetector = new GestureDetector(this);
		if (NICOFLICK_1STROKE == mFlickNicoInput) {
			mDetector.setIsLongpressEnabled(true);
			mIsActiveLongPress = true;
		}
		else{
			// mDetector.setIsLongpressEnabled(false);
			mDetector.setIsLongpressEnabled(true);
			mIsActiveLongPress = false;
		}
		mMetrics.setToDefaults();
		//changeKeyboardKeyHeight();
		return mMainView;
	}

	/**
	 * Update the SHIFT/ALT keys indicator.
	 *
	 * @param mode  The state of SHIFT/ALT keys.
	 */
	public void updateIndicator(final int mode) {
		/*
		final Resources res = mWnn.getResources();
		final TextView text1 = (TextView)mSubView.findViewById(R.id.shift);
		final TextView text2 = (TextView)mSubView.findViewById(R.id.alt);

		switch (mode) {
			case HARD_KEYMODE_SHIFT_OFF_ALT_OFF:
				text1.setTextColor(res.getColor(R.color.indicator_textcolor_caps_off));
				text2.setTextColor(res.getColor(R.color.indicator_textcolor_alt_off));
				text1.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				text2.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				break;
			case HARD_KEYMODE_SHIFT_ON_ALT_OFF:
				text1.setTextColor(res.getColor(R.color.indicator_textcolor_caps_on));
				text2.setTextColor(res.getColor(R.color.indicator_textcolor_alt_off));
				text1.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				text2.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				break;
			case HARD_KEYMODE_SHIFT_LOCK_ALT_OFF:
				text1.setTextColor(res.getColor(R.color.indicator_textcolor_caps_lock));
				text2.setTextColor(res.getColor(R.color.indicator_textcolor_alt_off));
				text1.setBackgroundColor(res.getColor(R.color.indicator_background_lock_caps));
				text2.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				break;
			case HARD_KEYMODE_SHIFT_OFF_ALT_ON:
				text1.setTextColor(res.getColor(R.color.indicator_textcolor_caps_off));
				text2.setTextColor(res.getColor(R.color.indicator_textcolor_alt_on));
				text1.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				text2.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				break;
			case HARD_KEYMODE_SHIFT_OFF_ALT_LOCK:
				text1.setTextColor(res.getColor(R.color.indicator_textcolor_caps_off));
				text2.setTextColor(res.getColor(R.color.indicator_textcolor_alt_lock));
				text1.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				text2.setBackgroundColor(res.getColor(R.color.indicator_background_lock_alt));
				break;
			case HARD_KEYMODE_SHIFT_ON_ALT_ON:
				text1.setTextColor(res.getColor(R.color.indicator_textcolor_caps_on));
				text2.setTextColor(res.getColor(R.color.indicator_textcolor_alt_on));
				text1.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				text2.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				break;
			case HARD_KEYMODE_SHIFT_ON_ALT_LOCK:
				text1.setTextColor(res.getColor(R.color.indicator_textcolor_caps_on));
				text2.setTextColor(res.getColor(R.color.indicator_textcolor_alt_lock));
				text1.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				text2.setBackgroundColor(res.getColor(R.color.indicator_background_lock_alt));
				break;
			case HARD_KEYMODE_SHIFT_LOCK_ALT_ON:
				text1.setTextColor(res.getColor(R.color.indicator_textcolor_caps_lock));
				text2.setTextColor(res.getColor(R.color.indicator_textcolor_alt_on));
				text1.setBackgroundColor(res.getColor(R.color.indicator_background_lock_caps));
				text2.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				break;
			case HARD_KEYMODE_SHIFT_LOCK_ALT_LOCK:
				text1.setTextColor(res.getColor(R.color.indicator_textcolor_caps_lock));
				text2.setTextColor(res.getColor(R.color.indicator_textcolor_alt_lock));
				text1.setBackgroundColor(res.getColor(R.color.indicator_background_lock_caps));
				text2.setBackgroundColor(res.getColor(R.color.indicator_background_lock_alt));
				break;
			default:
				text1.setTextColor(res.getColor(R.color.indicator_textcolor_caps_off));
				text2.setTextColor(res.getColor(R.color.indicator_textcolor_alt_off));
				text1.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				text2.setBackgroundColor(res.getColor(R.color.indicator_textbackground_default));
				break;
		}
		*/
		if (mCurrentKeyboard == null) {
			return;
		}
		switch (mode) {
		case HARD_KEYMODE_SHIFT_OFF_ALT_OFF:
		case HARD_KEYMODE_SHIFT_OFF_ALT_ON:
		case HARD_KEYMODE_SHIFT_OFF_ALT_LOCK:
			mCurrentKeyboard.setShiftKeyIconLock(false);
			break;
		case HARD_KEYMODE_SHIFT_ON_ALT_OFF:
		case HARD_KEYMODE_SHIFT_ON_ALT_ON:
		case HARD_KEYMODE_SHIFT_ON_ALT_LOCK:
			mCurrentKeyboard.setShiftKeyIconLock(false);
			break;
		case HARD_KEYMODE_SHIFT_LOCK_ALT_OFF:
		case HARD_KEYMODE_SHIFT_LOCK_ALT_ON:
		case HARD_KEYMODE_SHIFT_LOCK_ALT_LOCK:
			mCurrentKeyboard.setShiftKeyIconLock(true);
			break;
		default:
			mCurrentKeyboard.setShiftKeyIconLock(false);
			break;
		}
		return;
	}

	/** @see net.gorry.android.input.nicownng.InputViewManager#getCurrentView */
	@Override
	public View getCurrentView() {
		if (mCurrentKeyboard == null) {
			return null;
		}
		return mMainView;
	}

	/** @see net.gorry.android.input.nicownng.InputViewManager#onUpdateState */
	@Override
	public void onUpdateState(final NicoWnnG parent) {
		try {
			if (parent.mComposingText.size(1) == 0) {
				if (!mNoInput) {
					/* when the mode changed to "no input" */
					mNoInput = true;
					final MyHeightKeyboard newKeyboard = getKeyboardInputed(false);
					if (mCurrentKeyboard != newKeyboard) {
						changeKeyboard(newKeyboard);
					}
				}
			} else {
				if (mNoInput) {
					/* when the mode changed to "input some characters" */
					mNoInput = false;
					final MyHeightKeyboard newKeyboard = getKeyboardInputed(true);
					if (mCurrentKeyboard != newKeyboard) {
						changeKeyboard(newKeyboard);
					}
				}
			}
		} catch (final Exception ex) {
		}
	}

	/** @see net.gorry.android.input.nicownng.InputViewManager#setPreferences */
	@Override
	public void setPreferences(final SharedPreferences pref, final EditorInfo editor) {

		/* vibrator */
		try {
			if (pref.getBoolean("key_vibration", false)) {
				mVibrator = (Vibrator)mWnn.getSystemService(Context.VIBRATOR_SERVICE);
			} else {
				mVibrator = null;
			}
		} catch (final Exception ex) {
			Log.d("NicoWnnG", "NO VIBRATOR");
		}

		/* sound */
		try {
			if (mSoundPool != null) {
				mSoundPool.release();
				mSoundPool = null;
				mSound = 0;
			}
			if (pref.getBoolean("key_sound", false)) {
				mSoundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
				try {
					String path = Environment.getExternalStorageDirectory().toString();
					path += "/nicoWnnG/type.wav";
					mSound = mSoundPool.load(path, 1);
				} catch (final Exception e) {
					//
				}
				if (mSound == 0) {
					try {
						String path = Environment.getExternalStorageDirectory().toString();
						path += "/nicoWnnG/type.ogg";
						mSound = mSoundPool.load(path, 1);
					} catch (final Exception e) {
						//
					}
				}
				if (mSound == 0) {
					mSound = mSoundPool.load(mWnn, R.raw.type, 1);
				}
				final int vol = Integer.valueOf(pref.getString("key_sound_vol", "0"));
				switch (vol) {
					default:
					case 0:
						mSoundVolume = 1.0F;
						break;
					case 1:
						mSoundVolume = 0.5F;
						break;
					case 2:
						mSoundVolume = 0.25F;
						break;
				}
			}
		} catch (final Exception ex) {
			Log.d("NicoWnnG", "NO SOUND");
		}
		mFlickNicoInput = Integer.valueOf(mWnn.getOrientPrefString(pref, "nicoflick_mode", "0"));
		mFlickSensitivity = Integer.valueOf(mWnn.getOrientPrefString(pref, "flick_sensitivity_mode", "2"));
		mFlickGuide = mWnn.getOrientPrefBoolean(pref, "flick_guide", true);
		if (null != mDetector) {
			if (NICOFLICK_1STROKE == mFlickNicoInput) {
				mDetector.setIsLongpressEnabled(true);
				mIsActiveLongPress = true;
			}
			else{
				mDetector.setIsLongpressEnabled(true);
				// mDetector.setIsLongpressEnabled(false);
				mIsActiveLongPress = false;
			}
		}
		/* pop-up preview */
		mKeyboardView.setPreviewEnabled(false);
		mShowPreview = pref.getBoolean("popup_preview", true);
	}

	/** @see net.gorry.android.input.nicownng.InputViewManager#closing */
	@Override
	public void closing() {
		if (mKeyboardView != null) {
			mKeyboardView.closing();
		}
		mDisableKeyInput = true;
		resetKeyRepeat();
		showPreview(NOT_A_KEY, -1);
	}

	public String[] convertFlickToKeyString(int flickdir) {
		return null;
	}

	/***********************************************************************
	 * onKeyboardActionListener
	 ***********************************************************************/
	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#onKey */
	@Override
	public void onKey(final int primaryCode, final int[] keyCodes) {
	}

	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#swipeRight */
	@Override
	public void swipeRight() {
	}
	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#swipeLeft */
	@Override
	public void swipeLeft() {
	}
	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#swipeDown */
	@Override
	public void swipeDown() {
	}
	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#swipeUp */
	@Override
	public void swipeUp() {
	}
	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#onRelease */
	@Override
	public void onRelease(final int primaryCode) {
		resetKeyRepeat();
		showPreview(NOT_A_KEY, -1);
	}

	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#onPress */
	@Override
	public void onPress(final int primaryCode) {
		mPressedKeyCode = primaryCode;
		/* key click sound & vibration */
		if (mSoundPool != null) {
			try { mSoundPool.play(mSound, mSoundVolume, mSoundVolume, 0, 0, 1); } catch (final Exception ex) { }
		}
		if (mVibrator != null) {
			try { mVibrator.vibrate(30); } catch (final Exception ex) { }
		}
		// check flick
		if (false == mNicoFirst) {
			mPrevInputKeyCode = primaryCode;
		}
		switch (primaryCode) {
		case KEYCODE_JP12_UP:
		case KEYCODE_JP12_DOWN:
		case KEYCODE_JP12_LEFT:
		case KEYCODE_JP12_RIGHT:
		case KEYCODE_JP12_BACKSPACE:
		case KEYCODE_QWERTY_BACKSPACE:
		case KEYCODE_JP12_EMOJI:
		case KEYCODE_QWERTY_EMOJI:
		case KEYCODE_EISU_KANA:
			setKeyRepeat(primaryCode);
			break;
		}
		showPreview(mKeyIndex, -1);
	}

	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#onText */
	@Override
	public void onText(final CharSequence text) {}

	/**
	 * Get current key mode.
	 *
	 * @return Current key mode
	 */
	public int getKeyMode() {
		return mCurrentKeyMode;
	}

	/**
	 * Get current keyboard type.
	 *
	 * @return Current keyboard type
	 */
	public int getKeyboardType() {
		return m12keyTable[mCurrentKeyMode];
	}

	/**
	 * Set the H/W keyboard's state.
	 *
	 * @param hidden {@code true} if hidden.
	 */
	public void setHardKeyboardHidden(final boolean hidden, final boolean hardkey) {
		mHardKeyboardHidden = hidden;
		mIsHardKeyboard   = hardkey;
	}
	/**
	 *
	 */
	public boolean isHideSoftKeyboardByHardKeyboard() {
/*
		if (mDisplayMode == DefaultSoftKeyboard.PORTRAIT) {
			return false;
		}
*/
		if (1 == mHiddenSoftKeyboard) {
			return true;
		}
		if (2 == mHiddenSoftKeyboard) {
			return false;
		}

		if (false == mIsHardKeyboard) {
			return false;
		}
		if (true == mHardKeyboardHidden) {
			return false;
		}
		return true;
	}

	/**
	 * Get current keyboard view.
	 */
	public View getKeyboardView() {
		return mKeyboardView;
	}

	/**
	 * reset kicotouch keyboard
	 */
	public void resetNicoKeyboard() {
		// no operation
	}

	/**
	 * get mHiddenSoftKeyboard
	 */
	public int getHiddenSoftKeyboard() {
		return mHiddenSoftKeyboard;
	}

	/**
	 * Reset the current keyboard
	 */
	public void resetCurrentKeyboard() {
		closing();
		final MyHeightKeyboard keyboard = mCurrentKeyboard;
		mCurrentKeyboard = null;
		changeKeyboard(keyboard);
	}
	/**
	 * Change to the next input mode
	 */
	public void nextKeyMode() {
	}
	/**
	 * set default Keyboard
	 */
	public void setDefaultKeyboard() {
		changeKeyMode(KEYMODE_JA_FULL_HIRAGANA);
	}
	/**
	 * get table index
	 */
	public int getTableIndex(final int keyCode) {
		return keyCode;
	}

	/******************************************************************************************/
	/******************************************************************************************/
	/***
	 * gesture control
	 */
	@Override
	public boolean onTouch(final View v, final MotionEvent event) {
		int touchX = (int) event.getX() - mKeyboardView.getPaddingLeft();
		int touchY = (int) event.getY() - mKeyboardView.getPaddingTop();
		mKeyIndex = getKeyIndices(touchX, touchY, null);

		mDetector.onTouchEvent(event);
		return false;
	}
	/**
	 * OnGestureListener functions
	 */
	@Override
	public boolean onDown(final MotionEvent ev){
		//Log.d("NicoWnnG", "onDown");
		mStartX = ev.getRawX();
		mStartY = ev.getRawY();
		mGestureX = mStartX;
		mGestureY = mStartY;
		mIsLongPress = false;
		mLastFlingDir = -1;
		mFirstKey = null;
		//Log.d("NicoWnnG", "mFirstKey clear");
		return true;
	}
	@Override
	public boolean onFling(final MotionEvent ev1, final MotionEvent ev2, final float getX, final float getY){
		//Log.d("NicoWnnG", "onFling: x="+getX+", y="+getY);
/*
		final int flingdir = checkFlickKeyCode(true, true);
		if (mLastFlingDir != flingdir) {
			final int flingkeycode = checkFlickKeyCode(true, false);
			showPreview(mKeyIndex, flingdir);
		}
*/
		return true;
	}
	
	@Override
	public void onLongPress(final MotionEvent ev){
		if (mFirstKey != null) {
			switch (mFirstKey.codes[0]) {
			case KEYCODE_JP12_RIGHT:
			case KEYCODE_JP12_LEFT:
			case KEYCODE_JP12_UP:
			case KEYCODE_JP12_DOWN:
				return;
			}
		}
		// showPreview(NOT_A_KEY, -1);
		mIsLongPress = true;
		/*
		switch (mPressedKeyCode) {
			case KEYCODE_JP12_EMOJI:
			case KEYCODE_QWERTY_EMOJI:
			case KEYCODE_EISU_KANA:
				final String str = mWnn.getComposingText(ComposingText.LAYER2);
				mWnn.onEvent(new NicoWnnGEvent(NicoWnnGEvent.INPUT_KEY, new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK)));
				mWnn.invokeMushroom(str);
				break;
		}
		*/
	}
	@Override
	public boolean onScroll(final MotionEvent ev1, final MotionEvent ev2, final float distX, final float distY){
		mGestureX = ev2.getRawX();
		mGestureY = ev2.getRawY();
		// Log.d("NicoWnnG", "onScroll: x="+mGestureX+", y="+mGestureY);
		final int flingdir = checkFlickKeyCode(true, true);
		if (mLastFlingDir != flingdir) {
			showPreview(mKeyIndex, flingdir);
		} else if (flingdir == -1) {
			showPreview(mKeyIndex, flingdir);
		}

		return true;
	}
	@Override
	public void    onShowPress(final MotionEvent ev){
	}
	@Override
	public boolean onSingleTapUp(final MotionEvent ev){
		return true;
	}
	/**
	 * calc. flick keycode
	 */
	public int checkFlickKeyCode(final boolean enablesharp, final boolean test) {
		boolean isArrowKey = false;

		switch (mPrevInputKeyCode) {
		case KEYCODE_JP12_RIGHT:
		case KEYCODE_JP12_LEFT:
		case KEYCODE_JP12_UP:
		case KEYCODE_JP12_DOWN:
			break;

		default:
			if (NICOFLICK_NONE == mFlickNicoInput) {
				return -1;
			}
			if (!(
					(mCurrentKeyMode == KEYMODE_JA_FULL_NICO) ||
					(mCurrentKeyMode == KEYMODE_JA_FULL_NICO_KATAKANA) ||
					(mCurrentKeyMode == KEYMODE_JA_HALF_NICO_KATAKANA) ||
					(mCurrentKeyMode == KEYMODE_JA_HALF_ALPHABET) ||
					(mCurrentKeyMode == KEYMODE_JA_FULL_ALPHABET) ||
					(mCurrentKeyMode == KEYMODE_JA_HALF_KATAKANA) ||
					(mCurrentKeyMode == KEYMODE_JA_FULL_HIRAGANA) ||
					(mCurrentKeyMode == KEYMODE_JA_FULL_KATAKANA)
			)) {
				return -1;
			}
		}
		if (true == mNicoFirst) {
			return -1;
		}
		// check 12key
		switch (mPrevInputKeyCode) {
		case KEYCODE_JP12_RIGHT:
		case KEYCODE_JP12_LEFT:
		case KEYCODE_JP12_UP:
		case KEYCODE_JP12_DOWN:
			isArrowKey = true;
			break;
		case KEYCODE_JP12_1:	// A
		case KEYCODE_JP12_2:	// Ka
		case KEYCODE_JP12_3:	// Sa
		case KEYCODE_JP12_4:	// Ta
		case KEYCODE_JP12_5:	// Na
		case KEYCODE_JP12_6:	// Ha
		case KEYCODE_JP12_7:	// Ma
		case KEYCODE_JP12_8:	// Ya
		case KEYCODE_JP12_9:	// Ra
		case KEYCODE_JP12_0:	// Wa
			break;
		case KEYCODE_JP12_SHARP:
			if (false == enablesharp) {
				return -1;
			}
			break;
		default:
			return -1;
		}
		/**
		 * creation flick keys
		 */
		float calcx, calcy;
		//calcx = (mGestureX - mStartX) * mMetrics.scaledDensity;
		//calcy = (mGestureY - mStartY) * mMetrics.scaledDensity;
		calcx = (mGestureX - mStartX);
		calcy = (mGestureY - mStartY);
		//Log.d("NicoWnnG", "checkFlick " + calcx + "/" + calcy);

		final float length = FloatMath.sqrt((calcx * calcx) + (calcy * calcy));
		double rotation;
		rotation = Math.atan2(calcy, calcx);
		// change radian -> degree
		float getrot = (float)(rotation / Math.PI * 180.0);
		if (getrot < 0.0f) {
			getrot = 360.0f + getrot;
		}
		// change rotate -> keycode
		int keycode = -1;
		if (length < flickSensitivityModeTable[mFlickSensitivity]) {
			if (mIsActiveLongPress) {
				if (!mIsLongPress) {
					keycode = 0;
				}
			}
		} else {
			if ((getrot >= 45.0f) && (getrot < 135.0f)) {
				keycode = 1;
			}
			else if ((getrot >= 135.0f) && (getrot < 225.0f)) {
				keycode = 2;
			}
			else if ((getrot >= 225.0f) && (getrot < 315.0f)) {
				keycode = 3;
			}
			else {
				keycode = 4;
			}
		}
		if (test) {
			return keycode;
		}
		if (-1 != keycode) {
			if (isArrowKey) {
				switch (keycode) {
				case 0:
					keycode = mPrevInputKeyCode;
					break;
				case 1:
					keycode = KEYCODE_JP12_DOWN;
					break;
				case 2:
					keycode = KEYCODE_JP12_LEFT;
					break;
				case 3:
					keycode = KEYCODE_JP12_UP;
					break;
				case 4:
					keycode = KEYCODE_JP12_RIGHT;
					break;
				}
				resetKeyRepeat();
				setKeyRepeat(keycode);
				mPrevInputKeyCode = keycode;
			} else {
				keycode = convertModeFlick(getTableIndex(mPrevInputKeyCode), keycode);
			}
		}
		return keycode;
	}
	/**
	 *
	 */
	public int convertModeFlick(final int prev, final int key) {
		return key;
	}
	/**
	 *
	 */
	public boolean isLongPress() {
		return mIsLongPress;
	}

	//

	private static final int NOT_A_KEY = -1;

	private TextView mPreviewText;
	private PopupWindow mPreviewPopup;
	private int mPreviewTextSizeLarge;
	private int mPreviewOffset;
	private int mPreviewHeight;
	private int mKeyIndex = NOT_A_KEY;
	private int mCurrentKeyIndex = NOT_A_KEY;
	private Key[] mKeys;
	private boolean mShowPreview = true;
	private boolean mPreviewCentered = false;
	private int mKeyTextSize;
	private int mPopupPreviewX;
	private int mPopupPreviewY;
	private int mMiniKeyboardOffsetX;
	private int mMiniKeyboardOffsetY;
	private int mWindowY;
	private int mProximityThreshold;
	private int mLastFlingDir;
	private Key mFirstKey;

	private static final int[] mPreviewBackground = {
		R.drawable.keyboard_key_feedback_background,
		R.drawable.keyboard_key_feedback_d_background,
		R.drawable.keyboard_key_feedback_l_background,
		R.drawable.keyboard_key_feedback_u_background,
		R.drawable.keyboard_key_feedback_r_background,
	};
	private static final Drawable[] mCursorIcon = new Drawable[5];

	private static final int[] mCursorIconResId = {
		0,
		R.drawable.key_qwerty_down_b,
		R.drawable.key_qwerty_left_b,
		R.drawable.key_qwerty_up_b,
		R.drawable.key_qwerty_right_b,
	};

	private int[] mOffsetInWindow = new int[2];
	private int[] mWindowLocation = new int[2];

	private static int MAX_NEARBY_KEYS = 12;
	private int[] mDistances = new int[MAX_NEARBY_KEYS];

	private static final int MSG_SHOW_PREVIEW = 1;
	private static final int MSG_REMOVE_PREVIEW = 2;

	private static final int DELAY_BEFORE_PREVIEW = 0;
	private static final int DELAY_AFTER_PREVIEW = 100;
	private static final int DELAY_AFTER_PREVIEW_FLICK = 250;
	private static final int DEBOUNCE_TIME = 70;

	Handler mShowPreviewHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
				case MSG_SHOW_PREVIEW:
					showKey(msg.arg1, msg.arg2);
					break;
				case MSG_REMOVE_PREVIEW:
					mPreviewText.setVisibility(View.INVISIBLE);
					break;
			}
		}
	};

	private CharSequence adjustCase(CharSequence label) {
		if (mCurrentKeyboard.isShifted() && label != null && label.length() < 3
				&& Character.isLowerCase(label.charAt(0))) {
			label = label.toString().toUpperCase();
		}
		return label;
	}

	private CharSequence getPreviewText(Key key) {
		return adjustCase(key.label);
	}

	private void computeProximityThreshold(MyHeightKeyboard keyboard) {
		if (keyboard == null) return;
		final Key[] keys = mKeys;
		if (keys == null) return;
		int length = keys.length;
		int dimensionSum = 0;
		for (int i = 0; i < length; i++) {
			Key key = keys[i];
			dimensionSum += Math.min(key.width, key.height) + key.gap;
		}
		if (dimensionSum < 0 || length == 0) return;
		mProximityThreshold = (int) (dimensionSum * 1.4f / length);
		mProximityThreshold *= mProximityThreshold; // Square it
	}

	private void initShowPreview() {
		Context context = mKeyboardView.getContext();
		
		List<Key> keys = mCurrentKeyboard.getKeys();
		mKeys = keys.toArray(new Key[keys.size()]);

		computeProximityThreshold(mCurrentKeyboard);
		
		if (mPreviewPopup == null) {
			mPreviewPopup = new PopupWindow(context);
			LayoutInflater inflate = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
			mPreviewText = (TextView) inflate.inflate(R.layout.keyboard_key_preview, null);
		}
		mPreviewTextSizeLarge = (int)mPreviewText.getTextSize();
		mPreviewPopup.setContentView(mPreviewText);
		mPreviewPopup.setBackgroundDrawable(null);

		Resources res = mWnn.getResources();
		mKeyTextSize = res.getDimensionPixelSize(R.dimen.key_text_size);
		mPreviewHeight = res.getDimensionPixelSize(R.dimen.key_preview_height);

		mPreviewPopup.setTouchable(false);

		for (int i=0; i<mCursorIcon.length; i++) {
			int id = mCursorIconResId[i];
			if (id > 0) {
				Drawable icon = res.getDrawable(id);
				icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
				mCursorIcon[i] = icon;
			}
		}

		mLastFlingDir = -1;
	}

	public void fadePreview() {
		showPreview(NOT_A_KEY, -1);
	}

	private void showPreview(int keyIndex, int flingDir) {
		int oldFlingDir = mLastFlingDir;
		int oldKeyIndex = mCurrentKeyIndex;
		final PopupWindow previewPopup = mPreviewPopup;
		
		mLastFlingDir = flingDir;
		mCurrentKeyIndex = keyIndex;

		// Release the old key and press the new key
		final Key[] keys = mKeys;
		/*
		if (oldKeyIndex != mCurrentKeyIndex) {
			if ((oldKeyIndex != NOT_A_KEY) && (keys.length > oldKeyIndex)) {
				keys[oldKeyIndex].onReleased((mCurrentKeyIndex == NOT_A_KEY));
				// invalidateKey(oldKeyIndex);
			}
			if ((mCurrentKeyIndex != NOT_A_KEY) && (keys.length > mCurrentKeyIndex)) {
				keys[mCurrentKeyIndex].onPressed();
				// invalidateKey(mCurrentKeyIndex);
			}
		}
		*/
		// If key changed and preview is on ...
		if (((oldKeyIndex != mCurrentKeyIndex) || (oldFlingDir != mLastFlingDir)) && (null != previewPopup) && mShowPreview) {
			mShowPreviewHandler.removeMessages(MSG_SHOW_PREVIEW);
			if (previewPopup.isShowing()) {
				if (keyIndex == NOT_A_KEY) {
					mShowPreviewHandler.sendMessageDelayed(
							mShowPreviewHandler.obtainMessage(MSG_REMOVE_PREVIEW), 
							((oldFlingDir>0) ? DELAY_AFTER_PREVIEW_FLICK : DELAY_AFTER_PREVIEW)
					);
				}
			}
			if (keyIndex != NOT_A_KEY) {
				if (previewPopup.isShowing() && mPreviewText.getVisibility() == View.VISIBLE) {
					// Show right away, if it's already visible and finger is moving around
					showKey(keyIndex, flingDir);
				} else {
					mShowPreviewHandler.sendMessageDelayed(
							mShowPreviewHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, flingDir), 
							DELAY_BEFORE_PREVIEW
					);
				}
			}
		}
	}
	
	private static final String[] mCursorString = {"CURSOR"};
	private void showKey(final int keyIndex, final int flingDir) {
		final PopupWindow previewPopup = mPreviewPopup;
		final Key[] keys = mKeys;
		if (keyIndex < 0 || keyIndex >= mKeys.length) return;
		Key key = keys[keyIndex];
		if (mFirstKey == null) {
			mFirstKey = key;
			// Log.d("NicoWnnG", "mFirstKey="+mFirstKey.codes[0]);
		}
		String[] keyString = null;
		if (flingDir >= 0) {
			// Log.d("NicoWnnG", "flingDir="+flingDir);
			int dir2 = -1;
			switch (mPrevInputKeyCode) {
			case KEYCODE_JP12_RIGHT:
			case KEYCODE_JP12_LEFT:
			case KEYCODE_JP12_UP:
			case KEYCODE_JP12_DOWN:
				if (flingDir > 0) {
					keyString = mCursorString;
				}
				break;
			default:
				dir2 = convertModeFlick(getTableIndex(mPrevInputKeyCode), flingDir);
				if (dir2 >= 0) {
					keyString = convertFlickToKeyString(dir2);
				}
				break;
			}
			mPreviewText.setBackgroundResource(mPreviewBackground[flingDir]);
		} else {
			mPreviewText.setBackgroundResource(R.drawable.keyboard_key_feedback_background);
		}
		if ((null == keyString) && (key.icon != null)) {
			mPreviewText.setCompoundDrawables(
			  null, null, null, 
			  ((key.iconPreview) != null ? key.iconPreview : key.icon)
			);
			mPreviewText.setText(null);
		} else {
			if (keyString == mCursorString) {
				mPreviewText.setCompoundDrawables(
				  null, null, null, 
				  mCursorIcon[flingDir]
				);
				mPreviewText.setText(null);
			} else {
				mPreviewText.setCompoundDrawables(null, null, null, null);
				if (null != keyString) {
					mPreviewText.setText(keyString[0]);
					mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSizeLarge);
					mPreviewText.setTypeface(Typeface.DEFAULT);
				} else {
					mPreviewText.setText(getPreviewText(key));
					if (key.label.length() > 1 && key.codes.length < 2) {
						mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyTextSize);
						mPreviewText.setTypeface(Typeface.DEFAULT_BOLD);
					} else {
						mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSizeLarge);
						mPreviewText.setTypeface(Typeface.DEFAULT);
					}
				}
			}
		}
		mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 
				MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
		int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), mFirstKey.width 
				+ mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
		final int popupHeight = mPreviewHeight;
		LayoutParams lp = mPreviewText.getLayoutParams();
		if (lp != null) {
			lp.width = popupWidth;
			lp.height = popupHeight;
		}
		if (!mPreviewCentered) {
			mPopupPreviewX = mFirstKey.x - mPreviewText.getPaddingLeft() + mKeyboardView.getPaddingLeft();
			mPopupPreviewY = mFirstKey.y - (keys[0].height/2) - popupHeight + mPreviewOffset;
		} else {
			// TODO: Fix this if centering is brought back
			mPopupPreviewX = 160 - mPreviewText.getMeasuredWidth() / 2;
			mPopupPreviewY = - mPreviewText.getMeasuredHeight();
		}
		mShowPreviewHandler.removeMessages(MSG_REMOVE_PREVIEW);
		mKeyboardView.getLocationInWindow(mOffsetInWindow);
		mOffsetInWindow[0] += mMiniKeyboardOffsetX; // Offset may be zero
		mOffsetInWindow[1] += mMiniKeyboardOffsetY; // Offset may be zero
		mKeyboardView.getLocationOnScreen(mWindowLocation);
		mWindowY = mWindowLocation[1];
		// Set the preview background state
		/*
		mPreviewText.getBackground().setState(
				key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
		*/
		mPopupPreviewX += mOffsetInWindow[0];
		mPopupPreviewY += mOffsetInWindow[1];

		// If the popup cannot be shown above the key, put it on the side
		if (mPopupPreviewY + mWindowY < 0) {
			// If the key you're pressing is on the left side of the keyboard, show the popup on
			// the right, offset by enough to see at least one key to the left/right.
			if (mFirstKey.x + key.width <= mKeyboardView.getWidth() / 2) {
				mPopupPreviewX += (int) (key.width * 2.5);
			} else {
				mPopupPreviewX -= (int) (key.width * 2.5);
			}
			mPopupPreviewY += popupHeight;
		}

		if (previewPopup.isShowing()) {
			previewPopup.update(mPopupPreviewX, mPopupPreviewY,
					popupWidth, popupHeight);
		} else {
			previewPopup.setWidth(popupWidth);
			previewPopup.setHeight(popupHeight);
			previewPopup.showAtLocation(
					mKeyboardView, Gravity.NO_GRAVITY, 
					mPopupPreviewX, mPopupPreviewY
			);
		}
		mPreviewText.setVisibility(View.VISIBLE);
	}

	private int getKeyIndices(int x, int y, int[] allKeys) {
		final Key[] keys = mKeys;
		int primaryIndex = NOT_A_KEY;
		int closestKey = NOT_A_KEY;
		int closestKeyDist = mProximityThreshold + 1;
		java.util.Arrays.fill(mDistances, Integer.MAX_VALUE);
		int [] nearestKeyIndices = mCurrentKeyboard.getNearestKeys(x, y);
		final int keyCount = nearestKeyIndices.length;
		for (int i = 0; i < keyCount; i++) {
			final Key key = keys[nearestKeyIndices[i]];
			int dist = 0;
			boolean isInside = key.isInside(x,y);
			if (isInside) {
				primaryIndex = nearestKeyIndices[i];
			}

			if (((mKeyboardView.isProximityCorrectionEnabled() 
					&& (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold) 
					|| isInside)
					&& key.codes[0] > 32) {
				// Find insertion point
				final int nCodes = key.codes.length;
				if (dist < closestKeyDist) {
					closestKeyDist = dist;
					closestKey = nearestKeyIndices[i];
				}
				
				if (allKeys == null) continue;
				
				for (int j = 0; j < mDistances.length; j++) {
					if (mDistances[j] > dist) {
						// Make space for nCodes codes
						System.arraycopy(mDistances, j, mDistances, j + nCodes,
								mDistances.length - j - nCodes);
						System.arraycopy(allKeys, j, allKeys, j + nCodes,
								allKeys.length - j - nCodes);
						for (int c = 0; c < nCodes; c++) {
							allKeys[j + c] = key.codes[c];
							mDistances[j + c] = dist;
						}
						break;
					}
				}
			}
		}
		if (primaryIndex == NOT_A_KEY) {
			primaryIndex = closestKey;
		}
		return primaryIndex;
	}




}
/**************** end of file ****************/
