/*
 * 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 java.util.ArrayList;
import java.util.HashMap;
import java.lang.Math;

import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.media.MediaPlayer;
import android.os.AsyncTask;
import android.os.SystemClock;
import android.os.Vibrator;
import android.text.TextUtils;
import android.text.TextPaint;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.ImageSpan;
import android.text.style.DynamicDrawableSpan;
import android.util.Log;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.HorizontalScrollView;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.graphics.drawable.Drawable;

/**
 * The default candidates view manager class using {@link EditText}.
 *
 * @author Copyright (C) 2009 OMRON SOFTWARE CO., LTD.  All Rights Reserved.
 */
public class TextCandidatesViewManager implements CandidatesViewManager, GestureDetector.OnGestureListener {
	/** Height of a line */
	public static final int LINE_HEIGHT = 34;

	public static final int LINE_NUM_1LINE    = 1;
	public static final int LINE_NUM_2LINE    = 2;
	public static final int LINE_NUM_MAX      = 2;

	/** Number of lines to display (Portrait) */
	//public static final int LINE_NUM_PORTRAIT       = 1;
	/** Number of lines to display (Landscape) */
	//public static final int LINE_NUM_LANDSCAPE      = 1;

	/** Maximum lines */
	private static final int DISPLAY_LINE_MAX_COUNT = 1000;
	/** Width of the view */
	private static final int CANDIDATE_MINIMUM_WIDTH = 48;
	/** Height of the view */
	//private static final int CANDIDATE_MINIMUM_HEIGHT = 42;
	/**
	 * candidate-line height
	 */
	private static final HashMap<String, Integer> candidateViewKeyHeight = new HashMap<String, Integer>() {
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		{
			put("candidateview_value_0", 0);
			put("candidateview_value_1", 1);
			put("candidateview_value_2", 2);
		}};
	private static int mCandidateViewHeightIndex = 0;
	public static int candidateViewDataTable[] = { 42, 51, 60 };

	/** Maximum number of displaying candidates par one line (full view mode) */
	private static final int FULL_VIEW_DIV = 5;

	/** Body view of the candidates list */
	private ViewGroup  mViewBody;
	/** Scroller of {@code mViewBodyText} */
	private ScrollView           mViewBodyVScroll;
	private HorizontalScrollView mViewBodyHScroll;

	private boolean mIsScroll;
	/** Base of {@code mViewCandidateList1st}, {@code mViewCandidateList2nd} */
	private ViewGroup mViewCandidateBase;
	/** The view of the scaling up candidate */
	private View mViewScaleUp;
	/** Layout for the candidates list on normal view */
	private LinearLayout  mViewCandidateList1st;
	/** Layout for the candidates list on full view */
	private RelativeLayout mViewCandidateList2nd;

	/** {@link NicoWnn} instance using this manager */
	private NicoWnn mWnn;
	/** View type (VIEW_TYPE_NORMAL or VIEW_TYPE_FULL or VIEW_TYPE_CLOSE) */
	private int mViewType;
	/** Portrait display({@code true}) or landscape({@code false}) */
	private boolean mPortrait;

	/** Width of the view */
	int mViewWidth;
	/** Height of the view */
	private int mViewHeight;
	/** Whether hide the view if there is no candidates */
	private boolean mAutoHideMode;
	/** The converter to be get candidates from and notice the selected candidate to. */
	public WnnEngine mConverter;
	/** Vibrator for touch vibration */
	private Vibrator mVibrator = null;
	/** MediaPlayer for click sound */
	private MediaPlayer mSound = null;

	/** Number of candidates displaying */
	int mWordCount;
	int m1stWordCount;
	int[] mWnnLineCount    = new int[LINE_NUM_MAX];
	int[] mWnnLineCountMax = new int[LINE_NUM_MAX];
	private int[] mWnnLineOffset   = new int[LINE_NUM_MAX];

	/** portrait/landscape line */
	private static final HashMap<String, Integer> lineModeTable = new HashMap<String, Integer>() {/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

	{
			put("1_1", 0);
			put("2_1", 1);
			put("1_2", 2);
			put("2_2", 3);
		}};
	
	private int mLineMode;
	private int mPortraitLine;
	private int mLandscapeLine;

	private boolean mNoSpace;

	/** get last id of topline */
	private int mToplineLastId;

	private int mTotalLastId;
	/** List of candidates */
	private ArrayList<WnnWord> mWnnWordArray;
	private ArrayList<Integer> mWnnWordTextLength;
	private ArrayList<Integer> mWnnWordOccupyCount;

	/** Gesture detector */
	private GestureDetector mGestureDetector;
	/** The word pressed */
	private WnnWord mWord;
	/** Number of lines displayed */
	private int mLineCount = 1;

	/** {@code true} if the candidate delete state is selected */
	private boolean mIsScaleUp = false;

	/** {@code true} if the full screen mode is selected */
	private boolean mIsFullView = false;

	private boolean mIsCreateFullView = false;

	private boolean mIsLockHScroll = false;

	private int mTargetScrollWidth;

	/** The event object for "touch" */
	private MotionEvent mMotionEvent = null;

	/** {@code true} if there are more candidates to display. */
	private boolean mCanReadMore = false;
	/** Width of {@code mReadMoreButton} */
	private int mTextColor = 0;
	/** Template object for each candidate and normal/full view change button */
	private TextView mViewCandidateTemplate;
	/** Number of candidates in full view */
	private int mFullViewWordCount;
	/** Number of candidates in the current line (in full view) */
	private int mFullViewOccupyCount;
	/** View of the previous candidate (in full view) */
	private TextView mFullViewPrevView;
	/** Id of the top line view (in full view) */
	private int mFullViewPrevLineTopId;
	/** Layout of the previous candidate (in full view) */
	private RelativeLayout.LayoutParams mFullViewPrevParams;
	/** Whether all candidates is displayed */
	private boolean mCreateCandidateDone;
	/** general infomation about a display */
	private static final DisplayMetrics mMetrics = new DisplayMetrics();

	/* asynctask */
	private TextCandidateTask mCandidateTask = null;
	private boolean   mIsActiveTask = false;
	private boolean   mIsCancelTask = false;
	
