/*
 * 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 com.hiroshica.android.input.nicownn2;

import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.MotionEvent;
import android.widget.TextView;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.Keyboard.Key;
import android.inputmethodservice.KeyboardView;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.content.res.*;
import android.os.Vibrator;
import android.media.MediaPlayer;
import android.content.Context;

import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.util.DisplayMetrics;
import android.util.FloatMath;

import java.lang.Math;
import java.util.HashMap;
import java.util.Iterator;

import android.util.Log;


/**
 * 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;
    
	/* 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;
    
	/** OpenWnn instance which hold this software keyboard*/
	protected NicoWnn      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 Keyboard 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;

	/** 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;
	public static final HashMap<String, Integer> flickModeTable = new HashMap<String, Integer>() {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		{
			put("none_flick", 0);
			put("normal_stroke", 1);
			put("nico_stroke", 2);
		}};
	
	/** 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 HashMap<String, Integer> flickSensivityModeTable = new HashMap<String, Integer>() {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		{
			put("sensitivity_value_0", 15);
			put("sensitivity_value_1", 20);
			put("sensitivity_value_2", 25);
			put("sensitivity_value_3", 30);
			put("sensitivity_value_4", 35);
		}};
	public float mFlickSensivity = 25.0f;
	/**
	 * input keyboard key height
	 */
	public static final HashMap<String, Integer> inputViewKeyHeight = new HashMap<String, Integer>() {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		{
			put("mainview_value_0", 0);
			put("mainview_value_1", 1);
			put("mainview_value_2", 2);
		}};
	public int mInputViewHeightIndex = 0;
	public static int inputViewDataTable[][] = {
		{ 48, 38 },
		{ 54, 44 },
		{ 60, 50 },
	};

	/**
	 * Keyboard surfaces 
	 * <br>
	 * Keyboard[language][portrait/landscape][keyboard type][shift off/on][key-mode]
	 */
	public    Keyboard[][][][][][] mKeyboard;
	protected boolean              mCreateKeyboard = 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;

	/* 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;

	/* 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;
	/** State of the shift key */
	protected int mShiftOn = 0;
	/** Shift key off */
	public static final int KEYBOARD_SHIFT_OFF = 0;
	/** Shift key on */
	public static final int KEYBOARD_SHIFT_ON  = 1;

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

	/* 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 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;

	/* 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 mIsQwertyKeyboard   = false;
	protected boolean mHardKeyboardHidden = true;
	/** option softkeyboard on/off **/
	protected boolean mHiddenSoftKeyboard = false;

	/**
	 * 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;
    
	/** MediaPlayer for key click sound */
	protected MediaPlayer mSound = null;
    
	/** Key toggle cycle table currently using */
	protected String[] mCurrentCycleTable;

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

	/**
	 * Create keyboard views
	 *
	 * @param parent   OpenWnn using the keyboards.
	 */
	protected void createKeyboards(NicoWnn parent) {
		/*
		 *  Keyboard[# of Languages][portrait/landscape][# of keyboard type]
		 *          [shift off/on][max # of key-modes][non-input/input]
		 */
		mKeyboard = new Keyboard[3][2][4][2][9][2];
	}

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

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

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

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

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

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

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

			if (!mNoInput && kbd[1] != null) {
				return kbd[1];
			}
			return kbd[0];
		} catch (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 Keyboard getKeyboardInputed(boolean inputed) {
		try {
			Keyboard[] kbd = mKeyboard[mCurrentLanguage][mDisplayMode][mCurrentKeyboardType][mShiftOn+mCurrentSlide][mCurrentKeyMode];

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

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

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

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

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

	/**
	 * Toggle change the shift lock state.
	 */
	protected void toggleShiftLock() {
		if (mShiftOn == 0) {
			/* turn shift on */
			Keyboard newKeyboard = getShiftChangeKeyboard(KEYBOARD_SHIFT_ON);
			if (newKeyboard != null) {
				mShiftOn = 1;
				changeKeyboard(newKeyboard);
			}
			mCapsLock = true;
		} else {
			/* turn shift off */
			Keyboard newKeyboard = getShiftChangeKeyboard(KEYBOARD_SHIFT_OFF);
			if (newKeyboard != null) {
				mShiftOn = 0;
				changeKeyboard(newKeyboard);
			}
			mCapsLock = false;
		}
	}

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

		int mode = -1;
		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) {
			Keyboard kbd = getModeChangeKeyboard(mode);
			if (kbd != null) {
				mCurrentKeyMode = mode;
				changeKeyboard(kbd);
			}
		}
	}

	/**
	 * Change the keyboard type.
	 *
	 * @param type  Type of the keyboard
	 * @see com.hiroshica.android.input.nicownn2.DefaultSoftKeyboard#KEYBOARD_QWERTY
	 * @see com.hiroshica.android.input.nicownn2.DefaultSoftKeyboard#KEYBOARD_12KEY
	 */
	public void changeKeyboardType(int type) {
		/* ignore invalid parameter */
		if (type != KEYBOARD_QWERTY && type != KEYBOARD_12KEY) {
			return;
		}
        
		/* change keyboard view */
		Keyboard kbd = getTypeChangeKeyboard(type);
		if (kbd != null) {
			mCurrentKeyboardType = type;
			changeKeyboard(kbd);
		}

		/* notice that the keyboard is changed */
		mWnn.onEvent(new NicoWnnEvent(NicoWnnEvent.CHANGE_MODE,
									  NicoWnnEvent.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(Keyboard keyboard) {

		if (keyboard == null) {
			return false;
		}
		if (mCurrentKeyboard != keyboard) {
			mKeyboardView.setKeyboard(keyboard);
			mKeyboardView.setShifted((mShiftOn == 0) ? false : true);
			mCurrentKeyboard = keyboard;
			return true;
		} else {
			mKeyboardView.setShifted((mShiftOn == 0) ? false : true);
			return false;
		}
	}
	
	/*
	 * 
	 */
	public void changeKeyMode(int keyMode) {
	}
	
	/** @see com.hiroshica.android.input.nicownn2.InputViewManager#initView */
	public View initView(NicoWnn parent, int width, int height) {
		mWnn = parent;
		mDisplayMode = 
			(parent.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
			? LANDSCAPE : PORTRAIT;
		// get hardkeyboard status
        int qwertyState = parent.getResources().getConfiguration().keyboard;
        boolean qwerty  = (qwertyState == Configuration.KEYBOARD_QWERTY);
        int hiddenState = parent.getResources().getConfiguration().hardKeyboardHidden;
        boolean hidden  = (hiddenState == Configuration.HARDKEYBOARDHIDDEN_YES);
		setHardKeyboardHidden(hidden, qwerty);
		SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(parent);
		mInputViewHeightIndex = inputViewKeyHeight.get(pref.getString("mainview_height_mode", "mainview_value_0"));

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

		String skin = pref.getString("keyboard_skin", mWnn.getResources().getString(R.string.keyboard_skin_id_default));
		int id = parent.getResources().getIdentifier(skin, "layout", "com.hiroshica.android.input.nicownn2");
		mFlickNicoInput       = flickModeTable.get(pref.getString("nicoflick_mode", "none_flick"));
		mFlickSensivity       = (float)flickSensivityModeTable.get(pref.getString("flick_sensivity_mode", "sensitivity_value_2"));

		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);
			mIsActiveLongPress = false;
		}
		mMetrics.setToDefaults();
		//changeKeyboardKeyHeight();
		return mMainView;
	}
    
	/**
	 * Update the SHFIT/ALT keys indicator.
	 * 
	 * @param mode  The state of SHIFT/ALT keys.
	 */
	public void updateIndicator(int mode) {
		Resources res = mWnn.getResources();
		TextView text1 = (TextView)mSubView.findViewById(R.id.shift);
		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;
		}
		return;
	}
    
	/** @see com.hiroshica.android.input.nicownn2.InputViewManager#getCurrentView */
	public View getCurrentView() {
		if (mCurrentKeyboard == null) {
			return null;
		}
		return mMainView;
	}

	/** @see com.hiroshica.android.input.nicownn2.InputViewManager#onUpdateState */
	public void onUpdateState(NicoWnn parent) {
		try {
			if (parent.mComposingText.size(1) == 0) {
				if (!mNoInput) {
					/* when the mode changed to "no input" */
					mNoInput = true;
					Keyboard newKeyboard = getKeyboardInputed(false);
					if (mCurrentKeyboard != newKeyboard) {
						changeKeyboard(newKeyboard);
					}
				}
			} else {
				if (mNoInput) {
					/* when the mode changed to "input some characters" */
					mNoInput = false;
					Keyboard newKeyboard = getKeyboardInputed(true);
					if (mCurrentKeyboard != newKeyboard) {
						changeKeyboard(newKeyboard);
					}
				}
			}
		} catch (Exception ex) {
		}
	}

	/** @see com.hiroshica.android.input.nicownn2.InputViewManager#setPreferences */
	public void setPreferences(SharedPreferences pref, EditorInfo editor) {

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

		/* sound */
		try {
			if (pref.getBoolean("key_sound", false)) {
				mSound = MediaPlayer.create(mWnn, R.raw.type);
			} else {
				mSound = null;
			}
		} catch (Exception ex) {
			Log.d("NicoWnn", "NO SOUND");
		}
		mFlickNicoInput       = flickModeTable.get(pref.getString("nicoflick_mode", "none_flick"));
		mFlickSensivity       = (float)flickSensivityModeTable.get(pref.getString("flick_sensivity_mode", "sensitivity_value_2"));
		if (null != mDetector) {
			if (NICOFLICK_1STROKE == mFlickNicoInput) {
				mDetector.setIsLongpressEnabled(true);
				mIsActiveLongPress = true;
			}
			else{
				mDetector.setIsLongpressEnabled(false);
				mIsActiveLongPress = false;
			}
		}
		/* pop-up preview */
		mKeyboardView.setPreviewEnabled(pref.getBoolean("popup_preview", true));
	}

	/** @see com.hiroshica.android.input.nicownn2.InputViewManager#closing */
	public void closing() {
		if (mKeyboardView != null) {
			mKeyboardView.closing();
		}
		mDisableKeyInput = true;
	}

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

	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#swipeRight */
	public void swipeRight() {
	}
	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#swipeLeft */
	public void swipeLeft() {
	}
	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#swipeDown */
	public void swipeDown() {
	}
	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#swipeUp */
	public void swipeUp() {
	}
	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#onRelease */
	public void onRelease(int primaryCode) {
	}

	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#onPress */
	public void onPress(int primaryCode) {
		/* key click sound & vibration */
		if (mVibrator != null) {
			try { mVibrator.vibrate(30); } catch (Exception ex) { }
		}
		if (mSound != null) {
			try { mSound.seekTo(0); mSound.start(); } catch (Exception ex) { }
		}
		// check flick
		if (false == mNicoFirst) {
			mPrevInputKeyCode = primaryCode;
		}
	}
    
	/** @see android.inputmethodservice.KeyboardView.OnKeyboardActionListener#onText */
	public void onText(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 mCurrentKeyboardType;
	}

	/**
	 * Set the H/W keyboard's state.
	 * 
	 * @param hidden {@code true} if hidden.
	 */
	public void setHardKeyboardHidden(boolean hidden, boolean qwerty) {
		mHardKeyboardHidden = hidden;
		mIsQwertyKeyboard   = qwerty;
	}
	/**
	 *
	 */
	public boolean isHardQwertyOnLandscape() {
		if (mDisplayMode == DefaultSoftKeyboard.PORTRAIT) {
			return false;
		}
		if (false == mIsQwertyKeyboard) {
			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 boolean getHiddenSoftKeyboard() {
		return mHiddenSoftKeyboard;
	}

	/**
	 * Reset the current keyboard
	 */
	public void resetCurrentKeyboard() {
		closing();
		Keyboard 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(int keyCode) {
		return keyCode;
	}

	/******************************************************************************************/
	/******************************************************************************************/
	/***
	 * gesture control
	 */
	public boolean onTouch(View v, MotionEvent event) {
		mDetector.onTouchEvent(event);
		return false;
	}
	/**
	 * OnGestureListener functions
	 */
	public boolean onDown(MotionEvent ev){
		//Log.d("NicoWnn", "onDown");
		mStartX = ev.getRawX();
		mStartY = ev.getRawY();
		mGestureX = mStartX;
		mGestureY = mStartY;
		mIsLongPress = false;
		return true;
	}
	public boolean onFling(MotionEvent ev1, MotionEvent ev2, float getX, float getY){
		return true;
	}
	public void    onLongPress(MotionEvent ev){
		mIsLongPress = true;
	}
	public boolean onScroll(MotionEvent ev1, MotionEvent ev2, float distX, float distY){
		mGestureX = ev2.getRawX();
		mGestureY = ev2.getRawY();
		return true;
	}
	public void    onShowPress(MotionEvent ev){
	}
	public boolean onSingleTapUp(MotionEvent ev){
		return true;
	}
	/**
	 * calc. flick keycode
	 */
	public int checkFlickKeyCode(boolean enablesharp) {
		if (NICOFLICK_NONE == mFlickNicoInput) {
			return -1;
		}
		if (!((mCurrentKeyMode == KEYMODE_JA_FULL_NICO) ||
			  (mCurrentKeyMode == KEYMODE_JA_HALF_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_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("NicoWnn", "checkFlick " + calcx + "/" + calcy);

		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 < mFlickSensivity) {
			if (false == mIsActiveLongPress) {
				keycode = -1;
			}
			else{
				if (false == mIsLongPress) {
					keycode = 0;
				}
				else{
					keycode = -1;
				}
			}
		}
		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 (-1 != keycode) {
			keycode = convertModeFlick(getTableIndex(mPrevInputKeyCode), keycode);
		}
		return keycode;
	}
	/**
	 *
	 */
	public int convertModeFlick(int prev, int key) {
		return key;
	}
}
/**************** end of file ****************/
