package com.example.wordbook;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
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.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
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 WBListActivity extends Activity implements OnScrollListener {

	/** TAG */
	private static final String TAG = WBListActivity.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";

	/** 単語帳DBデータインデックス */
	private int mIndex;
	/** 単語帳DBデータ項目数 */
	private int mCount;

	/** 出題方法 */
	private int mMode;
	/** 出題順序 */
	private int mSort;
	/** 短縮コマンド */
	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
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		Log.d(TAG, "onCreate()");

		// 単語帳DBデータ情報取得
		String title = "";
		Bundle ext = getIntent().getExtras();
		if (ext == null) {
			Log.w(TAG, "onCreate() Intent==null");
			finish();
		} else {
			mIndex = ext.getInt(Common.INTENT_INDEX, 0);
			mCount = ext.getInt(Common.INTENT_COUNT, 0);
			title = ext.getString(Common.INTENT_TITLE);
		}
		Log.d(TAG, "index=" + mIndex + "/count=" + mCount + "/title=" + title);

		// 状態復帰
		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());

		// 設定取得
		mMode = Common.getMode(getApplicationContext());
		mSort = Common.getSort(getApplicationContext());
		mComm = Common.getComm(getApplicationContext());

		// レイアウト設定
		setContentView(R.layout.wblist);
		// タイトル設定
		setTitle(title);

		// ListView設定
		mFooter = getLayoutInflater().inflate(R.layout.list_footer, null);
		mListView = (ListView) findViewById(R.id.list);
		// 試験データ追加済み時はフッタ削除
		if (mData.size() != mCount) {
			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);
		mListView.setOnScrollListener(this);
	}

	/*
	 * (非 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.widget.AbsListView.OnScrollListener#onScroll(android.widget.
	 * AbsListView, int, int, int)
	 */
	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		Log.v(TAG, "onScroll()=" + totalItemCount + "/" + firstVisibleItem
				+ "/" + visibleItemCount);

		if (totalItemCount == firstVisibleItem + visibleItemCount) {
			// 最終行表示済み時は追加読み込み
			loadData();
		} else if (totalItemCount < firstVisibleItem + visibleItemCount) {
			// 異常時は位置補正（フッタ処理時）
			view.setSelection(totalItemCount - visibleItemCount < 0 ? 0
					: totalItemCount - visibleItemCount);
		}
	}

	/*
	 * (非 Javadoc)
	 * 
	 * @see
	 * android.widget.AbsListView.OnScrollListener#onScrollStateChanged(android
	 * .widget.AbsListView, int)
	 */
	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
	}

	/*
	 * (非 Javadoc)
	 * 
	 * @see android.app.Activity#finish()
	 */
	@Override
	public void finish() {
		super.finish();
		// overridePendingTransition(0, 0);
	}

	/**
	 * フラグ選択ダイアログ生成
	 * 
	 * @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(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(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);
			}
		} else {
			// 表示状態更新
			Data data = (Data) parent.getItemAtPosition(position);
			switch (data.stat & Data.STAT_SHOW) {
			case Data.STAT_QUESTION:
				// break;
			case Data.STAT_ANSWER:
				// 非表示->表示
				data.stat = Data.STAT_SHOW;
				break;
			case Data.STAT_SHOW:
				// 表示->非表示
				data.stat = mMode;
				break;
			default:
				Log.w(TAG, "doItemClick() stat=" + data.stat);
				break;
			}
			// 更新通知
			notifyUpdate();
			// 単語帳DBデータ更新
			saveData(data);
		}
	}

	/**
	 * アイコンクリック処理
	 * 
	 * @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);
	}

	/**
	 * 更新通知
	 */
	private void notifyUpdate() {

		// フッタ設定時対応更新通知
		if (mListView.getAdapter() instanceof WrapperListAdapter) {
			((BaseAdapter) ((WrapperListAdapter) mListView.getAdapter())
					.getWrappedAdapter()).notifyDataSetChanged();
		} else {
			((BaseAdapter) mListView.getAdapter()).notifyDataSetChanged();
		}
	}

	/**
	 * データ更新タスク呼び出し
	 * 
	 * @param data
	 *            更新データ
	 */
	private void saveData(Data data) {

		// データ更新タスク依頼
		try {
			if (mES.submit(new DBAccess.SaveDataTask(getApplicationContext(),
					mIndex, 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;

	/** 試験データ追加フラグ */
	private List<Integer> mLoad = new ArrayList<Integer>();

	/**
	 * 試験データ追加タスク呼び出し
	 */
	private void loadData() {
		final int ADD_SIZE = 100;

		// 試験データ追加済み時は処理無し
		if (mData.size() == mCount) {
			Log.w(TAG, "loadData() size=" + mCount);
			return;
		}

		// 試験データ追加タスク動作時は処理無し
		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()");

				// ランダム時の初回呼び出し時のみ追加フラグリストを初期化
				if (mSort == 0 && mData.size() == 0 && mLoad.size() == 0) {
					// 識別子は1から
					for (int i = 1; i <= mCount; i++) {
						mLoad.add(i);
					}
					Collections.shuffle(mLoad);
				}
			}

			@Override
			protected void onPostExecute(List<Data> result) {

				if (result.size() > 0) {
					// 試験データ更新
					mData.addAll(result);
					// 更新通知
					notifyUpdate();
					if (mData.size() == mCount) {
						// 試験データ追加済み時はフッタ削除
						mListView.removeFooterView(mFooter);
					}
				} else {
					// 試験データ追加無し時はフッタ削除
					mListView.removeFooterView(mFooter);
					if (mData.size() == 0) {
						// フッタ設定時は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()");
				List<Data> data = null;

				// 単語帳DBデータリスト取得
				if (mSort == 0) {
					// ランダム時は追加フラグリストから取得候補リストを取得
					List<Integer> in = new ArrayList<Integer>();
					for (int i = 0; i < ADD_SIZE && i < mLoad.size(); i++) {
						in.add(mLoad.get(i));
					}
					if (in.size() == 0) {
						data = new ArrayList<Data>();
					} else {
						data = DBAccess.getDBDataList(getApplicationContext(),
								mIndex, 0, ADD_SIZE, 0, in);
					}
					// 追加フラグリスト更新
					mLoad.removeAll(in);
				} else {
					data = DBAccess.getDBDataList(getApplicationContext(),
							mIndex, mData.size(), ADD_SIZE, mSort, null);
				}

				// 表示状態補正
				for (Data d : data) {
					d.stat |= mMode;
				}

				return data;
			};
		}.execute();

	}

}