	// docomo emoji hashmap
	private static final HashMap<String, Integer> DOCOMO_EMOJI_TABLE = new HashMap<String, Integer>() {/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

	{
			put("\uE63E", R.drawable.docomo_1); put("\uE63F", R.drawable.docomo_2); put("\uE640", R.drawable.docomo_3); put("\uE641", R.drawable.docomo_4);
			put("\uE642", R.drawable.docomo_5); put("\uE643", R.drawable.docomo_6); put("\uE644", R.drawable.docomo_7); put("\uE645", R.drawable.docomo_8);
			put("\uE646", R.drawable.docomo_9); put("\uE647", R.drawable.docomo_10); put("\uE648", R.drawable.docomo_11); put("\uE649", R.drawable.docomo_12);
			put("\uE64A", R.drawable.docomo_13); put("\uE64B", R.drawable.docomo_14); put("\uE64C", R.drawable.docomo_15); put("\uE64D", R.drawable.docomo_16);
			put("\uE64E", R.drawable.docomo_17); put("\uE64F", R.drawable.docomo_18); put("\uE650", R.drawable.docomo_19); put("\uE651", R.drawable.docomo_20);
			put("\uE652", R.drawable.docomo_21); put("\uE653", R.drawable.docomo_22); put("\uE654", R.drawable.docomo_23); put("\uE655", R.drawable.docomo_24);
			put("\uE656", R.drawable.docomo_25); put("\uE657", R.drawable.docomo_26); put("\uE658", R.drawable.docomo_27); put("\uE659", R.drawable.docomo_28);
			put("\uE65A", R.drawable.docomo_29); put("\uE65B", R.drawable.docomo_30); put("\uE65C", R.drawable.docomo_31); put("\uE65D", R.drawable.docomo_32);
			put("\uE65E", R.drawable.docomo_33); put("\uE65F", R.drawable.docomo_34); put("\uE660", R.drawable.docomo_35); put("\uE661", R.drawable.docomo_36);
			put("\uE662", R.drawable.docomo_37); put("\uE663", R.drawable.docomo_38); put("\uE664", R.drawable.docomo_39); put("\uE665", R.drawable.docomo_40);
			put("\uE666", R.drawable.docomo_41); put("\uE667", R.drawable.docomo_42); put("\uE668", R.drawable.docomo_43); put("\uE669", R.drawable.docomo_44);
			put("\uE66A", R.drawable.docomo_45); put("\uE66B", R.drawable.docomo_46); put("\uE66C", R.drawable.docomo_47); put("\uE66D", R.drawable.docomo_48);
			put("\uE66E", R.drawable.docomo_49); put("\uE66F", R.drawable.docomo_50); put("\uE670", R.drawable.docomo_51); put("\uE671", R.drawable.docomo_52);
			put("\uE672", R.drawable.docomo_53); put("\uE673", R.drawable.docomo_54); put("\uE674", R.drawable.docomo_55); put("\uE675", R.drawable.docomo_56);
			put("\uE676", R.drawable.docomo_57); put("\uE677", R.drawable.docomo_58); put("\uE678", R.drawable.docomo_59); put("\uE679", R.drawable.docomo_60);
			put("\uE67A", R.drawable.docomo_61); put("\uE67B", R.drawable.docomo_62); put("\uE67C", R.drawable.docomo_63); put("\uE67D", R.drawable.docomo_64);
			put("\uE67E", R.drawable.docomo_65); put("\uE67F", R.drawable.docomo_66); put("\uE680", R.drawable.docomo_67); put("\uE681", R.drawable.docomo_68);
			put("\uE682", R.drawable.docomo_69); put("\uE683", R.drawable.docomo_70); put("\uE684", R.drawable.docomo_71); put("\uE685", R.drawable.docomo_72);
			put("\uE686", R.drawable.docomo_73); put("\uE687", R.drawable.docomo_74); put("\uE688", R.drawable.docomo_75); put("\uE689", R.drawable.docomo_76);
			put("\uE68A", R.drawable.docomo_77); put("\uE68B", R.drawable.docomo_78); put("\uE68C", R.drawable.docomo_79); put("\uE68D", R.drawable.docomo_80);
			put("\uE68E", R.drawable.docomo_81); put("\uE68F", R.drawable.docomo_82); put("\uE690", R.drawable.docomo_83); put("\uE691", R.drawable.docomo_84);
			put("\uE692", R.drawable.docomo_85); put("\uE693", R.drawable.docomo_86); put("\uE694", R.drawable.docomo_87); put("\uE695", R.drawable.docomo_88);
			put("\uE696", R.drawable.docomo_89); put("\uE697", R.drawable.docomo_90); put("\uE698", R.drawable.docomo_91); put("\uE699", R.drawable.docomo_92);
			put("\uE69A", R.drawable.docomo_93); put("\uE69B", R.drawable.docomo_94); put("\uE69C", R.drawable.docomo_95); put("\uE69D", R.drawable.docomo_96);
			put("\uE69E", R.drawable.docomo_97); put("\uE69F", R.drawable.docomo_98); put("\uE6A0", R.drawable.docomo_99); put("\uE6A1", R.drawable.docomo_100);
			put("\uE6A2", R.drawable.docomo_101); put("\uE6A3", R.drawable.docomo_102); put("\uE6A4", R.drawable.docomo_103); put("\uE6A5", R.drawable.docomo_104);
			put("\uE6CE", R.drawable.docomo_105); put("\uE6CF", R.drawable.docomo_106); put("\uE6D0", R.drawable.docomo_107); put("\uE6D1", R.drawable.docomo_108);
			put("\uE6D2", R.drawable.docomo_109); put("\uE6D3", R.drawable.docomo_110); put("\uE6D4", R.drawable.docomo_111); put("\uE6D5", R.drawable.docomo_112);
			put("\uE6D6", R.drawable.docomo_113); put("\uE6D7", R.drawable.docomo_114); put("\uE6D8", R.drawable.docomo_115); put("\uE6D9", R.drawable.docomo_116);
			put("\uE6DA", R.drawable.docomo_117); put("\uE6DB", R.drawable.docomo_118); put("\uE6DC", R.drawable.docomo_119); put("\uE6DD", R.drawable.docomo_120);
			put("\uE6DE", R.drawable.docomo_121); put("\uE6DF", R.drawable.docomo_122); put("\uE6E0", R.drawable.docomo_123); put("\uE6E1", R.drawable.docomo_124);
			put("\uE6E2", R.drawable.docomo_125); put("\uE6E3", R.drawable.docomo_126); put("\uE6E4", R.drawable.docomo_127); put("\uE6E5", R.drawable.docomo_128);
			put("\uE6E6", R.drawable.docomo_129); put("\uE6E7", R.drawable.docomo_130); put("\uE6E8", R.drawable.docomo_131); put("\uE6E9", R.drawable.docomo_132);
			put("\uE6EA", R.drawable.docomo_133); put("\uE6EB", R.drawable.docomo_134); put("\uE70B", R.drawable.docomo_135); put("\uE6EC", R.drawable.docomo_136);
			put("\uE6ED", R.drawable.docomo_137); put("\uE6EE", R.drawable.docomo_138); put("\uE6EF", R.drawable.docomo_139); put("\uE6F0", R.drawable.docomo_140);
			put("\uE6F1", R.drawable.docomo_141); put("\uE6F2", R.drawable.docomo_142); put("\uE6F3", R.drawable.docomo_143); put("\uE6F4", R.drawable.docomo_144);
			put("\uE6F5", R.drawable.docomo_145); put("\uE6F6", R.drawable.docomo_146); put("\uE6F7", R.drawable.docomo_147); put("\uE6F8", R.drawable.docomo_148);
			put("\uE6F9", R.drawable.docomo_149); put("\uE6FA", R.drawable.docomo_150); put("\uE6FB", R.drawable.docomo_151); put("\uE6FC", R.drawable.docomo_152);
			put("\uE6FD", R.drawable.docomo_153); put("\uE6FE", R.drawable.docomo_154); put("\uE6FF", R.drawable.docomo_155); put("\uE700", R.drawable.docomo_156);
			put("\uE701", R.drawable.docomo_157); put("\uE702", R.drawable.docomo_158); put("\uE703", R.drawable.docomo_159); put("\uE704", R.drawable.docomo_160);
			put("\uE705", R.drawable.docomo_161); put("\uE706", R.drawable.docomo_162); put("\uE707", R.drawable.docomo_163); put("\uE708", R.drawable.docomo_164);
			put("\uE709", R.drawable.docomo_165); put("\uE70A", R.drawable.docomo_166); put("\uE6AC", R.drawable.docomo_167); put("\uE6AD", R.drawable.docomo_168);
			put("\uE6AE", R.drawable.docomo_169); put("\uE6B1", R.drawable.docomo_170); put("\uE6B2", R.drawable.docomo_171); put("\uE6B3", R.drawable.docomo_172);
			put("\uE6B7", R.drawable.docomo_173); put("\uE6B8", R.drawable.docomo_174); put("\uE6B9", R.drawable.docomo_175); put("\uE6BA", R.drawable.docomo_176);
			put("\uE70C", R.drawable.docomo_ex1); put("\uE70D", R.drawable.docomo_ex2); put("\uE70E", R.drawable.docomo_ex3); put("\uE70F", R.drawable.docomo_ex4);
			put("\uE710", R.drawable.docomo_ex5); put("\uE711", R.drawable.docomo_ex6); put("\uE712", R.drawable.docomo_ex7); put("\uE713", R.drawable.docomo_ex8);
			put("\uE714", R.drawable.docomo_ex9); put("\uE715", R.drawable.docomo_ex10); put("\uE716", R.drawable.docomo_ex11); put("\uE717", R.drawable.docomo_ex12);
			put("\uE718", R.drawable.docomo_ex13); put("\uE719", R.drawable.docomo_ex14); put("\uE71A", R.drawable.docomo_ex15); put("\uE71B", R.drawable.docomo_ex16);
			put("\uE71C", R.drawable.docomo_ex17); put("\uE71D", R.drawable.docomo_ex18); put("\uE71E", R.drawable.docomo_ex19); put("\uE71F", R.drawable.docomo_ex20);
			put("\uE720", R.drawable.docomo_ex21); put("\uE721", R.drawable.docomo_ex22); put("\uE722", R.drawable.docomo_ex23); put("\uE723", R.drawable.docomo_ex24);
			put("\uE724", R.drawable.docomo_ex25); put("\uE725", R.drawable.docomo_ex26); put("\uE726", R.drawable.docomo_ex27); put("\uE727", R.drawable.docomo_ex28);
			put("\uE728", R.drawable.docomo_ex29); put("\uE729", R.drawable.docomo_ex30); put("\uE72A", R.drawable.docomo_ex31); put("\uE72B", R.drawable.docomo_ex32);
			put("\uE72C", R.drawable.docomo_ex33); put("\uE72D", R.drawable.docomo_ex34); put("\uE72E", R.drawable.docomo_ex35); put("\uE72F", R.drawable.docomo_ex36);
			put("\uE730", R.drawable.docomo_ex37); put("\uE731", R.drawable.docomo_ex38); put("\uE732", R.drawable.docomo_ex39); put("\uE733", R.drawable.docomo_ex40);
			put("\uE734", R.drawable.docomo_ex41); put("\uE735", R.drawable.docomo_ex42); put("\uE736", R.drawable.docomo_ex43); put("\uE737", R.drawable.docomo_ex44);
			put("\uE738", R.drawable.docomo_ex45); put("\uE739", R.drawable.docomo_ex46); put("\uE73A", R.drawable.docomo_ex47); put("\uE73B", R.drawable.docomo_ex48);
			put("\uE73C", R.drawable.docomo_ex49); put("\uE73D", R.drawable.docomo_ex50); put("\uE73E", R.drawable.docomo_ex51); put("\uE73F", R.drawable.docomo_ex52);
			put("\uE740", R.drawable.docomo_ex53); put("\uE741", R.drawable.docomo_ex54); put("\uE742", R.drawable.docomo_ex55); put("\uE743", R.drawable.docomo_ex56);
			put("\uE744", R.drawable.docomo_ex57); put("\uE745", R.drawable.docomo_ex58); put("\uE746", R.drawable.docomo_ex59); put("\uE747", R.drawable.docomo_ex60);
			put("\uE748", R.drawable.docomo_ex61); put("\uE749", R.drawable.docomo_ex62); put("\uE74A", R.drawable.docomo_ex63); put("\uE74B", R.drawable.docomo_ex64);
			put("\uE74C", R.drawable.docomo_ex65); put("\uE74D", R.drawable.docomo_ex66); put("\uE74E", R.drawable.docomo_ex67); put("\uE74F", R.drawable.docomo_ex68);
			put("\uE750", R.drawable.docomo_ex69); put("\uE751", R.drawable.docomo_ex70); put("\uE752", R.drawable.docomo_ex71); put("\uE753", R.drawable.docomo_ex72);
			put("\uE754", R.drawable.docomo_ex73); put("\uE755", R.drawable.docomo_ex74); put("\uE756", R.drawable.docomo_ex75); put("\uE757", R.drawable.docomo_ex76);
		}};

