package com.example.wordbook;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.SearchManager;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.WrapperListAdapter;

import com.example.wordbook.common.Common;
import com.example.wordbook.common.DBAccess;
import com.example.wordbook.common.Data;

/**
 * 検索結果画面クラス
 */
public class SearchActivity extends Activity {

	/** TAG */
	private static final String TAG = SearchActivity.class.getSimpleName();

	/** フラグ選択ダイアログ */
	private static final int DIALOG_FLAG = 1;
	/** メモ編集ダイアログ */
	private static final int DIALOG_EDIT = 2;
	/** コマンド選択ダイアログ */
	private static final int DIALOG_COMM = 7;

	/** インスタンス状態用データ先頭キー */
	private static final String IS_TOP = "top";
	/** インスタンス状態用データ位置キー */
	private static final String IS_POS = "pos";
	/** インスタンス状態用データリストキー */
	private static final String IS_DATA = "data";

	/** 短縮コマンド */
	private int mComm;

	/** 検索データ位置 */
	private int mPos = 0;
	/** 検索データリスト */
	private List<Data> mData = new ArrayList<Data>();

	/** ListView */
	private ListView mListView;
	/** ListView footer */
	private View mFooter;

	/** ExecutorService */
	private ExecutorService mES;

	/*
	 * (非 Javadoc)
	 * 
	 * @see android.app.Activity#onCreate(android.os.Bundle)
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.d(TAG, "onCreate()");

		// 状態復帰
		int top = 0;
		if (savedInstanceState != null) {
			top = savedInstanceState.getInt(IS_TOP);
			mPos = savedInstanceState.getInt(IS_POS);
			mData = (List<Data>) savedInstanceState.getSerializable(IS_DATA);
		}
		Log.d(TAG, "top=" + top + "/pos=" + mPos + "/data=" + mData.size());

		// 設定取得
		mComm = Common.getComm(getApplicationContext());

		// レイアウト設定
		setContentView(R.layout.search);
		// タイトル設定
		// searchableとmanifestで指定するandroid:labelは一致させておくべき
		setTitle(getString(R.string.title_search));

		// ListView設定
		mFooter = getLayoutInflater().inflate(R.layout.list_footer, null);
		mListView = (ListView) findViewById(R.id.list);
		mListView.addFooterView(mFooter);
		// アダプタ設定
		WBListAdapter adp = new WBListAdapter(this, R.layout.list_wblist, mData);
		mListView.setAdapter(adp);
		// イベントリスナ設定
		mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				doItemClick(parent, position, id);
			}
		});
		mListView.setFastScrollEnabled(true);
		mListView.setSelectionFromTop(top, 0);

		if (mData.size() == 0) {
			// 検索実行
			doSearch(getIntent());
		} else {
			// まだレイアウトが生成されていないため最大値を設定
			DisplayMetrics dm = new DisplayMetrics();
			getWindowManager().getDefaultDisplay().getMetrics(dm);
			// setVisibility()のみでは表示領域が残るためsetPadding()で調整
			mFooter.setVisibility(View.GONE);
			mFooter.setPadding(0, -dm.heightPixels, 0, 0);
		}
	}

	/*
	 * (非 Javadoc)
	 * 
	 * @see android.app.Activity#onNewIntent(android.content.Intent)
	 */
	@Override
	protected void onNewIntent(Intent intent) {
		Log.d(TAG, "onNewIntent()");

		// まだアクティビティが再起動されていないため設定が必要
		setIntent(intent);
		// 検索実行
		doSearch(intent);
	}

	/*
	 * (非 Javadoc)
	 * 
	 * @see android.app.Activity#onResume()
	 */
	@Override
	protected void onResume() {
		super.onResume();
		Log.d(TAG, "onResume()");

		// ExecutorService開始
		mES = Executors.newSingleThreadExecutor();
	}

