package com.example.wordbook.common;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.provider.BaseColumns;
import android.util.TimingLogger;

import com.example.wordbook.provider.WBData;
import com.example.wordbook.provider.WBHelper;
import com.example.wordbook.provider.WBProvider;

/**
 * DB利用クラス
 */
public final class DBAccess {

	/**
	 * コンストラクタ（ユーティリティクラス）
	 */
	private DBAccess() {
	}

	/**
	 * フィルタパラメータ取得
	 * 
	 * @param context
	 *            コンテキスト
	 * @return フィルタパラメータ
	 */
	private static String getFilterParam(Context context) {
		StringBuilder filter = new StringBuilder();

		// getDBStat()と一致させること

		// 追加Where句
		boolean[] record = Common.getRecordList(context);
		if (record.length == 6) {
			if (!record[0]) {
				filter.append(" AND NOT(");
				filter.append(WBData.InfoColumns.INFO_OK + "=0");
				filter.append(" AND ");
				filter.append(WBData.InfoColumns.INFO_NG + "=0");
				filter.append(")");
			}
			if (!record[1]) {
				filter.append(" AND NOT(");
				filter.append(WBData.InfoColumns.INFO_OK + "+"
						+ WBData.InfoColumns.INFO_NG + ">0");
				filter.append(" AND ");
				filter.append(WBData.InfoColumns.INFO_OK + "*4<="
						+ WBData.InfoColumns.INFO_NG);
				filter.append(")");
			}
			if (!record[2]) {
				filter.append(" AND NOT(");
				filter.append(WBData.InfoColumns.INFO_OK + "*4>"
						+ WBData.InfoColumns.INFO_NG);
				filter.append(" AND ");
				filter.append(WBData.InfoColumns.INFO_OK + "*3<="
						+ WBData.InfoColumns.INFO_NG + "*2");
				filter.append(")");
			}
			if (!record[3]) {
				filter.append(" AND NOT(");
				filter.append(WBData.InfoColumns.INFO_OK + "*3>"
						+ WBData.InfoColumns.INFO_NG + "*2");
				filter.append(" AND ");
				filter.append(WBData.InfoColumns.INFO_OK + "*2<="
						+ WBData.InfoColumns.INFO_NG + "*3");
				filter.append(")");
			}
			if (!record[4]) {
				filter.append(" AND NOT(");
				filter.append(WBData.InfoColumns.INFO_OK + "*2>"
						+ WBData.InfoColumns.INFO_NG + "*3");
				filter.append(" AND ");
				filter.append(WBData.InfoColumns.INFO_OK + "<="
						+ WBData.InfoColumns.INFO_NG + "*4");
				filter.append(")");
			}
			if (!record[5]) {
				filter.append(" AND NOT(");
				filter.append(WBData.InfoColumns.INFO_OK + ">"
						+ WBData.InfoColumns.INFO_NG + "*4");
				filter.append(" AND ");
				filter.append(WBData.InfoColumns.INFO_OK + "+"
						+ WBData.InfoColumns.INFO_NG + ">0");
				filter.append(")");
			}
		}
		boolean[] flag = Common.getFlagList(context);
		for (int i = 0; i < flag.length; i++) {
			if (!flag[i]) {
				filter.append(" AND ");
				filter.append(WBData.InfoColumns.INFO_FLAG + "!=" + i);
			}
		}
		boolean[] level = Common.getLevelList(context);
		for (int i = 0; i < level.length; i++) {
			if (!level[i]) {
				filter.append(" AND ");
				filter.append(WBData.InfoColumns.INFO_LEVEL + "!=" + i);
			}
		}

		return filter.toString();
	}

	/**
	 * SortOrderパラメータ取得
	 * 
	 * @param sort
	 *            出題順序
	 * @return SortOrderパラメータ
	 */
	private static String getSortOrderParam(int sort) {
		String sortOrder = null;

		// OrderBy句
		switch (sort) {
		case 0:
			sortOrder = "RANDOM()";
			break;
		case 1:
			// NG数->解答数の優先度で取得（≠NG割合）
			sortOrder = WBData.InfoColumns.INFO_NG + " DESC,("
					+ WBData.InfoColumns.INFO_OK + "+"
					+ WBData.InfoColumns.INFO_NG + ") ASC";
			break;
		case 2:
			// OK数->解答数の優先度で取得（≠OK割合）
			sortOrder = WBData.InfoColumns.INFO_OK + " DESC,("
					+ WBData.InfoColumns.INFO_OK + "+"
					+ WBData.InfoColumns.INFO_NG + ") ASC";
			break;
		case 3:
			sortOrder = WBData.InfoColumns.INFO_FLAG + " ASC";
			break;
		case 4:
			sortOrder = WBData.InfoColumns.INFO_FLAG + " DESC";
			break;
		case 5:
			sortOrder = WBData.InfoColumns.INFO_LEVEL + " ASC";
			break;
		case 6:
			sortOrder = WBData.InfoColumns.INFO_LEVEL + " DESC";
			break;
		case 7:
			sortOrder = WBData.InfoColumns.INFO_NUM + " ASC";
			break;
		case 8:
			sortOrder = WBData.InfoColumns.INFO_NUM + " DESC";
			break;
		default:
			break;
		}

		return sortOrder;
	}