	/** Event listener for touching a candidate */
	private OnTouchListener mCandidateOnTouch = new OnTouchListener() {
		public boolean onTouch(View v, MotionEvent event) {
			if (mMotionEvent != null) {
				return true;
			}

			if ((event.getAction() == MotionEvent.ACTION_UP)
			    && (v instanceof TextView)) {
				Drawable d = v.getBackground();
				if (d != null) {
					d.setState(new int[] {});
				}
			}

			mMotionEvent = event;
			boolean ret = mWnn.onEvent(new NicoWnnEvent(NicoWnnEvent.CANDIDATE_VIEW_TOUCH));
			mMotionEvent = null;
			return ret;
		}
	};
    
    
	/** Event listener for clicking a candidate */
	private OnClickListener mCandidateOnClick = new OnClickListener() {
		public void onClick(View v) {
			if (!v.isShown()) {
				return;
			}
                
			if (v instanceof TextView) {
				checkCandidateTask();
				TextView text = (TextView)v;
				int wordcount = text.getId() - 1;
				WnnWord word = null;
				word = mWnnWordArray.get(wordcount);
				selectCandidate(word);
			}
		}
	};

	/** Event listener for long-clicking a candidate */
	private OnLongClickListener mCandidateOnLongClick = new OnLongClickListener() {
		public boolean onLongClick(View v) {
			if (mViewScaleUp == null) {
				return false;
			}

			if (!v.isShown()) {
				return true;
			}

			Drawable d = v.getBackground();
			if (d != null) {
				if(d.getState().length == 0){
					return true;
				}
			}
            
			int wordcount = ((TextView)v).getId() - 1;
			mWord = mWnnWordArray.get(wordcount);
			setViewScaleUp(true, mWord);
            
			return true;
		}
	};