	/*
	 * (非 Javadoc)
	 * 
	 * @see android.app.Activity#onPause()
	 */
	@Override
	protected void onPause() {
		super.onPause();
		Log.d(TAG, "onPause()");

		// タスク動作時はキャンセル
		if (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING) {
			Log.w(TAG, "onPause()=RUNNING");
			// キャンセル
			mTask.cancel(true);
		}

		// ExecutorService終了
		final long TIMEOUT = 100;
		mES.shutdown();
		try {
			// タスク終了待ち
			if (!mES.awaitTermination(TIMEOUT, TimeUnit.MILLISECONDS)) {
				mES.shutdownNow();
				Log.w(TAG, "onPause()=TIMEOUT");
			}
		} catch (InterruptedException e) {
			mES.shutdownNow();
			Thread.currentThread().interrupt();
			Log.e(TAG, "onPause()=InterruptedException");
			e.printStackTrace();
		}
	}

	/*
	 * (非 Javadoc)
	 * 
	 * @see android.app.Activity#onSaveInstanceState(android.os.Bundle)
	 */
	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		Log.d(TAG, "onSaveInstanceState()");

		// 状態保存
		outState.putInt(IS_TOP, mListView.getFirstVisiblePosition());
		outState.putInt(IS_POS, mPos);
		outState.putSerializable(IS_DATA, (Serializable) mData);
	}

	/*
	 * (非 Javadoc)
	 * 
	 * @see android.app.Activity#onCreateDialog(int)
	 */
	@Override
	protected Dialog onCreateDialog(int id) {

		switch (id) {
		case DIALOG_FLAG:
			// フラグ選択ダイアログ生成
			return getDialogFlag();
		case DIALOG_EDIT:
			// メモ編集ダイアログ生成
			return getDialogEdit();
		case DIALOG_COMM:
			// コマンド選択ダイアログ生成
			return getDialogComm();
		default:
			Log.w(TAG, "onCreateDialog() id=" + id);
			return null;
		}
	}

	/*
	 * (非 Javadoc)
	 * 
	 * @see android.app.Activity#onPrepareDialog(int, android.app.Dialog)
	 */
	@Override
	protected void onPrepareDialog(int id, Dialog dialog) {

		switch (id) {
		case DIALOG_FLAG:
			// フラグ選択ダイアログ準備
			// 処理無し
			break;
		case DIALOG_EDIT:
			// メモ編集ダイアログ準備
			setDialogEdit(dialog);
			break;
		case DIALOG_COMM:
			// コマンド選択ダイアログ準備
			// 処理無し
			break;
		default:
			Log.w(TAG, "onPrepareDialog() id=" + id);
			break;
		}
	}

	/*
	 * (非 Javadoc)
	 * 
	 * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu)
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {

		getMenuInflater().inflate(R.menu.search, menu);

		return true;
	}

	/*
	 * (非 Javadoc)
	 * 
	 * @see android.app.Activity#onMenuItemSelected(int, android.view.MenuItem)
	 */
	@Override
	public boolean onMenuItemSelected(int featureId, MenuItem item) {

		int id = item.getItemId();
		switch (id) {
		case R.id.menu_search:
			onSearchRequested();
			break;
		default:
			Log.w(TAG, "onMenuItemSelected() id=" + id);
			break;
		}

		return true;
	}

	/**
	 * フラグ選択ダイアログ生成
	 * 
	 * @return フラグ選択ダイアログ
	 */
	private AlertDialog getDialogFlag() {

		// ダイアログ生成
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setCancelable(false);
		builder.setTitle(getString(R.string.item_flag));
		// イベントリスナ設定
		builder.setNegativeButton(android.R.string.cancel, null);
		// アダプタ設定
		FlagAdapter adapter = new FlagAdapter(this,
				Common.getFlagItemList(getApplicationContext()),
				R.layout.dlg_flag, new String[] { Common.FLAG_TEXT },
				new int[] { R.id.textView });
		builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// 選択フラグ更新
				mData.get(mPos).flag = which;
				// 更新通知
				notifyUpdate();
				// データ更新タスク呼び出し
				saveData(getIndex(mPos), mData.get(mPos));
			}
		});

		return builder.create();
	}

	/**
	 * メモ編集ダイアログ生成
	 * 
	 * @return メモ編集ダイアログ
	 */
	private AlertDialog getDialogEdit() {

		// ダイアログ生成
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setCancelable(false);
		builder.setTitle(getString(R.string.item_edit));
		// インフレート対象にダイアログ用テーマを反映
		LayoutInflater inflater = builder.create().getLayoutInflater();
		View view = inflater.inflate(R.layout.dlg_edit, null);
		// ビュー取得
		final EditText edit = (EditText) view.findViewById(R.id.editText);
		// イベントリスナ設定
		builder.setPositiveButton(android.R.string.ok,
				new DialogInterface.OnClickListener() {
					@Override
					public void onClick(DialogInterface dialog, int which) {
						// 選択メモ更新
						mData.get(mPos).data2 = edit.getText().toString();
						// 更新通知
						notifyUpdate();
						// データ更新タスク呼び出し
						saveData(getIndex(mPos), mData.get(mPos));
					}
				});
		builder.setNegativeButton(android.R.string.cancel, null);

		builder.setView(view);
		return builder.create();
	}

	/**
	 * コマンド選択ダイアログ生成
	 * 
	 * @return コマンド選択ダイアログ
	 */
	private AlertDialog getDialogComm() {

		// ダイアログ生成
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setCancelable(false);
		builder.setTitle(getString(R.string.title_commdialog));
		// イベントリスナ設定
		builder.setNegativeButton(android.R.string.cancel, null);
		// アイテム設定
		builder.setItems(R.array.comm, new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				// クリック＆コマンド識別
				doCommClick(which, mPos);
			}
		});

		return builder.create();
	}

	/**
	 * メモ編集ダイアログ準備
	 * 
	 * @param dialog
	 *            ダイアログ
	 */
	private void setDialogEdit(Dialog dialog) {

		// 現状メモ設定
		EditText edit = (EditText) dialog.findViewById(R.id.editText);
		edit.setText(mData.get(mPos).data2);
	}

	/**
	 * アイテムクリック処理
	 * 
	 * @param parent
	 *            AdapterView
	 * @param position
	 *            Position
	 * @param id
	 *            ID
	 */
	private void doItemClick(AdapterView<?> parent, int position, long id) {
		Log.d(TAG, "doItemClick() id=" + id);

		// クリック領域識別（アイコン）
		if (id < 0) {
			// アダプタからロングクリックイベントを送信できないためIDで識別
			if (id == Common.ITEM_CLICK) {
				// クリック＆コマンド識別
				doCommClick(mComm, position);
			} else if (id == Common.ITEM_LONG_CLICK) {
				// ロングクリック
				doCommLongClick(mComm, position);
			} else {
				Log.w(TAG, "doItemClick() id=" + id);
			}
		}
	}

	/**
	 * アイコンクリック処理
	 * 
	 * @param command
	 *            Command
	 * @param position
	 *            Position
	 */
	@SuppressWarnings("deprecation")
	private void doCommClick(int command, int position) {

		// 選択アイテム位置保存
		mPos = position;
		// コマンド識別
		switch (command) {
		case 0:
			// フラグ選択ダイアログ表示
			showDialog(DIALOG_FLAG);
			break;
		case 1:
			// メモ編集ダイアログ表示
			showDialog(DIALOG_EDIT);
			break;
		case 2:
			// TODO
			Log.d(TAG, "SOUND");
			break;
		case 3:
			// TODO
			Log.d(TAG, "LINK");
			break;
		default:
			Log.w(TAG, "doCommClick() command=" + command);
			break;
		}
	}

	/**
	 * アイコンロングクリック処理
	 * 
	 * @param command
	 *            Command
	 * @param position
	 *            Position
	 */
	@SuppressWarnings("deprecation")
	private void doCommLongClick(int command, int position) {

		// 選択アイテム位置保存
		mPos = position;
		// コマンド選択ダイアログ表示
		showDialog(DIALOG_COMM);
	}

	/**
	 * 検索実行
	 * 
	 * @param intent
	 *            インテント
	 */
	private void doSearch(Intent intent) {

		String action = intent.getAction();
		if (Intent.ACTION_SEARCH.equals(action)) {
			Log.d(TAG, "ACTION_SEARCH");
			// QUERYには検索欄の値が設定されている
			String data = intent.getStringExtra(SearchManager.QUERY);
			if (!TextUtils.isEmpty(data)) {
				searchData(data, "");
			}
		} else if (Intent.ACTION_VIEW.equals(action)) {
			Log.d(TAG, "ACTION_VIEW");
			// DATAには選択候補の質問と解答が設定されている
			String[] data = intent.getData().toString().split(",");
			if (data.length == 2) {
				searchData(data[0], data[1]);
			}
		} else {
			Log.w(TAG, "doSearch() action=" + action);
		}
	}

	/**
	 * インデックス取得
	 * 
	 * @param pos
	 *            選択位置
	 * @return インデックス
	 */
	private int getIndex(int pos) {
		int value = 0;

		// アイテム用DataにはFILE値が含まれないためタイトル用Dataから取得する
		for (int i = pos - 1; i >= 0; i--) {
			if (mData.get(i).num == 0) {
				value = mData.get(i).stat;
				break;
			}
		}

		return value;
	}

	/**
	 * 更新通知
	 */
	private void notifyUpdate() {

		// フッタ設定時対応更新通知
		if (mListView.getAdapter() instanceof WrapperListAdapter) {
			((BaseAdapter) ((WrapperListAdapter) mListView.getAdapter())
					.getWrappedAdapter()).notifyDataSetChanged();
		} else {
			((BaseAdapter) mListView.getAdapter()).notifyDataSetChanged();
		}
	}

	/**
	 * データ更新タスク呼び出し
	 * 
	 * @param index
	 *            インデックス
	 * @param data
	 *            更新データ
	 */
	private void saveData(int index, Data data) {

		// データ更新タスク依頼
		try {
			if (mES.submit(new DBAccess.SaveDataTask(getApplicationContext(),
					index, data)) == null) {
				Log.w(TAG, "saveData() Future==null");
			}
		} catch (RejectedExecutionException e) {
			Log.e(TAG, "saveData()=RejectedExecutionException");
			e.printStackTrace();
		}
	}

	/** 検索結果表示タスク */
	private AsyncTask<Void, Void, List<Data>> mTask = null;

	/**
	 * 検索結果表示タスク呼び出し
	 * 
	 * @param data1
	 *            質問／検索欄の値
	 * @param data2
	 *            解答
	 */
	private void searchData(final String data1, final String data2) {

		// 検索結果表示タスク動作時は処理無し
		if (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING) {
			Log.w(TAG, "loadData()=RUNNING");
			return;
		}

		// 検索結果表示タスク実行
		mTask = new AsyncTask<Void, Void, List<Data>>() {

			@Override
			protected void onPreExecute() {
				super.onPreExecute();
				Log.d(TAG, "onPreExecute()");

				// setVisibility()のみでは表示領域が残るためsetPadding()で調整
				mFooter.setVisibility(View.VISIBLE);
				mFooter.setPadding(0, 0, 0, 0);
				// onNewIntent()対応
				mListView.setVisibility(View.VISIBLE);

				// 検索データ削除
				mData.clear();
				notifyUpdate();
			}

			@Override
			protected void onPostExecute(List<Data> result) {

				// setVisibility()のみでは表示領域が残るためsetPadding()で調整
				mFooter.setVisibility(View.GONE);
				mFooter.setPadding(0, -mFooter.getHeight(), 0, 0);
				if (result.size() > 0) {
					// 検索データ追加
					mData.addAll(result);
					notifyUpdate();
					// 表示位置設定
					mListView.setSelectionFromTop(0, 0);
				} else {
					// フッタ設定時はsetEmptyView()が無効のため手動で可視化
					findViewById(R.id.empty).setVisibility(View.VISIBLE);
					mListView.setVisibility(View.GONE);
				}

				Log.d(TAG, "onPostExecute()=" + result.size());
				super.onPostExecute(result);
			}

			@Override
			protected List<Data> doInBackground(Void... params) {
				Log.d(TAG, "doInBackground()=" + data1 + "/" + data2);

				// 単語帳DB検索結果データリスト取得
				return DBAccess.getDBSearchResultList(getApplicationContext(),
						data1, data2);
			};
		}.execute();

	}

}