	/**
	 * 単語帳DB検索結果データリスト取得
	 * <p>
	 * 全単語帳から検索結果データリストを取得する<br>
	 * - data1="検索欄の値"／data2=""：検索欄の値が質問か解答に含まれるデータを検索<br>
	 * - data1="質問"／data2="解答"：質問か解答が一致するデータを検索
	 * </p>
	 * 
	 * @param context
	 *            コンテキスト
	 * @param data1
	 *            質問／検索欄の値
	 * @param data2
	 *            解答
	 * @return 単語帳DB検索結果データリスト（未取得時はsize=0）
	 */
	public static synchronized List<Data> getDBSearchResultList(
			Context context, String data1, String data2) {
		List<Data> list = new ArrayList<Data>();

		// 設定ファイル情報
		List<Map<String, Object>> pref = Common.getWordbookPrefList(context);

		// 全単語帳から検索
		TimingLogger logger = new TimingLogger("dbg", "getDBSearchResultList()");

		// 検索設定
		String[] args = new String[2];
		StringBuilder sb = new StringBuilder();
		if (data2.length() == 0) {
			// ACTION_SEARCHの場合は検索欄の値が質問か解答に含まれるデータを検索
			sb.append(WBData.InfoColumns.INFO_QUESTION);
			sb.append(" LIKE '%'||?||'%'");
			sb.append(" OR ");
			sb.append(WBData.InfoColumns.INFO_ANSWER);
			sb.append(" LIKE '%'||?||'%'");
			args[0] = data1;
			args[1] = data1;
		} else {
			// ACTION_VIEWの場合は質問か解答が一致するデータを検索
			sb.append(WBData.InfoColumns.INFO_QUESTION);
			sb.append("=?");
			sb.append(" OR ");
			sb.append(WBData.InfoColumns.INFO_ANSWER);
			sb.append("=?");
			args[0] = data1;
			args[1] = data2;
		}
		// 単語帳DBのFILE毎に表示
		String sort = WBData.InfoColumns.INFO_FILE + " ASC";

		// クエリ生成
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
		qb.setTables(WBData.TABLE_NAME_INFO);
		qb.setProjectionMap(WBData.PROJECTION_MAP_INFO);
		qb.setDistinct(true);

		// データ生成
		Cursor c = qb.query(
				WBHelper.getInstance(context).getReadableDatabase(), null,
				sb.toString(), args, null, null, sort, null);
		int idx_file, idx_num, idx_level, idx_flag, idx_ok, idx_ng, idx_q, idx_a, idx_d1, idx_d2;
		int file = 0;
		idx_file = c.getColumnIndex(WBData.InfoColumns.INFO_FILE);
		idx_num = c.getColumnIndex(WBData.InfoColumns.INFO_NUM);
		idx_level = c.getColumnIndex(WBData.InfoColumns.INFO_LEVEL);
		idx_flag = c.getColumnIndex(WBData.InfoColumns.INFO_FLAG);
		idx_ok = c.getColumnIndex(WBData.InfoColumns.INFO_OK);
		idx_ng = c.getColumnIndex(WBData.InfoColumns.INFO_NG);
		idx_q = c.getColumnIndex(WBData.InfoColumns.INFO_QUESTION);
		idx_a = c.getColumnIndex(WBData.InfoColumns.INFO_ANSWER);
		idx_d1 = c.getColumnIndex(WBData.InfoColumns.INFO_DATA1);
		idx_d2 = c.getColumnIndex(WBData.InfoColumns.INFO_DATA2);
		while (c.moveToNext()) {
			int temp = c.getInt(idx_file);
			if (temp > file) {
				file = temp;
				// タイトル取得
				String title = "";
				for (Map<String, Object> item : pref) {
					if (item.get(Common.WORDBOOK_INDEX).equals(temp)) {
						title = item.get(Common.WORDBOOK_TITLE).toString();
						break;
					}
				}
				// タイトル用データ（NUM=0/STAT=file/QUESTION=title）
				Data d = new Data(0, temp, 0, 0, 0, 0, title, "", "", "");
				list.add(d);
			}
			// アイテム用データ（同時表示=STAT_SHOW）
			Data d = new Data(c.getInt(idx_num), Data.STAT_SHOW,
					c.getInt(idx_level), c.getInt(idx_flag), c.getInt(idx_ok),
					c.getInt(idx_ng), c.getString(idx_q), c.getString(idx_a),
					c.getString(idx_d1), c.getString(idx_d2));
			list.add(d);
		}
		c.close();

		logger.addSplit("Search");

		// 処理時間計測
		logger.dumpToLog();

		return list;
	}