	/**
	 * Constructor
	 */
	public TextCandidatesViewManager() {
		this(-1);
	}

	/**
	 * Constructor
	 *
	 * @param displayLimit      The limit of display
	 */
	public TextCandidatesViewManager(int displayLimit) {
		this.mWnnWordArray = new ArrayList<WnnWord>();
		this.mWnnWordTextLength = new ArrayList<Integer>();
		this.mWnnWordOccupyCount = new ArrayList<Integer>();
		this.mAutoHideMode = true;
		this.mIsActiveTask = false;
		mMetrics.setToDefaults();
	}

	/**
	 * Set auto-hide mode.
	 * @param hide      {@code true} if the view will hidden when no candidate exists;
	 *                  {@code false} if the view is always shown.
	 */
	public void setAutoHide(boolean hide) {
		mAutoHideMode = hide;
	}

	/** @see CandidatesViewManager */
	public View initView(NicoWnn parent, int width, int height) {
		mWnn = parent;
		mViewWidth = Math.round((float)width / mMetrics.density);
		mViewHeight = Math.round((float)height / mMetrics.density);
		mPortrait = 
			(parent.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE);

		mLineMode = 0;
		mPortraitLine  = 1;
		mLandscapeLine = 1;
		mNoSpace = false;

		SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(parent);
		if (null != pref) {
			mLineMode = lineModeTable.get(pref.getString("nico_candidate_mode", "1_1"));
			if (0 == mLineMode) {
				mPortraitLine  = 1;
				mLandscapeLine = 1;
			}
			if (1 == mLineMode) {
				mPortraitLine  = 2;
				mLandscapeLine = 1;
			}
			if (2 == mLineMode) {
				mPortraitLine  = 1;
				mLandscapeLine = 2;
			}
			if (3 == mLineMode) {
				mPortraitLine  = 2;
				mLandscapeLine = 2;
			}
			mNoSpace = pref.getBoolean("nospace_candidate", false);
			// candidate line height
			mCandidateViewHeightIndex = candidateViewKeyHeight.get(pref.getString("candidateview_height_mode", "candidateview_value_0"));
		}

		Resources r = mWnn.getResources();

		LayoutInflater inflater = parent.getLayoutInflater();
		mViewBody = (ViewGroup)inflater.inflate(R.layout.candidates, null);

		mViewBodyVScroll = (ScrollView)mViewBody.findViewById(R.id.candview_scroll);
		mViewBodyVScroll.setOnTouchListener(mCandidateOnTouch);

		mViewBodyHScroll = (HorizontalScrollView)mViewBody.findViewById(R.id.candview_hscroll);
		mViewBodyHScroll.setOnTouchListener(mCandidateOnTouch);
		mIsLockHScroll = false;

		mViewCandidateBase = (ViewGroup)mViewBody.findViewById(R.id.candview_base);

		mViewCandidateList1st = (LinearLayout)mViewBody.findViewById(R.id.candidates_1st_view);
		mViewCandidateList1st.setOnTouchListener(mCandidateOnTouch);
		mViewCandidateList1st.setOnClickListener(mCandidateOnClick);

		mViewCandidateList2nd = (RelativeLayout)mViewBody.findViewById(R.id.candidates_2nd_view);
		mViewCandidateList2nd.setOnTouchListener(mCandidateOnTouch);
		mViewCandidateList2nd.setOnClickListener(mCandidateOnClick);

		// create first textView
		for (int iI = 0; iI < LINE_NUM_MAX; ++iI) {
            LinearLayout lineView = new LinearLayout(mViewBodyVScroll.getContext());
            lineView.setOrientation(LinearLayout.HORIZONTAL);
            LinearLayout.LayoutParams layoutParams = 
				new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                                              ViewGroup.LayoutParams.WRAP_CONTENT);
            lineView.setLayoutParams(layoutParams);
			lineView.setGravity(Gravity.LEFT);

			lineView.setMinimumHeight(getCandidateMinimumHeight());
            mViewCandidateList1st.addView(lineView);
		}

		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(getCandidateMinimumWidth(), getCandidateMinimumHeight());
		params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
		params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
		
		TextView textView = createCandidateView();
		//textView.setLayoutParams(params);
		mViewCandidateList2nd.addView(textView);
		mViewCandidateTemplate = textView;

		mTextColor = r.getColor(R.color.candidate_text);

		setViewType(CandidatesViewManager.VIEW_TYPE_CLOSE);

		mGestureDetector = new GestureDetector(this);

		View scaleUp = (View)inflater.inflate(R.layout.candidate_scale_up, null);
		mViewScaleUp = scaleUp;

		/* select button */
		Button b = (Button)scaleUp.findViewById(R.id.candidate_select);
		b.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				selectCandidate(mWord);
			}
		});

		/* cancel button */
		b = (Button)scaleUp.findViewById(R.id.candidate_cancel);
		b.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
				setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
				mWnn.onEvent(new NicoWnnEvent(NicoWnnEvent.UPDATE_CANDIDATE));
			}
		});

		return mViewBody;
	}

	/** @see CandidatesViewManager#getCurrentView */
	public View getCurrentView() {
		return mViewBody;
	}

	/** @see CandidatesViewManager#setViewType */
	public void setViewType(int type) {
		boolean readMore = setViewLayout(type);
		
		if (readMore) {
			if (false == mIsCreateFullView) {
				mCanReadMore = false;
				mFullViewWordCount = 0;
				mFullViewOccupyCount = 0;
				mFullViewPrevLineTopId = 0;
				mCreateCandidateDone = false;
				mIsScroll = false;

				mLineCount     = 1;
				mWordCount     = 0;
				mToplineLastId = 0;
				mTotalLastId   = 0;
				mIsLockHScroll = false;

				displayCandidates(this.mConverter, false, -1);
				mIsCreateFullView = true;
			}
			else{
				visibleFullCandidate();
			}
		} else { 
			if (type == CandidatesViewManager.VIEW_TYPE_NORMAL) {
				mIsFullView = false;
				/*
				  if (mDisplayEndOffset > 0) {
				  int maxLine = getMaxLine();
				  displayCandidates(this.mConverter, false, maxLine);
				  }
				*/
			} else {
				mIsFullView = true;
				if (mViewBody.isShown()) {
					mWnn.setCandidatesViewShown(false);
				}
			}
		}
	}

	/**
	 * Set the view layout
	 *
	 * @param type      View type
	 * @return          {@code true} if display is updated; {@code false} if otherwise
	 */
	private boolean setViewLayout(int type) {
		mViewType = type;
		setViewScaleUp(false, null);

		switch (type) {
		case CandidatesViewManager.VIEW_TYPE_CLOSE:
			checkCandidateTask();
			mViewCandidateBase.setMinimumHeight(-1);
			//mIsLockHScroll = false;
			return false;

		case CandidatesViewManager.VIEW_TYPE_NORMAL:
			clearNormalCandidates();
			mViewBodyVScroll.scrollTo(0, 0);
			if (false == mIsLockHScroll) {
				//mViewBodyHScroll.scrollTo(0, 0);
			}
			mViewCandidateList1st.setVisibility(View.VISIBLE);
			LinearLayout candidateList = mViewCandidateList1st;
			for (int iI = 0; iI < LINE_NUM_MAX; ++iI) {
				LinearLayout lineView = (LinearLayout)candidateList.getChildAt(iI);
				if (iI < getMaxLine()) {
					lineView.setVisibility(View.VISIBLE);
				}
				else{
					lineView.setVisibility(View.GONE);
				}
			}
			mViewCandidateList2nd.setVisibility(View.GONE);
			mViewCandidateBase.setMinimumHeight(-1);
			int line = getMaxLine();
			int minheight = (getCandidateMinimumHeight() * line);
			if (false == mNoSpace) {
				minheight += ((getCandidateMinimumHeight()/3) * 2);
			}
			mViewCandidateList1st.setMinimumHeight(minheight);
			return false;
			
		case CandidatesViewManager.VIEW_TYPE_FULL:
		default:
			mViewCandidateList1st.setVisibility(View.GONE);
			mViewCandidateList2nd.setVisibility(View.VISIBLE);
			mViewCandidateList2nd.setMinimumHeight(mViewHeight);
			mViewCandidateBase.setMinimumHeight(mViewHeight);
			return true;
		}
	}

	/** @see CandidatesViewManager#getViewType */
	public int getViewType() {
		return mViewType;
	}

	/** set full view */
	public void setFullView() {
		mIsFullView = true;
		mIsScroll   = false; // reset scroll flag
		mWnn.onEvent(new NicoWnnEvent(NicoWnnEvent.LIST_CANDIDATES_FULL));
	}
	
	/** @see CandidatesViewManager#displayCandidates */
	public void displayCandidates(WnnEngine converter) {
		SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(mWnn);

		mLineMode = 0;
		mPortraitLine  = 1;
		mLandscapeLine = 1;
		mNoSpace = false;

		mLineMode = lineModeTable.get(pref.getString("nico_candidate_mode", "1_1"));
		if (0 == mLineMode) {
			mPortraitLine  = 1;
			mLandscapeLine = 1;
		}
		else if (1 == mLineMode) {
			mPortraitLine  = 2;
			mLandscapeLine = 1;
		}
		else if (2 == mLineMode) {
			mPortraitLine  = 1;
			mLandscapeLine = 2;
		}
		else if (3 == mLineMode) {
			mPortraitLine  = 2;
			mLandscapeLine = 2;
		}
		mNoSpace = pref.getBoolean("nospace_candidate", false);
		// candidate line height
		mCandidateViewHeightIndex = candidateViewKeyHeight.get(pref.getString("candidateview_height_mode", "candidateview_value_0"));

		mCanReadMore = false;
		mIsFullView = false;
		mFullViewWordCount = 0;
		mFullViewOccupyCount = 0;
		mFullViewPrevLineTopId = 0;
		mCreateCandidateDone = false;
		mIsCreateFullView = false;
		mIsScroll = false;

		clearCandidates();
		mConverter = converter;
		
		createWnnWordArray();

		setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
		/* create normalview */
		display1stCandidates(mConverter, mViewWidth);
		mTargetScrollWidth = mViewWidth / 2;
		mViewBodyHScroll.scrollTo(0, 0);
		mIsLockHScroll = false;
		/* create background normalview */
		startCandidateTask();
	}
	/*
	 * 
	 */
	private void startCandidateTask() {
		int viewWidth = mViewWidth * 1000;
		mCandidateTask = new TextCandidateTask(this);
		if (null != mCandidateTask) {
			mIsActiveTask = true;
			mIsCancelTask = false;
			mCandidateTask.execute(viewWidth);
		}
	}
	/*
	 * 
	 */
	public void checkCandidateTask() {
		if (null != mCandidateTask) {
            mCandidateTask.cancel(true);
			mCandidateTask = null;
		}
	}
	/*
	 * 
	 */
	public void hideCandidateTask() {
        int count = 100;
		if (null != mCandidateTask) {
            mCandidateTask.cancel(true);
            while(count > 0) {
                try {
                    Thread.sleep(20);
                } catch (Exception e) {
                    return;
                }
                //SystemClock.sleep(20);
                if (true == mIsCancelTask) {
                	break;
                }
                count--;
            }
			mCandidateTask = null;
		}
	}
	/*
	 * cancel callback 
	 */
	public void cancelTask() {
        mIsCancelTask = true;
	}
	/*
	 * 
	 */
	synchronized private void createWnnWordArray() {
	//private void createWnnWordArray() {
		WnnEngine converter = mConverter;
		if (null == converter) {
			return;
		}
		WnnWord result = null;
		int index = 0;
		int indentWidth = mViewWidth / FULL_VIEW_DIV;
		int maxindex = DISPLAY_LINE_MAX_COUNT * FULL_VIEW_DIV;
		int maxWidth = 0;
		do {
			result = converter.getNextCandidate();
			if (result == null) {
				break;
			}
			int textLength  = measureText(result.candidate, 0, result.candidate.length());
			int occupyCount = Math.min((textLength + indentWidth) / indentWidth, FULL_VIEW_DIV);
			int textWidth   = indentWidth * occupyCount;
			
			mWnnWordArray.add(index, result);
			mWnnWordTextLength.add(index, textWidth);
			mWnnWordOccupyCount.add(index, occupyCount);
			maxWidth += textWidth;
			index++;
		} while (index < maxindex);

		/* split wnnword tables */
		int iI;
		int size      = mWnnWordArray.size();
		int maxline   = getMaxLine();
		int halfWidth = (maxWidth / maxline);
		int calcWidth = 0;
		int divsize   = 0;
		int offset    = 0;

		halfWidth = Math.max(halfWidth, maxWidth - halfWidth);
		for (iI = 0; iI < size; ++iI, ++divsize) {
			if (halfWidth <= calcWidth) {
				break;
			}
			calcWidth += mWnnWordTextLength.get(iI);
		}
		for (iI = 0; iI < maxline; ++iI) {
			mWnnLineCount[iI]    = 0;
			mWnnLineOffset[iI]   = offset;
			mWnnLineCountMax[iI] = divsize;
			offset  += divsize;
			divsize  = mWnnWordArray.size() - divsize;
		}
	}

	/** @see CandidatesViewManager#getMaxLine */
	int getMaxLine() {
		int maxLine = (mPortrait) ? mPortraitLine : mLandscapeLine;
		return maxLine;
	}
	/*
	 *
	 */
	//synchronized private void display1stCandidates(WnnEngine converter, int width) {
	private void display1stCandidates(WnnEngine converter, int width) {
		if (converter == null) {
			return;
		}
		/* Get candidates */
		WnnWord result = null;
		int maxline = getMaxLine();
		int wordcount = 0;
		int size      = 0;
		int offset    = 0;
		int calcwidth = 0;
		int calcid    = 0;
		int iI;
		mWordCount = 0;
		for (iI = 0; iI < maxline; ++iI) {
			wordcount = mWnnLineCount[iI];
			size      = mWnnLineCountMax[iI];
			offset    = mWnnLineOffset[iI];
			calcwidth = 0;

			LinearLayout candidateList = mViewCandidateList1st;
            LinearLayout lineView = (LinearLayout)candidateList.getChildAt(iI);
			while (wordcount < size) {
				calcid = wordcount + offset;
				result = mWnnWordArray.get(calcid);
				calcwidth += mWnnWordTextLength.get(calcid);
				set1stCandidate(wordcount, calcid, result, mWnnWordTextLength.get(calcid), mWnnWordOccupyCount.get(calcid), lineView);
				wordcount++;
				if (calcwidth >= width) {
					break;
				}
			}
			mWordCount += wordcount;
			mWnnLineCount[iI] = wordcount;
		}
		m1stWordCount = mWordCount;

		if (mWordCount < 1) { /* no candidates */
			if (mAutoHideMode) {
				mWnn.setCandidatesViewShown(false);
				return;
			} else {
				mCanReadMore = false;
				mIsFullView = false;
				setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
			}
		}
		else{
			mCanReadMore = true;
		}
		if (!(mViewBody.isShown())) {
			mWnn.setCandidatesViewShown(true);
		}
	}
	/*
	 * 
	 */
	public int calc1stCandidates(int line, int width) {
		if (false == mIsActiveTask) {
			return 0;
		}

		int wordcount = 0;
		int size      = 0;
		int offset    = 0;
		int calcid    = 0;
		int calcwidth = 0;
		wordcount = mWnnLineCount[line];
		size      = mWnnLineCountMax[line];
		offset    = mWnnLineOffset[line];
		while(calcwidth < width) {
			if (wordcount >= size) {
				break;
			}
			calcid = wordcount + offset;
			WnnWord wnnword = null;
			try {
				wnnword = mWnnWordArray.get(calcid);
			} catch (Exception e) {
				mWnnLineCount[line] = mWnnLineCountMax[line];
				return 0;
			}
			calcwidth += mWnnWordTextLength.get(calcid);
			wordcount++;
		}
		return calcwidth;
	}
	/*
	 * 
	 */
	public boolean display1stCandidates(int line, int width) {
		if (false == mIsActiveTask) {
			return true;
		}
		boolean result = false;

		int wordcount = 0;
		int size      = 0;
		int offset    = 0;
		int calcid    = 0;
		int calcwidth = 0;
		wordcount = mWnnLineCount[line];
		size      = mWnnLineCountMax[line];
		offset    = mWnnLineOffset[line];
		LinearLayout candidateList = mViewCandidateList1st;
		LinearLayout lineView = (LinearLayout)candidateList.getChildAt(line);
		while(calcwidth < width) {
			if (wordcount >= size) {
				result = true;
				break;
			}
			calcid = wordcount + offset;
			WnnWord wnnword = null;
			try {
				wnnword = mWnnWordArray.get(calcid);
			} catch (Exception e) {
				mWnnLineCount[line] = mWnnLineCountMax[line];
				return true;
			}
			calcwidth += mWnnWordTextLength.get(calcid);
			set1stCandidate(wordcount, calcid, wnnword, mWnnWordTextLength.get(calcid), mWnnWordOccupyCount.get(calcid), lineView);
			wordcount++;
		}
		mWnnLineCount[line] = wordcount;
		return result;
	}
	/*
	 *
	 */
	public void invalidate1stView() {
		int maxLine  = getMaxLine();
		LinearLayout lineView = (LinearLayout)mViewCandidateList1st.getChildAt(0);
		int width = 0;
		for (int iI = 0; iI < maxLine; ++iI) {
			lineView = (LinearLayout)mViewCandidateList1st.getChildAt(iI);
			int getwidth = lineView.getWidth();
			if (width < getwidth) {
				width = getwidth;
			}
		}
		int getx = mViewBodyHScroll.getScrollX();
		int gety = mViewBodyHScroll.getScrollY();
		mViewCandidateList1st.invalidate();
		mViewBodyHScroll.scrollTo(getx, gety);
	}
	/*
	 * 
	 */
	public void display1stLastSetup() {
		m1stWordCount = mWordCount;
		mIsActiveTask = false;
	}
	/**
	 * Display the candidates.
	 * 
	 * @param converter  {@link WnnEngine} which holds candidates.
	 * @param dispFirst  Whether it is the first time displaying the candidates
	 * @param maxLine    The maximum number of displaying lines
	 */
	//synchronized private void displayCandidates(WnnEngine converter, boolean dispFirst, int maxLine) {
	private void displayCandidates(WnnEngine converter, boolean dispFirst, int maxLine) {
		if (converter == null) {
			return;
		}
		boolean isBreak = false;

		/* Get candidates */
		WnnWord result = null;
		int size = mWnnWordArray.size();
		while (mWordCount < size) {
			result = mWnnWordArray.get(mWordCount);
			setCandidate(result, mWnnWordTextLength.get(mWordCount), mWnnWordOccupyCount.get(mWordCount));
			if (dispFirst && (maxLine < mLineCount)) {
				mCanReadMore = true;
				isBreak = true;
				break;
			}
		}

		if (!isBreak && !mCreateCandidateDone) {
			/* align left if necessary */
			createNextLine();
			mCreateCandidateDone = true;
		}
        
		if (mWordCount < 1) { /* no candidates */
			if (mAutoHideMode) {
				mWnn.setCandidatesViewShown(false);
				return;
			} else {
				mCanReadMore = false;
				mIsFullView = false;
				setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
			}
		}
		if (!(mViewBody.isShown())) {
			mWnn.setCandidatesViewShown(true);
		}
		return;
	}

	/**
	 * 
	 */
	private void set1stCandidate(int viewindex, int inid, WnnWord word, int textLength, int occupyCount, LinearLayout lineView) {
		TextView textView;
		int indentWidth = mViewWidth / FULL_VIEW_DIV;
		
		//int width = indentWidth * occupyCount;
		int width = textLength;
		int height = getCandidateMinimumHeight();
		boolean iscreate = false;
		textView = (TextView) lineView.getChildAt(viewindex);
		if (textView == null) {
			textView = createCandidateView();
			iscreate = true;
		}
		
		int textId = inid + 1;
		textView.setWidth(width);
		textView.setHeight(height);
		textView.setText(word.candidate);
		textView.setTextColor(mTextColor);
		textView.setId(textId);
		textView.setVisibility(View.VISIBLE);
		textView.setPressed(false);
		textView.setEllipsize(TextUtils.TruncateAt.END);

		textView.setOnClickListener(mCandidateOnClick);
		textView.setOnLongClickListener(mCandidateOnLongClick);
		textView.setBackgroundResource(R.drawable.cand_back);

		textView.setOnTouchListener(mCandidateOnTouch);

		checkImageSpan(textView, word);
		if (true == iscreate) {
			lineView.addView(textView);
		}
	}
	/**
	 * Add a candidate into the list.
	 * @param word        A candidate word
	 */
	private void setCandidate(WnnWord word, int textLength, int occupyCount) {
		TextView textView;
		int indentWidth = mViewWidth / FULL_VIEW_DIV;

		RelativeLayout layout = mViewCandidateList2nd;

		int width = indentWidth * occupyCount;
		int height = getCandidateMinimumHeight();
		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);

		if (mFullViewPrevLineTopId == 0) {
			params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
		} else {
			params.addRule(RelativeLayout.BELOW, mFullViewPrevLineTopId);
		}

		if (mFullViewOccupyCount == 0) {
			params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
		} else {
			params.addRule(RelativeLayout.RIGHT_OF, (mWordCount));
		}

		textView = (TextView) layout.getChildAt(mFullViewWordCount);
		if (textView == null) {
			textView = createCandidateView();
			textView.setLayoutParams(params);

			mViewCandidateList2nd.addView(textView);
		} else {
			mViewCandidateList2nd.updateViewLayout(textView, params);
		}
		int textId = mWordCount+1;

		mFullViewOccupyCount += occupyCount;
		mFullViewWordCount++;
		mFullViewPrevView = textView;
		mFullViewPrevParams = params;

		textView.setText(word.candidate);
		textView.setTextColor(mTextColor);
		textView.setId(textId);
		textView.setVisibility(View.VISIBLE);
		textView.setPressed(false);
		textView.setEllipsize(TextUtils.TruncateAt.END);

		textView.setOnClickListener(mCandidateOnClick);
		textView.setOnLongClickListener(mCandidateOnLongClick);
		textView.setBackgroundResource(R.drawable.cand_back);

		textView.setOnTouchListener(mCandidateOnTouch);

		checkImageSpan(textView, word);
		mWordCount++;
		mTotalLastId = textId;

		if (FULL_VIEW_DIV < (mFullViewOccupyCount + occupyCount)) {
			if (FULL_VIEW_DIV != mFullViewOccupyCount) {
				mFullViewPrevParams.width += (FULL_VIEW_DIV - mFullViewOccupyCount) * indentWidth;
				mViewCandidateList2nd.updateViewLayout(mFullViewPrevView, mFullViewPrevParams);
			}
			if (getMaxLine() >= mLineCount) {
				mToplineLastId = textId;
			}
			createNextLine();
		}
	}
	/*
	 * check Image Span
	 */
	private void checkImageSpan(TextView textView, WnnWord word) {
		ImageSpan span = null;
		if (word.candidate.equals(" ")) {
			span = new ImageSpan(mWnn, R.drawable.word_half_space, DynamicDrawableSpan.ALIGN_BASELINE);
		} else if (word.candidate.equals("\u3000" /* full-width space */)) {
			span = new ImageSpan(mWnn, R.drawable.word_full_space, DynamicDrawableSpan.ALIGN_BASELINE);
		}
		// check docomo emoji
		Integer getres = getDocomoEmojiRes(word);
		if (null != getres) {
			span = new ImageSpan(mWnn, getres.intValue(), DynamicDrawableSpan.ALIGN_BASELINE);
		}
		if (span != null) {
			SpannableString spannable = new SpannableString("   ");
			spannable.setSpan(span, 1, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 
			textView.setText(spannable);
		}
	}
	/**
	 * Create a view for a candidate.
	 * @return the view
	 */
	public TextView createCandidateView() {
		TextView text = new TextView(mViewBodyVScroll.getContext());
		text.setTextSize(20);
		text.setBackgroundResource(R.drawable.cand_back);
		text.setGravity(Gravity.CENTER);
		text.setSingleLine();
		text.setPadding(1, 1, 1, 1);
		text.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
														   ViewGroup.LayoutParams.WRAP_CONTENT,
														   1.0f));
		text.setMinHeight(getCandidateMinimumHeight());
		text.setMinimumWidth(getCandidateMinimumWidth());
		return text;
	}

    /**
     * Clear the list of the normal candidate view.
     */
    private void clearNormalViewCandidate() {
        LinearLayout candidateList = mViewCandidateList1st;
        int lineNum = candidateList.getChildCount();
        for (int i = 0; i < lineNum; i++) {
            LinearLayout lineView = (LinearLayout)candidateList.getChildAt(i);
            int size = lineView.getChildCount();
            for (int j = 0; j < size; j++) {
                View v = lineView.getChildAt(j);
                v.setVisibility(View.GONE);
            }
        }
    }

	/** @see CandidatesViewManager#clearCandidates */
	public void clearCandidates() {
		int size = 0;
		checkCandidateTask();
        clearNormalViewCandidate();

		RelativeLayout layout2nd = mViewCandidateList2nd;
		size = layout2nd.getChildCount();
		for (int i = 0; i < size; i++) {
			View v = layout2nd.getChildAt(i);
			v.setVisibility(View.GONE);
		}
    
		mLineCount = 1;
		mWordCount = 0;
		m1stWordCount = 0;
		mToplineLastId = 0;
		mTotalLastId   = 0;
		mWnnWordArray.clear();
		mWnnWordTextLength.clear();
		mWnnWordOccupyCount.clear();

		mIsFullView = false;
		setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
		if (mAutoHideMode) {
			setViewLayout(CandidatesViewManager.VIEW_TYPE_CLOSE);
		}

		if (mAutoHideMode && mViewBody.isShown()) {
			mWnn.setCandidatesViewShown(false);
		}
		mCanReadMore = false;
	}
	/** clear normalCandidate */
	private void clearNormalCandidates() {
		if (false == mIsCreateFullView) {
			return;
		}
		if (mTotalLastId > mToplineLastId) {
			mCanReadMore = true;
			mIsScroll    = false;
		}
	}
	/** view fullCandidate */
	private void visibleFullCandidate() {
		if (false == mIsCreateFullView) {
			return;
		}
		RelativeLayout layout = mViewCandidateList2nd;
		int size = layout.getChildCount();
		if (size > mTotalLastId) {
			size = mTotalLastId;
		}
		for (int i = 0; i < size; i++) {
			View v = layout.getChildAt(i);
			v.setVisibility(View.VISIBLE);
		}
		if (mTotalLastId != 0) {
			mCanReadMore = false;
		}
	}
	/** @see CandidatesViewManager#setPreferences */
	public void setPreferences(SharedPreferences pref) {
		try {
			if (pref.getBoolean("key_vibration", false)) {
				mVibrator = (Vibrator)mWnn.getSystemService(Context.VIBRATOR_SERVICE);
			} else {
				mVibrator = null;
			}
			if (pref.getBoolean("key_sound", false)) {
				mSound = MediaPlayer.create(mWnn, R.raw.type);
			} else {
				mSound = null;
			}
		} catch (Exception ex) {
			Log.d("iwnn", "NO VIBRATOR");
		}
	}
    
	/**
	 * Process {@code OpenWnnEvent.CANDIDATE_VIEW_TOUCH} event.
	 * 
	 * @return      {@code true} if event is processed; {@code false} if otherwise
	 */
	public boolean onTouchSync() {
		return mGestureDetector.onTouchEvent(mMotionEvent);
	}

	/**
	 * Select a candidate.
	 * <br>
	 * This method notices the selected word to {@link NicoWnn}.
	 *
	 * @param word  The selected word
	 */
	private void selectCandidate(WnnWord word) {
		setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
		if (mVibrator != null) {
			try { mVibrator.vibrate(30); } catch (Exception ex) { }
		}
		if (mSound != null) {
			try { mSound.seekTo(0); mSound.start(); } catch (Exception ex) { }
		}
		mIsLockHScroll = true;
		mWnn.onEvent(new NicoWnnEvent(NicoWnnEvent.SELECT_CANDIDATE, word));
	}

	/** @see android.view.GestureDetector.OnGestureListener#onDown */
	public boolean onDown(MotionEvent arg0) {
		return false;
	}

	/** @see android.view.GestureDetector.OnGestureListener#onFling */
	public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
		if (mIsScaleUp) {
			return false;
		}
		boolean consumed = false;
		
		int gety = 0;
		if (arg0 != null && arg1 != null) {
			gety = (int)(arg1.getY() - arg0.getY());
		}
		else{
			gety = (int)arg3;
		}

		//if (arg3 < (float)-(getCandidateMinimumHeight() * 10)) {
		if (gety < -(getCandidateMinimumHeight() * 2)) {
			if ((mViewType == CandidatesViewManager.VIEW_TYPE_NORMAL) && mCanReadMore) {
				if (mVibrator != null) {
					try { mVibrator.vibrate(30); } catch (Exception ex) { }
				}
				mIsFullView = true;
				mIsScroll   = false; // reset scroll flag
				mWnn.onEvent(new NicoWnnEvent(NicoWnnEvent.LIST_CANDIDATES_FULL));
				consumed = true;
			}
		}
		//else if (arg3 > (float)getCandidateMinimumHeight()) {
		else if (gety > (getCandidateMinimumHeight() * 1)) {
			if ((mViewBodyVScroll.getScrollY() == 0) && (true == mIsFullView)) {
				if (true == mIsScroll) {
					mIsScroll = false;
					return false;
				}
				if (mVibrator != null) {
					try { mVibrator.vibrate(30); } catch (Exception ex) { }
				}
				mIsFullView = false;
				mWnn.onEvent(new NicoWnnEvent(NicoWnnEvent.LIST_CANDIDATES_NORMAL));
				consumed = true;
			}
		}
		return consumed;
	}

	/** @see android.view.GestureDetector.OnGestureListener#onLongPress */
	public void onLongPress(MotionEvent arg0) {
		return;
	}

	/** @see android.view.GestureDetector.OnGestureListener#onScroll */
	public boolean onScroll(MotionEvent arg0, MotionEvent arg1, float arg2, float arg3) {
		if (mViewBodyVScroll.getScrollY() != 0) {
			mIsScroll = true;
		}
		return false;
	}
	/** @see android.view.GestureDetector.OnGestureListener#onShowPress */
	public void onShowPress(MotionEvent arg0) {
	}

	/** @see android.view.GestureDetector.OnGestureListener#onSingleTapUp */
	public boolean onSingleTapUp(MotionEvent arg0) {
		return false;
	}
    
	/**
	 * Retrieve the width of string to draw.
	 * 
	 * @param text          The string
	 * @param start         The start position (specified by the number of character)
	 * @param end           The end position (specified by the number of character)
	 * @return          The width of string to draw
	 */ 
	public int measureText(CharSequence text, int start, int end) {
        TextPaint paint = mViewCandidateTemplate.getPaint();
        int getwidth = Math.max((int)paint.measureText(text, start, end), CANDIDATE_MINIMUM_WIDTH);
        //int getwidth = Math.max((int)(end - start) * 40, CANDIDATE_MINIMUM_WIDTH);
		return getwidth;
	}

	/**
	 * Switch list/enlarge view mode.
	 * @param up  {@code true}:enlarge, {@code false}:list
	 * @param word  The candidate word to be enlarged.
	 */
	private void setViewScaleUp(boolean up, WnnWord word) {
		if (up == mIsScaleUp || (mViewScaleUp == null)) {
			return;
		}
		if (up) {
			setViewLayout(CandidatesViewManager.VIEW_TYPE_NORMAL);
			mViewCandidateList1st.setVisibility(View.GONE);
			mViewCandidateList2nd.setVisibility(View.GONE);
			mViewCandidateBase.setMinimumHeight(-1);
			mViewCandidateBase.addView(mViewScaleUp);
			TextView text = (TextView)mViewScaleUp.findViewById(R.id.candidate_scale_up_text);
			text.setText(word.candidate);
			checkImageSpan(text, word);
			if (!mPortrait) {
				Resources r = mViewBodyVScroll.getContext().getResources();
				text.setTextSize(r.getDimensionPixelSize(R.dimen.candidate_delete_word_size_landscape));
			}

			mIsScaleUp = true;
		} else {
			mIsScaleUp = false;
			mViewCandidateBase.removeView(mViewScaleUp);
		}
	}

	/**
	 * Create a layout for the next line.
	 */
	private void createNextLine() {
		mFullViewOccupyCount = 0;
		mFullViewPrevLineTopId = mFullViewPrevView.getId();
		mLineCount++;
	}

	/**
	 * @return the minimum width of a candidate view.
	 */
	public static int getCandidateMinimumWidth() {
        //return (int)(CANDIDATE_MINIMUM_WIDTH * mMetrics.density);
        return (int)(CANDIDATE_MINIMUM_WIDTH);
	}

	/**
	 * @return the minimum height of a candidate view.
	 */
	public static int getCandidateMinimumHeight() {
		//return (int)((float)candidateViewDataTable[mCandidateViewHeightIndex] * mMetrics.density);
		return (int)((float)candidateViewDataTable[mCandidateViewHeightIndex]);
	}

	/**
	 * get docomo-emoji resource
	 */
	private Integer getDocomoEmojiRes(WnnWord word) {
		return DOCOMO_EMOJI_TABLE.get(word.candidate);
	}

}