	/**
	 * 単語帳DB項目数取得
	 * <p>
	 * 指定された単語帳の項目数を取得する
	 * </p>
	 * 
	 * @param context
	 *            コンテキスト
	 * @param index
	 *            インデックス
	 * @return 単語帳DB項目数
	 */
	public static synchronized int getDBCount(Context context, int index) {
		int count;
		StringBuilder sql = new StringBuilder();
		String[] selectionArgs = { Integer.toString(index) };

		sql.append("SELECT");

		sql.append(" COUNT(" + BaseColumns._ID + ")");

		sql.append(" FROM " + WBData.TABLE_NAME_INFO);
		sql.append(" WHERE " + WBData.InfoColumns.INFO_FILE + "=?");

		// Indexの項目数を取得
		TimingLogger logger = new TimingLogger("dbg", "getDBCount()");

		// by SQLiteOpenHelper
		WBHelper helper = WBHelper.getInstance(context);
		SQLiteDatabase db = helper.getReadableDatabase();

		// トランザクション
		try {
			// トランザクション開始
			db.beginTransaction();
			Cursor c = db.rawQuery(sql.toString(), selectionArgs);
			c.moveToFirst();
			count = c.getInt(0);
			c.close();
			// トランザクション成功
			db.setTransactionSuccessful();
		} finally {
			// トランザクション終了
			db.endTransaction();
		}

		db.close();
		// helper.close();
		logger.addSplit("Count");

		// 処理時間計測
		logger.dumpToLog();

		return count;
	}

	/**
	 * 単語帳DB情報取得
	 * <p>
	 * 指定された単語帳の情報配列を取得する<br>
	 * - [0]: 項目数<br>
	 * - [1]: 未解答項目数<br>
	 * - [2]: 正解数<br>
	 * - [3]: 不正解数
	 * </p>
	 * 
	 * @param context
	 *            コンテキスト
	 * @param index
	 *            インデックス
	 * @return 単語帳DB情報
	 */
	public static synchronized int[] getDBInfo(Context context, int index) {
		int count, zero, ok, ng;
		StringBuilder sql = new StringBuilder();
		String[] selectionArgs = { Integer.toString(index) };

		sql.append("SELECT");

		sql.append(" COUNT(" + BaseColumns._ID + "),");
		sql.append(" COUNT((");
		sql.append(WBData.InfoColumns.INFO_OK + "=0");
		sql.append(" AND ");
		sql.append(WBData.InfoColumns.INFO_NG + "=0");
		sql.append(") OR NULL),");
		sql.append(" SUM(" + WBData.InfoColumns.INFO_OK + "),");
		sql.append(" SUM(" + WBData.InfoColumns.INFO_NG + ")");

		sql.append(" FROM " + WBData.TABLE_NAME_INFO);
		sql.append(" WHERE " + WBData.InfoColumns.INFO_FILE + "=?");

		// Indexの情報を取得
		TimingLogger logger = new TimingLogger("dbg", "getDBInfo()");

		// by SQLiteOpenHelper
		WBHelper helper = WBHelper.getInstance(context);
		SQLiteDatabase db = helper.getReadableDatabase();

		// トランザクション
		try {
			// トランザクション開始
			db.beginTransaction();
			Cursor c = db.rawQuery(sql.toString(), selectionArgs);
			c.moveToFirst();
			count = c.getInt(0);
			zero = c.getInt(1);
			ok = c.getInt(2);
			ng = c.getInt(3);
			c.close();
			// トランザクション成功
			db.setTransactionSuccessful();
		} finally {
			// トランザクション終了
			db.endTransaction();
		}

		db.close();
		// helper.close();
		logger.addSplit("Info");

		// 処理時間計測
		logger.dumpToLog();

		return new int[] { count, zero, ok, ng };
	}

	/**
	 * 単語帳DB統計取得
	 * <p>
	 * 指定された単語帳の統計配列を取得する<br>
	 * - [0]-[7]: Level(0-7)数<br>
	 * - [8]-[15]: Flag(0-7)数<br>
	 * - [16]-[21]: Record(0-7)数<br>
	 * - [22]: 正解数<br>
	 * - [23]: 不正解数
	 * </p>
	 * 
	 * @param context
	 *            コンテキスト
	 * @param index
	 *            インデックス
	 * @return 単語帳DB統計
	 */
	public static synchronized Integer[] getDBStat(Context context, int index) {
		Integer[] stat = new Integer[24];
		StringBuilder sql = new StringBuilder();
		String[] selectionArgs = { Integer.toString(index) };

		// getFilterParam()と一致させること

		sql.append("SELECT");

		// level(0-7)
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_LEVEL + "=0 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_LEVEL + "=1 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_LEVEL + "=2 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_LEVEL + "=3 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_LEVEL + "=4 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_LEVEL + "=5 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_LEVEL + "=6 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_LEVEL + "=7 OR NULL),");
		// flag(8-15)
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_FLAG + "=0 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_FLAG + "=1 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_FLAG + "=2 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_FLAG + "=3 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_FLAG + "=4 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_FLAG + "=5 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_FLAG + "=6 OR NULL),");
		sql.append(" COUNT(" + WBData.InfoColumns.INFO_FLAG + "=7 OR NULL),");
		// record(16-21)
		sql.append(" COUNT((");
		sql.append(WBData.InfoColumns.INFO_OK + "=0");
		sql.append(" AND ");
		sql.append(WBData.InfoColumns.INFO_NG + "=0");
		sql.append(") OR NULL),");
		sql.append(" COUNT((");
		sql.append(WBData.InfoColumns.INFO_OK + "+"
				+ WBData.InfoColumns.INFO_NG + ">0");
		sql.append(" AND ");
		sql.append(WBData.InfoColumns.INFO_OK + "*4<="
				+ WBData.InfoColumns.INFO_NG);
		sql.append(") OR NULL),");
		sql.append(" COUNT((");
		sql.append(WBData.InfoColumns.INFO_OK + "*4>"
				+ WBData.InfoColumns.INFO_NG);
		sql.append(" AND ");
		sql.append(WBData.InfoColumns.INFO_OK + "*3<="
				+ WBData.InfoColumns.INFO_NG + "*2");
		sql.append(") OR NULL),");
		sql.append(" COUNT((");
		sql.append(WBData.InfoColumns.INFO_OK + "*3>"
				+ WBData.InfoColumns.INFO_NG + "*2");
		sql.append(" AND ");
		sql.append(WBData.InfoColumns.INFO_OK + "*2<="
				+ WBData.InfoColumns.INFO_NG + "*3");
		sql.append(") OR NULL),");
		sql.append(" COUNT((");
		sql.append(WBData.InfoColumns.INFO_OK + "*2>"
				+ WBData.InfoColumns.INFO_NG + "*3");
		sql.append(" AND ");
		sql.append(WBData.InfoColumns.INFO_OK + "<="
				+ WBData.InfoColumns.INFO_NG + "*4");
		sql.append(") OR NULL),");
		sql.append(" COUNT((");
		sql.append(WBData.InfoColumns.INFO_OK + ">"
				+ WBData.InfoColumns.INFO_NG + "*4");
		sql.append(" AND ");
		sql.append(WBData.InfoColumns.INFO_OK + "+"
				+ WBData.InfoColumns.INFO_NG + ">0");
		sql.append(") OR NULL),");
		// option(22-23)
		sql.append(" SUM(" + WBData.InfoColumns.INFO_OK + "),");
		sql.append(" SUM(" + WBData.InfoColumns.INFO_NG + ")");

		sql.append(" FROM " + WBData.TABLE_NAME_INFO);
		sql.append(" WHERE " + WBData.InfoColumns.INFO_FILE + "=?");

		// Indexの統計を取得
		TimingLogger logger = new TimingLogger("dbg", "getDBStat()");

		// by SQLiteOpenHelper
		WBHelper helper = WBHelper.getInstance(context);
		SQLiteDatabase db = helper.getReadableDatabase();

		// トランザクション
		try {
			// トランザクション開始
			db.beginTransaction();
			Cursor c = db.rawQuery(sql.toString(), selectionArgs);
			c.moveToFirst();
			for (int i = 0; i < stat.length; i++) {
				stat[i] = c.getInt(i);
			}
			c.close();
			// トランザクション成功
			db.setTransactionSuccessful();
		} finally {
			// トランザクション終了
			db.endTransaction();
		}

		db.close();
		// helper.close();
		logger.addSplit("Stat");

		// 処理時間計測
		logger.dumpToLog();

		return stat;
	}

	/**
	 * 単語帳DBデータ取得
	 * <p>
	 * 指定された単語帳の指定された識別子のデータを取得する<br>
	 * - フィルタ設定を適用しない
	 * </p>
	 * 
	 * @param context
	 *            コンテキスト
	 * @param index
	 *            インデックス
	 * @param num
	 *            識別子
	 * @return 単語帳DBデータ（未取得時はnull）
	 */
	public static synchronized Data getDBData(Context context, int index,
			int num) {
		Data d = null;

		// Indexのデータを取得
		final int DB_MODE = 1;
		TimingLogger logger = new TimingLogger("dbg", "getDBData()");

		// 取得設定
		String selection = WBData.InfoColumns.INFO_FILE + "=?" + " AND "
				+ WBData.InfoColumns.INFO_NUM + "=?";
		String[] selectionArgs = { Integer.toString(index),
				Integer.toString(num) };
		Cursor c;
		int idx_num, idx_stat, idx_level, idx_flag, idx_ok, idx_ng, idx_q, idx_a, idx_d1, idx_d2;

		switch (DB_MODE) {
		case 0:
			// by ContentProvider
			ContentResolver cr = context.getContentResolver();
			Uri uri = WBProvider.INFO_CONTENT_URI;
			c = cr.query(uri, null, selection, selectionArgs, null);
			idx_num = c.getColumnIndex(WBData.InfoColumns.INFO_NUM);
			idx_stat = c.getColumnIndex(WBData.InfoColumns.INFO_STAT);
			idx_level = c.getColumnIndex(WBData.InfoColumns.INFO_LEVEL);
			idx_flag = c.getColumnIndex(WBData.InfoColumns.INFO_FLAG);
			idx_ok = c.getColumnIndex(WBData.InfoColumns.INFO_OK);
			idx_ng = c.getColumnIndex(WBData.InfoColumns.INFO_NG);
			idx_q = c.getColumnIndex(WBData.InfoColumns.INFO_QUESTION);
			idx_a = c.getColumnIndex(WBData.InfoColumns.INFO_ANSWER);
			idx_d1 = c.getColumnIndex(WBData.InfoColumns.INFO_DATA1);
			idx_d2 = c.getColumnIndex(WBData.InfoColumns.INFO_DATA2);
			while (c.moveToNext()) {
				d = new Data(c.getInt(idx_num), c.getInt(idx_stat),
						c.getInt(idx_level), c.getInt(idx_flag),
						c.getInt(idx_ok), c.getInt(idx_ng), c.getString(idx_q),
						c.getString(idx_a), c.getString(idx_d1),
						c.getString(idx_d2));
				break;
			}
			c.close();
			logger.addSplit("ContentProvider");
			break;
		default:
			// by SQLiteOpenHelper
			WBHelper helper = WBHelper.getInstance(context);
			SQLiteDatabase db = helper.getReadableDatabase();

			// トランザクション
			try {
				// トランザクション開始
				db.beginTransaction();
				c = db.query(false, WBData.TABLE_NAME_INFO, null, selection,
						selectionArgs, null, null, null, null);
				idx_num = c.getColumnIndex(WBData.InfoColumns.INFO_NUM);
				idx_stat = c.getColumnIndex(WBData.InfoColumns.INFO_STAT);
				idx_level = c.getColumnIndex(WBData.InfoColumns.INFO_LEVEL);
				idx_flag = c.getColumnIndex(WBData.InfoColumns.INFO_FLAG);
				idx_ok = c.getColumnIndex(WBData.InfoColumns.INFO_OK);
				idx_ng = c.getColumnIndex(WBData.InfoColumns.INFO_NG);
				idx_q = c.getColumnIndex(WBData.InfoColumns.INFO_QUESTION);
				idx_a = c.getColumnIndex(WBData.InfoColumns.INFO_ANSWER);
				idx_d1 = c.getColumnIndex(WBData.InfoColumns.INFO_DATA1);
				idx_d2 = c.getColumnIndex(WBData.InfoColumns.INFO_DATA2);
				while (c.moveToNext()) {
					d = new Data(c.getInt(idx_num), c.getInt(idx_stat),
							c.getInt(idx_level), c.getInt(idx_flag),
							c.getInt(idx_ok), c.getInt(idx_ng),
							c.getString(idx_q), c.getString(idx_a),
							c.getString(idx_d1), c.getString(idx_d2));
					break;
				}
				c.close();
				// トランザクション成功
				db.setTransactionSuccessful();
			} finally {
				// トランザクション終了
				db.endTransaction();
			}

			db.close();
			// helper.close();
			logger.addSplit("SQLiteOpenHelper");
			break;
		}

		// 処理時間計測
		logger.dumpToLog();

		return d;
	}

	/**
	 * 単語帳DBデータリスト取得
	 * <p>
	 * 指定された単語帳の指定された位置からデータリストを取得する<br>
	 * - フィルタ設定を適用する<br>
	 * - 取得開始位置は出題順序のソート結果に依存する<br>
	 * - 取得候補リストは識別子で指定する
	 * </p>
	 * 
	 * @param context
	 *            コンテキスト
	 * @param index
	 *            インデックス
	 * @param start
	 *            取得開始位置
	 * @param size
	 *            取得サイズ
	 * @param sort
	 *            出題順序
	 * @param in
	 *            取得候補リスト（null時は全対象）
	 * @return 単語帳DBデータリスト（未取得時はsize=0）
	 */
	public static synchronized List<Data> getDBDataList(Context context,
			int index, int start, int size, int sort, List<Integer> in) {
		List<Data> list = new ArrayList<Data>();

		// Indexのデータを取得
		final int DB_MODE = 1;
		TimingLogger logger = new TimingLogger("dbg", "getDBDataList()");

		// 取得設定
		String sortOrder = getSortOrderParam(sort);
		String limit = Integer.toString(start) + "," + Integer.toString(size);
		Cursor c;
		int idx_num, idx_stat, idx_level, idx_flag, idx_ok, idx_ng, idx_q, idx_a, idx_d1, idx_d2;
		// Where句
		StringBuilder sb = new StringBuilder();
		sb.append(WBData.InfoColumns.INFO_FILE + "=?");
		sb.append(getFilterParam(context));
		// 追加Where句
		int p_size = (in == null) ? 1 : 1 + in.size();
		String[] selectionArgs = new String[p_size];
		selectionArgs[0] = Integer.toString(index);
		// 取得候補リスト用の追加パラメータを設定
		if (in != null && p_size > 1) {
			sb.append(" AND ");
			sb.append(WBData.InfoColumns.INFO_NUM);
			sb.append(" IN(");
			for (int i = 0; i < p_size - 1; i++) {
				selectionArgs[1 + i] = Integer.toString(in.get(i));
				sb.append("?,");
			}
			// 終端,削除
			sb.deleteCharAt(sb.length() - 1);
			sb.append(")");
		}
		String selection = sb.toString();

		switch (DB_MODE) {
		case 0:
			// by ContentProvider
			ContentResolver cr = context.getContentResolver();
			Uri uri = WBProvider.INFO_CONTENT_URI.buildUpon()
					.appendQueryParameter("limit", limit).build();
			c = cr.query(uri, null, selection, selectionArgs, sortOrder);
			idx_num = c.getColumnIndex(WBData.InfoColumns.INFO_NUM);
			idx_stat = c.getColumnIndex(WBData.InfoColumns.INFO_STAT);
			idx_level = c.getColumnIndex(WBData.InfoColumns.INFO_LEVEL);
			idx_flag = c.getColumnIndex(WBData.InfoColumns.INFO_FLAG);
			idx_ok = c.getColumnIndex(WBData.InfoColumns.INFO_OK);
			idx_ng = c.getColumnIndex(WBData.InfoColumns.INFO_NG);
			idx_q = c.getColumnIndex(WBData.InfoColumns.INFO_QUESTION);
			idx_a = c.getColumnIndex(WBData.InfoColumns.INFO_ANSWER);
			idx_d1 = c.getColumnIndex(WBData.InfoColumns.INFO_DATA1);
			idx_d2 = c.getColumnIndex(WBData.InfoColumns.INFO_DATA2);
			while (c.moveToNext()) {
				Data d = new Data(c.getInt(idx_num), c.getInt(idx_stat),
						c.getInt(idx_level), c.getInt(idx_flag),
						c.getInt(idx_ok), c.getInt(idx_ng), c.getString(idx_q),
						c.getString(idx_a), c.getString(idx_d1),
						c.getString(idx_d2));
				list.add(d);
			}
			c.close();
			logger.addSplit("ContentProvider");
			break;
		default:
			// by SQLiteOpenHelper
			WBHelper helper = WBHelper.getInstance(context);
			SQLiteDatabase db = helper.getReadableDatabase();

			// トランザクション
			try {
				// トランザクション開始
				db.beginTransaction();
				c = db.query(false, WBData.TABLE_NAME_INFO, null, selection,
						selectionArgs, null, null, sortOrder, limit);
				idx_num = c.getColumnIndex(WBData.InfoColumns.INFO_NUM);
				idx_stat = c.getColumnIndex(WBData.InfoColumns.INFO_STAT);
				idx_level = c.getColumnIndex(WBData.InfoColumns.INFO_LEVEL);
				idx_flag = c.getColumnIndex(WBData.InfoColumns.INFO_FLAG);
				idx_ok = c.getColumnIndex(WBData.InfoColumns.INFO_OK);
				idx_ng = c.getColumnIndex(WBData.InfoColumns.INFO_NG);
				idx_q = c.getColumnIndex(WBData.InfoColumns.INFO_QUESTION);
				idx_a = c.getColumnIndex(WBData.InfoColumns.INFO_ANSWER);
				idx_d1 = c.getColumnIndex(WBData.InfoColumns.INFO_DATA1);
				idx_d2 = c.getColumnIndex(WBData.InfoColumns.INFO_DATA2);
				while (c.moveToNext()) {
					Data d = new Data(c.getInt(idx_num), c.getInt(idx_stat),
							c.getInt(idx_level), c.getInt(idx_flag),
							c.getInt(idx_ok), c.getInt(idx_ng),
							c.getString(idx_q), c.getString(idx_a),
							c.getString(idx_d1), c.getString(idx_d2));
					list.add(d);
				}
				c.close();
				// トランザクション成功
				db.setTransactionSuccessful();
			} finally {
				// トランザクション終了
				db.endTransaction();
			}

			db.close();
			// helper.close();
			logger.addSplit("SQLiteOpenHelper");
			break;
		}

		// 処理時間計測
		logger.dumpToLog();

		return list;
	}

	/**
	 * 単語帳DBデータ更新
	 * 
	 * @param context
	 *            コンテキスト
	 * @param index
	 *            インデックス
	 * @param data
	 *            単語帳DBデータ
	 */
	private static synchronized void modDBData(Context context, int index,
			Data data) {

		// Indexのデータを更新
		final int DB_MODE = 1;
		TimingLogger logger = new TimingLogger("dbg", "modDBData()");

		// 更新設定
		String selection = WBData.InfoColumns.INFO_FILE + "=?" + " AND "
				+ WBData.InfoColumns.INFO_NUM + "=?";
		String[] selectionArgs = { Integer.toString(index),
				Integer.toString(data.num) };
		ContentValues cv = new ContentValues();

		switch (DB_MODE) {
		case 0:
			// by ContentProvider
			ContentResolver cr = context.getContentResolver();
			Uri uri = WBProvider.INFO_CONTENT_URI;
			cv.clear();
			// cv.put(WBData.InfoColumns.INFO_FILE, index);
			// cv.put(WBData.InfoColumns.INFO_NUM, data.num);
			cv.put(WBData.InfoColumns.INFO_STAT, data.stat);
			// cv.put(WBData.InfoColumns.INFO_LEVEL, data.level);
			cv.put(WBData.InfoColumns.INFO_FLAG, data.flag);
			cv.put(WBData.InfoColumns.INFO_OK, data.ok);
			cv.put(WBData.InfoColumns.INFO_NG, data.ng);
			// cv.put(WBData.InfoColumns.INFO_QUESTION, data.question);
			// cv.put(WBData.InfoColumns.INFO_ANSWER, data.answer);
			// cv.put(WBData.InfoColumns.INFO_DATA1, d.data1);
			cv.put(WBData.InfoColumns.INFO_DATA2, data.data2);
			cr.update(uri, cv, selection, selectionArgs);
			logger.addSplit("ContentProvider");
			break;
		default:
			// by SQLiteOpenHelper
			WBHelper helper = WBHelper.getInstance(context);
			SQLiteDatabase db = helper.getWritableDatabase();

			// トランザクション
			try {
				// トランザクション開始
				db.beginTransaction();
				cv.clear();
				// cv.put(WBData.InfoColumns.INFO_FILE, index);
				// cv.put(WBData.InfoColumns.INFO_NUM, data.num);
				cv.put(WBData.InfoColumns.INFO_STAT, data.stat);
				// cv.put(WBData.InfoColumns.INFO_LEVEL, data.level);
				cv.put(WBData.InfoColumns.INFO_FLAG, data.flag);
				cv.put(WBData.InfoColumns.INFO_OK, data.ok);
				cv.put(WBData.InfoColumns.INFO_NG, data.ng);
				// cv.put(WBData.InfoColumns.INFO_QUESTION, data.question);
				// cv.put(WBData.InfoColumns.INFO_ANSWER, data.answer);
				// cv.put(WBData.InfoColumns.INFO_DATA1, data.data1);
				cv.put(WBData.InfoColumns.INFO_DATA2, data.data2);
				db.update(WBData.TABLE_NAME_INFO, cv, selection, selectionArgs);
				// トランザクション成功
				db.setTransactionSuccessful();
			} finally {
				// トランザクション終了
				db.endTransaction();
			}

			db.close();
			// helper.close();
			logger.addSplit("SQLiteOpenHelper");
			break;
		}

		// 処理時間計測
		logger.dumpToLog();
	}

	/**
	 * 単語帳DBデータリスト更新
	 * 
	 * @param context
	 *            コンテキスト
	 * @param index
	 *            インデックス
	 * @param list
	 *            単語帳DBデータリスト
	 */
	private static synchronized void modDBDataList(Context context, int index,
			List<Data> list) {
		if (list == null || list.size() == 0) {
			return;
		}

		// Indexのデータを更新
		final int DB_MODE = 1;
		TimingLogger logger = new TimingLogger("dbg", "modDBDataList()");

		// 更新設定
		String selection = WBData.InfoColumns.INFO_FILE + "=?" + " AND "
				+ WBData.InfoColumns.INFO_NUM + "=?";
		String[] selectionArgs = { Integer.toString(index), "0" };
		ContentValues cv = new ContentValues();

		switch (DB_MODE) {
		case 0:
			// by ContentProvider
			ContentResolver cr = context.getContentResolver();
			Uri uri = WBProvider.INFO_CONTENT_URI;
			for (Data d : list) {
				selectionArgs[1] = Integer.toString(d.num);
				cv.clear();
				// cv.put(WBData.InfoColumns.INFO_FILE, index);
				// cv.put(WBData.InfoColumns.INFO_NUM, d.num);
				cv.put(WBData.InfoColumns.INFO_STAT, d.stat);
				// cv.put(WBData.InfoColumns.INFO_LEVEL, d.level);
				cv.put(WBData.InfoColumns.INFO_FLAG, d.flag);
				cv.put(WBData.InfoColumns.INFO_OK, d.ok);
				cv.put(WBData.InfoColumns.INFO_NG, d.ng);
				// cv.put(WBData.InfoColumns.INFO_QUESTION, d.question);
				// cv.put(WBData.InfoColumns.INFO_ANSWER, d.answer);
				// cv.put(WBData.InfoColumns.INFO_DATA1, d.data1);
				cv.put(WBData.InfoColumns.INFO_DATA2, d.data2);
				cr.update(uri, cv, selection, selectionArgs);
			}
			logger.addSplit("ContentProvider");
			break;
		default:
			// by SQLiteOpenHelper
			WBHelper helper = WBHelper.getInstance(context);
			SQLiteDatabase db = helper.getWritableDatabase();

			// トランザクション
			try {
				// トランザクション開始
				db.beginTransaction();
				for (Data d : list) {
					selectionArgs[1] = Integer.toString(d.num);
					cv.clear();
					// cv.put(WBData.InfoColumns.INFO_FILE, index);
					// cv.put(WBData.InfoColumns.INFO_NUM, d.num);
					cv.put(WBData.InfoColumns.INFO_STAT, d.stat);
					// cv.put(WBData.InfoColumns.INFO_LEVEL, d.level);
					cv.put(WBData.InfoColumns.INFO_FLAG, d.flag);
					cv.put(WBData.InfoColumns.INFO_OK, d.ok);
					cv.put(WBData.InfoColumns.INFO_NG, d.ng);
					// cv.put(WBData.InfoColumns.INFO_QUESTION, d.question);
					// cv.put(WBData.InfoColumns.INFO_ANSWER, d.answer);
					// cv.put(WBData.InfoColumns.INFO_DATA1, d.data1);
					cv.put(WBData.InfoColumns.INFO_DATA2, d.data2);
					db.update(WBData.TABLE_NAME_INFO, cv, selection,
							selectionArgs);
				}
				// トランザクション成功
				db.setTransactionSuccessful();
			} finally {
				// トランザクション終了
				db.endTransaction();
			}

			db.close();
			// helper.close();
			logger.addSplit("SQLiteOpenHelper");
			break;
		}

		// 処理時間計測
		logger.dumpToLog();
	}

	/**
	 * 試験データ更新タスク
	 */
	public static class SaveDataTask implements Runnable {
		private Context context;
		private int index;
		private Data data;

		public SaveDataTask(Context context, int index, Data data) {
			this.context = context;
			this.index = index;
			this.data = data;
		}

		@Override
		public void run() {
			// 単語帳DBデータ更新
			modDBData(context, index, data);
		}
	}

	/**
	 * 試験データリスト更新タスク
	 */
	public static class SaveDataListTask implements Runnable {
		private Context context;
		private int index;
		private List<Data> list;

		public SaveDataListTask(Context context, int index, List<Data> list) {
			this.context = context;
			this.index = index;
			this.list = list;
		}

		@Override
		public void run() {
			// 単語帳DBデータリスト更新
			modDBDataList(context, index, list);
		}
	}

}
