public class AlternativeLanguageInfo {

	/* List of attributes */
	private static final String TAG = AlternativeLanguageInfo.class.toString();
	private String language;
	private String category;
	private String markerSynopsis; /* Marker for Synopsis */
	private ArrayList<String> parserInfo;

	private static Hashtable<String, AlternativeLanguageInfo> instance = null; //new Hashtable<String, AlternativeLanguageInfo> ();
	private static Object lock = new Object();

	/* List of methods */
	public static void initHashMap() {
		synchronized (lock) {
			// construct HashTable and populate with proper data with language as the key
			if(instance == null) instance = new Hashtable<String, AlternativeLanguageInfo> ();

		
		    Log.d(TAG,"Polish Language added");
		}
	}

	public static Hashtable<String, AlternativeLanguageInfo> getAlternativeLanguageInfo() {
		synchronized (lock) {
			/* if instance is null, then initHashMap */
		   if(instance == null || instance.isEmpty()) initHashMap();
		   return instance;
		}
	}

	public AlternativeLanguageInfo(String _language, String _category, String _markerSynopsis, ArrayList<String> _parserInfo) {
	  // set the member variables
	  language = _language;
	  category = _category;
	  markerSynopsis = _markerSynopsis;
	  parserInfo = _parserInfo;
	}

	/* Setter & Getter */
	public String getLanguage(){
		return language;
	}

	public String getCategory(){
		return category;
	}

	public String getCategoryInfo(){
		return "Category:" + category;
	}

	public String getMarkerSynopsis(){
		return markerSynopsis;
	}

	public ArrayList<String> getParserInfo(){
		return parserInfo;
	}

	public void setLanguage(String _language){
		language = _language;
	}

	public void setCategory(String _category){
		category = _category;
	}

	public void setMarkerSynopsis(String _markerSynopsis){
		markerSynopsis = _markerSynopsis;
	}

	public void setParserInfo(ArrayList<String> _parserInfo){
		parserInfo = _parserInfo;
	}

} public class Constants {

	// public static final String BaseURL = "http://www.baka-tsuki.org/project/";
	public static final String BASE_URL_HTTPS = "http://fiction.icoolxue.com/index.php";
	public static final String BASE_URL = "http://fiction.icoolxue.com/index.php";
	public static final String BASE_IMAGE_URL = "http://fiction.icoolxue.com";
	// Intent parameter list
	public static final String EXTRA_NOVEL = "com.erakk.lnreader.NOVEL";
	public static final String EXTRA_PAGE = "com.erakk.lnreader.page";
	public static final String EXTRA_ID = "com.erakk.lnreader.id";
	public static final String EXTRA_SUMMARY = "com.erakk.lnreader.summary";
	public static final String EXTRA_TITLE = "com.erakk.lnreader.title";
	public static final String EXTRA_VOLUME = "com.erakk.lnreader.volume";
	public static final String EXTRA_ONLY_WATCHED = "com.erakk.lnreader.ONLY_WATCHED";
	public static final String EXTRA_IMAGE_URL = "com.erakk.lnreader.IMAGE_URL";
	public static final String EXTRA_SCROLL_X = "com.erakk.lnreader.SCROLL_X";
	public static final String EXTRA_SCROLL_Y = "com.erakk.lnreader.SCROLL_Y";
	public static final String EXTRA_P_INDEX = "pIndex";
	public static final String EXTRA_CALLER_ACTIVITY = "caller_activity";

	public static final String NOVEL_BOOK_DIVIDER = "%NOVEL_BOOK_DIVIDER%";
	public static final int CHECK_INTERVAL = 7;

	//小說類型id
	public static final String EXTRA_CATEGORY_ID="com.dotcool.category_id";
	public static final int EXTRA_HISTORY_CATEGORY_ID = 1;
	public static final int EXTRA_XUANHUAN_CATEGORY_ID = 3;
	public static final int EXTRA_KEHUAN_CATEGORY_ID = 7;
	public static final int EXTRA_QITA_CATEGORY_ID = 10;
	public static final int EXTRA_UPDATE_CATEGORY_ID = 11;
	public static final int EXTRA_MOOD_CATEGORY_ID = 12;
	
	// Shared Preferences keys
	public static final String PREF_FIRST_RUN = "first_run";
	public static final String PREF_DOWNLOAD_TOUCH = "touch_download_chapter";
	public static final String PREF_INVERT_COLOR = "invert_colors";
	public static final String PREF_LANGUAGE = "language_selection";
	public static final String PREF_LOCK_HORIZONTAL = "lock_horizontal";
	public static final String PREF_LAST_READ = "last_read";
	public static final String PREF_UPDATE_INTERVAL = "updates_interval";
	public static final String PREF_RUN_UPDATES = "run_update";
	public static final String PREF_RUN_UPDATES_STATUS = "run_update_status";
	public static final String PREF_DOWLOAD_BIG_IMAGE = "download_big_image";
	public static final String PREF_ZOOM_ENABLED = "enable_zoom";
	public static final String PREF_SHOW_IMAGE = "show_images";
	public static final String PREF_SHOW_ZOOM_CONTROL = "show_zoom_control";
	public static final String PREF_UPDATE_RING = "update_use_sound";
	public static final String PREF_UPDATE_VIBRATE = "update_use_vibration";
	public static final String PREF_PERSIST_NOTIFICATION = "persist_notification";
	public static final String PREF_USE_VOLUME_FOR_SCROLL = "use_volume_to_scroll";
	public static final String PREF_SCROLL_SIZE = "scroll_size";
	public static final String PREF_IS_NOVEL_ONLY = "novel_only";
	public static final String PREF_INVERT_SCROLL = "invert_scroll";
	public static final String PREF_ALPH_ORDER = "alphabet_order";
	public static final String PREF_SHOW_MISSING = "show_missing";
	public static final String PREF_SHOW_EXTERNAL = "show_external";
	public static final String PREF_KEEP_AWAKE = "keep_awake";
	public static final String PREF_FULSCREEN = "fullscreen";
	public static final String PREF_ENABLE_BOOKMARK = "enable_bookmark";
	public static final String PREF_ENABLE_WEBVIEW_BUTTONS = "enable_webview_buttons";
	public static final String PREF_USE_INTERNAL_WEBVIEW = "use_internal_webview";
	public static final String PREF_CONSOLIDATE_NOTIFICATION = "consolidate_notification";
	public static final String PREF_FORCE_JUSTIFIED = "force_justified";
	public static final String PREF_STRETCH_COVER = "strech_detail_image";
	public static final String PREF_LINESPACING = "line_spacing";
	public static final String PREF_USE_CUSTOM_CSS = "use_custom_css";
	public static final String PREF_CUSTOM_CSS_PATH = "custom_css_path";
	public static final String PREF_MARGINS = "margin_space";
	public static final String PREF_UI_SELECTION = "ui_selection";
	public static final String PREF_ORIENTATION = "orientation_lock";
	public static final String PREF_LAST_UPDATE = "last_update";
	public static final String PREF_TIMEOUT = "timeout";
	public static final String PREF_RETRY = "retry";
	public static final String PREF_INCREASE_RETRY = "increase_retry";
	public static final String PREF_IMAGE_SAVE_LOC = "save_location";
	public static final String PREF_AGGRESIVE_TITLE_CLEAN_UP = "aggresive_title_clean_up";
	public static final String PREF_USE_HTTPS = "use_https";
	public static final String PREF_HIDE_EMPTY_VOLUME = "hide_empty_volume";
	public static final String PREF_TTS_PITCH = "tts_pitch";
	public static final String PREF_TTS_SPEECH_RATE = "tts_reading_speed";
	public static final String PREF_TTS_DELAY = "tts_whitespace_delay";
	public static final String PREF_TTS_ENGINE = "tts_engine";
	public static final String PREF_TTS_TTS_STOP_ON_LOST_FOCUS = "tts_stop_on_lost_focus";
	public static final String PREF_AUTO_DOWNLOAD_UPDATED_CHAPTER = "auto_download_updated_chapter";
	public static final String PREF_BACKUP_THUMB_IMAGES = "backup_thumb_images";
	public static final String PREF_BACKUP_DB = "backup_database";
	public static final String PREF_RESTORE_DB = "restore_database";
	public static final String PREF_RESTORE_THUMB_IMAGES = "restore_thumb_images";
	public static final String PREF_RELINK_THUMB_IMAGES = "relink_images";
	public static final String PREF_BOOKMARK_ORDER = "bookmark_order";
	public static final String PREF_PROCESS_ALL_IMAGES = "process_all_images";
	public static final String PREF_SHOW_REDLINK = "show_redlink";

	public static final float DISPLAY_SCALE = LNReaderApplication.getInstance().getResources().getDisplayMetrics().density;

	@SuppressWarnings("deprecation")
	public static final int NOTIFIER_ID = (int) (new Date().getTime() - new Date(2012, 1, 1).getTime());
	public static final int CONSOLIDATED_NOTIFIER_ID = 20130210;

	public static final String STATUS_TEASER = "teaser";
	public static final String STATUS_STALLED = "stalled";
	public static final String STATUS_ABANDONED = "abandoned";
	public static final String STATUS_PENDING = "pending";
	public static final String STATUS_ORIGINAL = "original";
	public static final String STATUS_BAHASA_INDONESIA = "indonesian";

	public static final int COLOR_READ = Color.parseColor("#888888");
	public static final int COLOR_UNREAD = Color.parseColor("#110d0d");
	public static final int COLOR_UNREAD_DARK = Color.parseColor("#222222");
	public static final int COLOR_MISSING = Color.parseColor("#ff0000");
	public static final int COLOR_EXTERNAL = Color.parseColor("#3333ff");

	public static final String ROOT_NOVEL = "Main_Page";

	/* Section of Task Key */
	public static final String KEY_LOAD_CHAPTER = ":LoadChapter:";
	public static final String KEY_DOWNLOAD_CHAPTER = ":DownloadChapters:";
	public static final String KEY_DOWNLOAD_ALL_CHAPTER = ":DownloadChaptersAll:";

	/* Section of Language for novel parser */
	public static final String LANG_ENGLISH = "中文";

	/* You just need to add a new alternative language here and in AlternativeLanguageInfo -> initSingleton */
	public static final String[] languageList = { Constants.LANG_ENGLISH};
	public static final int BUFFER = 1024;

} public class BookModelHelper {
	private static final String TAG = BookModelHelper.class.toString();
	private static DBHelper helper = NovelsDao.getInstance().getDBHelper();

	// New column should be appended as the last column
	// COLUMN_PAGE is not unique because being used for reference to the novel page.
	public static final String DATABASE_CREATE_NOVEL_BOOKS = "create table if not exists "
		      + DBHelper.TABLE_NOVEL_BOOK + "(" + DBHelper.COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "	// 0
				 				  + DBHelper.COLUMN_PAGE + " text not null, "						// 1
				  				  + DBHelper.COLUMN_TITLE + " text not null, "						// 2
				  				  + DBHelper.COLUMN_LAST_UPDATE + " integer, "						// 3
				  				  + DBHelper.COLUMN_LAST_CHECK + " integer, "						// 4
				  				   + DBHelper.COLUMN_BOOK_ID + " integer, "                         //5
                                  + DBHelper.COLUMN_CHAPTER_ID + " integer, "                       //6
				  				  + DBHelper.COLUMN_ORDER + " integer);";							// 7

	public static BookModel cursorToBookModel(Cursor cursor) {
		BookModel book = new BookModel();
		book.setId(cursor.getInt(0));
		book.setPage(cursor.getString(1));
		book.setTitle(cursor.getString(2));
		book.setLastUpdate(new Date(cursor.getInt(3)*1000));
		book.setLastCheck(new Date(cursor.getInt(4)*1000));
		book.setBookId(cursor.getInt(5));
		book.setChapter(cursor.getInt(6));
		book.setOrder(cursor.getInt(7));
		return book;
	}

	/*
	 * Query Stuff
	 */

	public static BookModel getBookModel(SQLiteDatabase db, int id) {
		BookModel book = null;
		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_NOVEL_BOOK
										 + " where " + DBHelper.COLUMN_ID + " = ? ", new String[] {"" + id});
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				book = cursorToBookModel(cursor);
				//Log.d(TAG, "Found: " + book.getPage() + Constants.NOVEL_BOOK_DIVIDER + book.getTitle());
				break;
			}
		} finally{
			if(cursor != null) cursor.close();
		}
	    return book;
	}

	public static BookModel getBookModel(SQLiteDatabase db, String page, String title) {
		BookModel book = null;
		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_NOVEL_BOOK
										 + " where " + DBHelper.COLUMN_PAGE + " = ? and " + DBHelper.COLUMN_TITLE + " = ?", new String[] {page, title});
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				book = cursorToBookModel(cursor);
				//Log.d(TAG, "Found: " + book.getPage() + Constants.NOVEL_BOOK_DIVIDER + book.getTitle());
				break;
			}
		} finally{
			if(cursor != null) cursor.close();
		}
	    return book;
	}

	public static ArrayList<BookModel> getBookCollectionOnly(SQLiteDatabase db, String page, NovelCollectionModel novelDetails) {
		// get the books
	    ArrayList<BookModel> bookCollection = new ArrayList<BookModel>();
	    Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_NOVEL_BOOK
	    								 + " where " + DBHelper.COLUMN_PAGE + " = ? "
	    								 + " order by " + DBHelper.COLUMN_ORDER, new String[] {page});
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				BookModel book = cursorToBookModel(cursor);
				book.setParent(novelDetails);
				bookCollection.add(book);
				//Log.d(TAG, "Found: " + book.toString());
				cursor.moveToNext();
			}
		} finally{
			if(cursor != null) cursor.close();
		}
		return bookCollection;
	}

	/*
	 * Insert Stuff
	 */

	public static BookModel insertBookModel(SQLiteDatabase db, BookModel book) {
		ContentValues cv2 = new ContentValues();
		cv2.put(DBHelper.COLUMN_PAGE, book.getPage());
		cv2.put(DBHelper.COLUMN_TITLE , book.getTitle());
		cv2.put(DBHelper.COLUMN_ORDER , book.getOrder());
		cv2.put(DBHelper.COLUMN_BOOK_ID , book.getBookId());
	    cv2.put(DBHelper.COLUMN_CHAPTER_ID , book.getChapter());
		cv2.put(DBHelper.COLUMN_LAST_CHECK, "" + (int) (new Date().getTime() / 1000));

		BookModel tempBook = BookModelHelper.getBookModel(db, book.getId());
		if(tempBook == null) tempBook = BookModelHelper.getBookModel(db, book.getPage(), book.getTitle());
		if(tempBook == null) {
			//Log.d(TAG, "Inserting Novel Book: " + novelDetails.getPage() + Constants.NOVEL_BOOK_DIVIDER + book.getTitle());
			if(book.getLastUpdate() == null)
				cv2.put(DBHelper.COLUMN_LAST_UPDATE, 0);
			else
				cv2.put(DBHelper.COLUMN_LAST_UPDATE, "" + (int) (book.getLastUpdate().getTime() / 1000));
			helper.insertOrThrow(db, DBHelper.TABLE_NOVEL_BOOK, null, cv2);
		}
		else {
			//Log.d(TAG, "Updating Novel Book: " + tempBook.getPage() + Constants.NOVEL_BOOK_DIVIDER + tempBook.getTitle() + " id: " + tempBook.getId());
			cv2.put(DBHelper.COLUMN_LAST_UPDATE, "" + (int) (tempBook.getLastUpdate().getTime() / 1000));
			helper.update(db, DBHelper.TABLE_NOVEL_BOOK, cv2, DBHelper.COLUMN_ID + " = ?", new String[] {"" + tempBook.getId()});
		}

		book = getBookModel(db, book.getPage(), book.getTitle());

		return book;
	}

	/*
	 * Delete Stuff
	 */

	public static void deleteBookModel(SQLiteDatabase db, BookModel book) {
		int chaptersCount = 0;
		ArrayList<PageModel> chapters = book.getChapterCollection();
		if(chapters != null && chapters.size() > 0) {
			for(Iterator<PageModel> i = chapters.iterator(); i.hasNext();) {
				PageModel page = i.next();
				chaptersCount += helper.delete(db, DBHelper.TABLE_PAGE, DBHelper.COLUMN_ID + " = ? ", new String[] {"" + page.getId()});
			}
			Log.w(TAG, "Deleted PageModel: " + chaptersCount);
		}
		int bookCount = helper.delete(db, DBHelper.TABLE_NOVEL_BOOK, DBHelper.COLUMN_ID + " = ? ", new String[] { "" + book.getId() });
		Log.w(TAG, "Deleted BookModel: " + bookCount);
	}
} public class BookmarkModelHelper {
	private static final String TAG = BookmarkModelHelper.class.toString();
	private static DBHelper helper = NovelsDao.getInstance().getDBHelper();

	// New column should be appended as the last column
	public static final String DATABASE_CREATE_NOVEL_BOOKMARK = "create table if not exists "
			+ DBHelper.TABLE_NOVEL_BOOKMARK + "(" + DBHelper.COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " // 0
			+ DBHelper.COLUMN_PAGE + " text not null, " // 1
			+ DBHelper.COLUMN_PARAGRAPH_INDEX + " integer, " // 2
			+ DBHelper.COLUMN_EXCERPT + " text, " // 3
			+ DBHelper.COLUMN_CREATE_DATE + " integer);"; // 4

	public static BookmarkModel cursorToNovelBookmark(Cursor cursor) {
		BookmarkModel bookmark = new BookmarkModel();
		bookmark.setId(cursor.getInt(0));
		bookmark.setPage(cursor.getString(1));
		bookmark.setpIndex(cursor.getInt(2));
		bookmark.setExcerpt(cursor.getString(3));
		bookmark.setCreationDate(new Date(cursor.getLong(4) * 1000));
		return bookmark;
	}

	/*
	 * Query Stuff
	 */

	public static ArrayList<BookmarkModel> getAllBookmarks(SQLiteDatabase db, boolean isOrderByDate) {
		ArrayList<BookmarkModel> bookmarks = new ArrayList<BookmarkModel>();

		String sql = "select * from " + DBHelper.TABLE_NOVEL_BOOKMARK;
		if (isOrderByDate) {
			sql += " order by " + DBHelper.COLUMN_CREATE_DATE + " DESC "
					+ ", " + DBHelper.COLUMN_PAGE
					+ ", " + DBHelper.COLUMN_PARAGRAPH_INDEX;
		} else {
			sql += " order by " + DBHelper.COLUMN_PAGE
					+ ", " + DBHelper.COLUMN_PARAGRAPH_INDEX;
		}
		Cursor cursor = helper.rawQuery(db, sql, null);
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				BookmarkModel bookmark = cursorToNovelBookmark(cursor);
				bookmarks.add(bookmark);
				cursor.moveToNext();
			}
		} finally {
			if (cursor != null)
				cursor.close();
		}

		return bookmarks;
	}

	public static ArrayList<BookmarkModel> getBookmarks(SQLiteDatabase db, PageModel page) {
		ArrayList<BookmarkModel> bookmarks = new ArrayList<BookmarkModel>();

		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_NOVEL_BOOKMARK
				+ " where " + DBHelper.COLUMN_PAGE + " = ? ", new String[] { page.getPage() });
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				BookmarkModel bookmark = cursorToNovelBookmark(cursor);
				bookmarks.add(bookmark);
				cursor.moveToNext();
			}
		} finally {
			if (cursor != null)
				cursor.close();
		}

		return bookmarks;
	}

	private static BookmarkModel getBookmark(SQLiteDatabase db, String page, int pIndex) {
		BookmarkModel bookmark = null;

		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_NOVEL_BOOKMARK
				+ " where " + DBHelper.COLUMN_PAGE + " = ? and " + DBHelper.COLUMN_PARAGRAPH_INDEX + " = ? "
				, new String[] { page, "" + pIndex });
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				bookmark = cursorToNovelBookmark(cursor);
				break;
			}
		} finally {
			if (cursor != null)
				cursor.close();
		}

		return bookmark;
	}

	/*
	 * Insert Stuff
	 */

	public static int insertBookmark(SQLiteDatabase db, BookmarkModel bookmark) {
		BookmarkModel tempBookmark = getBookmark(db, bookmark.getPage(), bookmark.getpIndex());

		if (tempBookmark == null) {
			ContentValues cv = new ContentValues();
			cv.put(DBHelper.COLUMN_PAGE, bookmark.getPage());
			cv.put(DBHelper.COLUMN_PARAGRAPH_INDEX, bookmark.getpIndex());
			String excerpt = bookmark.getExcerpt();
			if (excerpt.length() > 200) {
				excerpt = excerpt.substring(0, 197) + "...";
			}
			cv.put(DBHelper.COLUMN_EXCERPT, excerpt);
			cv.put(DBHelper.COLUMN_CREATE_DATE, (int) (new Date().getTime() / 1000));
			return (int) helper.insertOrThrow(db, DBHelper.TABLE_NOVEL_BOOKMARK, null, cv);
		}
		else {
			Log.d(TAG, "Bookmark already created.");
			return 0;
		}

	}

	/*
	 * Delete Stuff
	 */

	public static int deleteBookmark(SQLiteDatabase db, BookmarkModel bookmark) {
		return helper.delete(db, DBHelper.TABLE_NOVEL_BOOKMARK, DBHelper.COLUMN_PAGE + " = ? and " + DBHelper.COLUMN_PARAGRAPH_INDEX + " = ? "
				, new String[] { bookmark.getPage(), "" + bookmark.getpIndex() });
	}
} public class ImageModelHelper {
	private static final String TAG = ImageModelHelper.class.toString();
	private static DBHelper helper = NovelsDao.getInstance().getDBHelper();

	// New column should be appended as the last column
	public static final String DATABASE_CREATE_IMAGES = "create table if not exists " + DBHelper.TABLE_IMAGE + "(" + DBHelper.COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " // 0
			+ DBHelper.COLUMN_IMAGE_NAME + " text unique not null, " // 1
			+ DBHelper.COLUMN_FILEPATH + " text not null, " // 2
			+ DBHelper.COLUMN_URL + " text not null, " // 3
			+ DBHelper.COLUMN_REFERER + " text, " // 4
			+ DBHelper.COLUMN_LAST_UPDATE + " integer, " // 5
			+ DBHelper.COLUMN_LAST_CHECK + " integer, " // 6
			+ DBHelper.COLUMN_IS_BIG_IMAGE + " boolean);"; // 7

	public static ImageModel cursorToImage(Cursor cursor) {
		ImageModel image = new ImageModel();
		image.setId(cursor.getInt(0));
		image.setName(cursor.getString(1));
		image.setPath(cursor.getString(2));
		try {
			image.setUrl(new URL(cursor.getString(3)));
		} catch (MalformedURLException ex) {
			Log.e(TAG, "Invalid URL: " + cursor.getString(3), ex);
		}
		image.setReferer(cursor.getString(4));
		image.setLastUpdate(new Date(cursor.getInt(5) * 1000));
		image.setLastCheck(new Date(cursor.getInt(6) * 1000));

		image.setBigImage(cursor.getInt(7) == 1 ? true : false);
		return image;
	}

	/*
	 * Query Stuff
	 */

	public static ImageModel getImageByReferer(SQLiteDatabase db, ImageModel image) {
		return getImageByReferer(db, image.getReferer());
	}

	public static ImageModel getImageByReferer(SQLiteDatabase db, String url) {
		// Log.d(TAG, "Selecting Image by Referer: " + url);
		ImageModel image = null;

		String nameAlt = "";
		if (url.startsWith("https")) {
			nameAlt = url.replace("https://", "http://");
		} else {
			nameAlt = url.replace("http://", "https://");
		}

		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_IMAGE + " where " + DBHelper.COLUMN_REFERER + " = ? or " + DBHelper.COLUMN_REFERER + " = ? ", new String[] { url, nameAlt });
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				image = cursorToImage(cursor);
				Log.d(TAG, "Found by Ref: " + image.getReferer() + " name: " + image.getName() + " id: " + image.getId());
				break;
			}
		} finally {
			if (cursor != null)
				cursor.close();
		}

		if (image == null) {
			Log.w(TAG, "Not Found Image by Referer: " + url);
		}
		return image;
	}

	public static ImageModel getImage(SQLiteDatabase db, ImageModel image) {
		return getImage(db, image.getName());
	}

	public static ImageModel getImage(SQLiteDatabase db, String name) {
		// Log.d(TAG, "Selecting Image: " + name);
		ImageModel image = null;

		String nameAlt = "";
		if (name.startsWith("https")) {
			nameAlt = name.replace("https://", "http://");
		} else {
			nameAlt = name.replace("http://", "https://");
		}

		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_IMAGE + " where " + DBHelper.COLUMN_IMAGE_NAME + " = ? or " + DBHelper.COLUMN_IMAGE_NAME + " = ? ", new String[] { name, nameAlt });
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				image = cursorToImage(cursor);
				// Log.d(TAG, "Found: " + image.getName() + " id: " + image.getId());
				break;
			}
		} finally {
			if (cursor != null)
				cursor.close();
		}

		if (image == null) {
			Log.w(TAG, "Not Found Image: " + name);
		}
		return image;
	}

	/*
	 * Insert Stuff
	 */

	public static ImageModel insertImage(SQLiteDatabase db, ImageModel image) {
		ImageModel temp = getImage(db, image.getName());

		ContentValues cv = new ContentValues();
		cv.put(DBHelper.COLUMN_IMAGE_NAME, image.getName());
		cv.put(DBHelper.COLUMN_FILEPATH, image.getPath());
		cv.put(DBHelper.COLUMN_URL, image.getUrl().toString());
		cv.put(DBHelper.COLUMN_REFERER, image.getReferer());
		cv.put(DBHelper.COLUMN_IS_BIG_IMAGE, image.isBigImage());
		if (temp == null) {
			cv.put(DBHelper.COLUMN_LAST_UPDATE, "" + (int) (new Date().getTime() / 1000));
			cv.put(DBHelper.COLUMN_LAST_CHECK, "" + (int) (new Date().getTime() / 1000));
			helper.insertOrThrow(db, DBHelper.TABLE_IMAGE, null, cv);
			Log.i(TAG, "Complete Insert Images: " + image.getName() + " Ref: " + image.getReferer());
		} else {
			cv.put(DBHelper.COLUMN_LAST_UPDATE, "" + (int) (temp.getLastUpdate().getTime() / 1000));
			cv.put(DBHelper.COLUMN_LAST_CHECK, "" + (int) (new Date().getTime() / 1000));
			helper.update(db, DBHelper.TABLE_IMAGE, cv, DBHelper.COLUMN_ID + " = ?", new String[] { "" + temp.getId() });
			Log.i(TAG, "Complete Update Images: " + image.getName() + " Ref: " + image.getReferer());
		}
		// get updated data
		image = getImage(db, image.getName());

		// Log.d(TAG, "Complete Insert Images: " + image.getName() + " id: " + image.getId());

		return image;
	}

	public static ArrayList<ImageModel> getAllImages(SQLiteDatabase db) {
		ArrayList<ImageModel> result = new ArrayList<ImageModel>();
		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_IMAGE, null);
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				ImageModel image = cursorToImage(cursor);
				result.add(image);
				cursor.moveToNext();
			}
		} finally {
			if (cursor != null)
				cursor.close();
		}
		return result;
	}

	/*
	 * Delete Stuff
	 */
	// TODO: only have bulk delete by dropping the table...
} public class NovelCollectionModelHelper {
	private static final String TAG = NovelCollectionModelHelper.class.toString();
	private static DBHelper helper = NovelsDao.getInstance().getDBHelper();

	// New column should be appended as the last column
	public static final String DATABASE_CREATE_NOVEL_DETAILS = "create table if not exists "
		      + DBHelper.TABLE_NOVEL_DETAILS + "(" + DBHelper.COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
				 				  + DBHelper.COLUMN_PAGE + " text, "
				  				  + DBHelper.COLUMN_SYNOPSIS + " text, "
				  				  + DBHelper.COLUMN_IMAGE_NAME + " text, "
				  				  + DBHelper.COLUMN_LAST_UPDATE + " integer, "
				  				  + DBHelper.COLUMN_LAST_CHECK + " integer);";

	public static NovelCollectionModel cursorToNovelCollection(Cursor cursor) {
		NovelCollectionModel novelDetails = new NovelCollectionModel();
		novelDetails.setId(cursor.getInt(0));
		novelDetails.setPage(cursor.getString(1));
		novelDetails.setSynopsis(cursor.getString(2));
		novelDetails.setCover(cursor.getString(3));
		novelDetails.setLastUpdate(new Date(cursor.getInt(4)*1000));
		novelDetails.setLastCheck(new Date(cursor.getInt(5)*1000));
		return novelDetails;
	}

	/*
	 * Query Stuff
	 */
	public static NovelCollectionModel getNovelDetails(SQLiteDatabase db, String page) {
		Log.d(TAG, "Selecting Novel Details: " + page);
		NovelCollectionModel novelDetails = getNovelDetailsOnly(db, page);

	    if(novelDetails != null) {
	    	novelDetails.setPageModel(PageModelHelper.getPageModel(db, page));

		    // get the books
		    ArrayList<BookModel> bookCollection = BookModelHelper.getBookCollectionOnly(db, page, novelDetails);

			// get the chapters
			for(Iterator<BookModel> iBook = bookCollection.iterator(); iBook.hasNext();) {
				BookModel book = iBook.next();
				ArrayList<PageModel> chapters = getChapterCollection(db, novelDetails.getPage() + Constants.NOVEL_BOOK_DIVIDER + book.getTitle(), book);
				book.setChapterCollection(chapters);
			}
			novelDetails.setBookCollections(bookCollection);
	    }
	    else {
	    	Log.w(TAG, "No Data for Novel Details: " + page);
	    }

		Log.d(TAG, "Complete Selecting Novel Details: " + page);
		return novelDetails;
	}

	public static NovelCollectionModel getNovelDetailsOnly(SQLiteDatabase db, String page) {
		NovelCollectionModel novelDetails = null;
		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_NOVEL_DETAILS
										 + " where " + DBHelper.COLUMN_PAGE + " = ? ", new String[] {page});
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				novelDetails = cursorToNovelCollection(cursor);
				Log.d(TAG, "Found: " + novelDetails.toString());
				break;
			}
		} finally{
			if(cursor != null) cursor.close();
		}
	    return novelDetails;
	}

	public static ArrayList<PageModel> getChapterCollection(SQLiteDatabase db, String parent, BookModel book) {
		ArrayList<PageModel> chapters = new ArrayList<PageModel>();
		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_PAGE
										 + " where " + DBHelper.COLUMN_PARENT + " = ? order by " + DBHelper.COLUMN_ORDER, new String[] {parent});
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				PageModel chapter = PageModelHelper.cursorToPageModel(cursor);
				chapter.setBook(book);
				chapters.add(chapter);
			    Log.d(TAG, "Found: " + chapter.toString());
				cursor.moveToNext();
			}
		} finally{
			if(cursor != null) cursor.close();
		}
		return chapters;
	}

	
	public static void insertNovelDetailsImg(SQLiteDatabase db, NovelCollectionModel novelDetails){
	    
	    ContentValues cv = new ContentValues();
	    cv.put(DBHelper.COLUMN_IMAGE_NAME, novelDetails.getCover());
	    NovelCollectionModel temp = getNovelDetailsOnly(db, novelDetails.getPage());

        helper.update(db, DBHelper.TABLE_NOVEL_DETAILS, cv, DBHelper.COLUMN_ID + " = ?", new String[] {"" + temp.getId()});
  
	}
	
	/*
	 * Insert Stuff
	 */
	public static NovelCollectionModel insertNovelDetails(SQLiteDatabase db, NovelCollectionModel novelDetails){
		ContentValues cv = new ContentValues();
		cv.put(DBHelper.COLUMN_PAGE, novelDetails.getPage());
		cv.put(DBHelper.COLUMN_SYNOPSIS, novelDetails.getSynopsis());
		cv.put(DBHelper.COLUMN_IMAGE_NAME, novelDetails.getCover());
		cv.put(DBHelper.COLUMN_LAST_CHECK, "" + (int) (new Date().getTime() / 1000));

		// check if exist
		NovelCollectionModel temp = getNovelDetailsOnly(db, novelDetails.getPage());
		if(temp == null) {
			Log.d(TAG, "Inserting Novel Details: " + novelDetails.getPage());
			if(novelDetails.getLastUpdate() == null)
				cv.put(DBHelper.COLUMN_LAST_UPDATE, 0);
			else
				cv.put(DBHelper.COLUMN_LAST_UPDATE, "" + (int) (novelDetails.getLastUpdate().getTime() / 1000));
			helper.insertOrThrow(db, DBHelper.TABLE_NOVEL_DETAILS, null, cv);
		}
		else {
			Log.d(TAG, "Updating Novel Details: " + novelDetails.getPage() + " id: " + temp.getId());
			cv.put(DBHelper.COLUMN_LAST_UPDATE, "" + (int) (temp.getLastUpdate().getTime() / 1000));
			helper.update(db, DBHelper.TABLE_NOVEL_DETAILS, cv, DBHelper.COLUMN_ID + " = ?", new String[] {"" + temp.getId()});
		}

		// insert book
		for(BookModel book : novelDetails.getBookCollections()){
			book.setPage(novelDetails.getPage());
			book.setLastUpdate(novelDetails.getLastUpdate());
			book = BookModelHelper.insertBookModel(db, book);
		}

		// insert chapter
		for(Iterator<BookModel> iBooks = novelDetails.getBookCollections().iterator(); iBooks.hasNext();){
			BookModel book = iBooks.next();
			for(PageModel page : book.getChapterCollection()) {
				PageModelHelper.insertOrUpdatePageModel(db, page, false);
			}
		}

		Log.d(TAG, "Complete Insert Novel Details: " + novelDetails.toString());

		// get updated data
		novelDetails = getNovelDetails(db, novelDetails.getPage());
		return novelDetails;
	}
	/*
	 * Delete Stuff
	 */
	public static void deleteNovelDetails(SQLiteDatabase db, NovelCollectionModel details) {
		int result = helper.delete(db, DBHelper.TABLE_NOVEL_DETAILS, DBHelper.COLUMN_ID + " = ?", new String[]{"" + details.getId()});
		Log.w(TAG, "NovelDetails Deleted: " + result);
	}

	public static boolean deleteNovel(SQLiteDatabase db, PageModel novel) {
		try{
			// TODO: delete images

			// delete chapter and books
			NovelCollectionModel details = getNovelDetails(db, novel.getPage());
			if(details != null) {
				ArrayList<BookModel> books = details.getBookCollections();
				for (BookModel bookModel : books) {
					BookModelHelper.deleteBookModel(db, bookModel);
				}
				deleteNovelDetails(db, details);
			}
			// delete page model;
			PageModelHelper.deletePageModel(db, novel);
			return true;
		} catch(Exception ex) {
			Log.e(TAG, "Error when deleting: " + novel.getPage(), ex);
			return false;
		}
	}

} public class NovelContentModelHelper {

	private static final String TAG = NovelContentModelHelper.class.toString();
	private static DBHelper helper = NovelsDao.getInstance().getDBHelper();

	// New column should be appended as the last column
	public static final String DATABASE_CREATE_NOVEL_CONTENT = "create table if not exists " + DBHelper.TABLE_NOVEL_CONTENT + "(" + DBHelper.COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " // 0
			+ DBHelper.COLUMN_CONTENT + " text not null, " // 1
			+ DBHelper.COLUMN_PAGE + " text unique not null, " // 2
			+ DBHelper.COLUMN_LAST_X + " integer, " // 3
			+ DBHelper.COLUMN_LAST_Y + " integer, " // 4
			+ DBHelper.COLUMN_ZOOM + " double, " // 5
			+ DBHelper.COLUMN_LAST_UPDATE + " integer, " // 6
			+ DBHelper.COLUMN_LAST_CHECK + " integer);"; // 7

	public static NovelContentModel cursorToNovelContent(Cursor cursor) {
		NovelContentModel content = new NovelContentModel();
		content.setId(cursor.getInt(0));
		content.setContent(cursor.getString(1));
		content.setPage(cursor.getString(2));
		content.setLastXScroll(cursor.getInt(3));
		content.setLastYScroll(cursor.getInt(4));
		content.setLastZoom(cursor.getDouble(5));
		content.setLastUpdate(new Date(cursor.getLong(6) * 1000));
		content.setLastCheck(new Date(cursor.getLong(7) * 1000));
		return content;
	}

	/*
	 * Query Stuff
	 */

	public static NovelContentModel getNovelContent(SQLiteDatabase db, String page) {
		// Log.d(TAG, "Selecting Novel Content: " + page);
		NovelContentModel content = null;

		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_NOVEL_CONTENT + " where " + DBHelper.COLUMN_PAGE + " = ? ", new String[] { page });
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				content = cursorToNovelContent(cursor);
				// Log.d(TAG, "Found: " + content.getPage() + " id: " + content.getId());
				break;
			}
		} finally {
			if (cursor != null)
				cursor.close();
		}
		if (content == null) {
			Log.w(TAG, "Not Found Novel Content: " + page);
		}
		return content;
	}

	/*
	 * Insert Stuff
	 */

	public static NovelContentModel insertNovelContent(SQLiteDatabase db, NovelContentModel content) throws Exception {
		ContentValues cv = new ContentValues();
		cv.put(DBHelper.COLUMN_CONTENT, content.getContent());
		cv.put(DBHelper.COLUMN_PAGE, content.getPage());
		cv.put(DBHelper.COLUMN_ZOOM, "" + content.getLastZoom());
		cv.put(DBHelper.COLUMN_LAST_CHECK, "" + (int) (new Date().getTime() / 1000));

		NovelContentModel temp = getNovelContent(db, content.getPage());
		if (temp == null) {
			cv.put(DBHelper.COLUMN_LAST_X, "" + content.getLastXScroll());
			cv.put(DBHelper.COLUMN_LAST_Y, "" + content.getLastYScroll());

			// Log.d(TAG, "Inserting Novel Content: " + content.getPage());
			if (content.getLastUpdate() == null)
				cv.put(DBHelper.COLUMN_LAST_UPDATE, 0);
			else
				cv.put(DBHelper.COLUMN_LAST_UPDATE, "" + (int) (content.getLastUpdate().getTime() / 1000));
			long id = helper.insertOrThrow(db, DBHelper.TABLE_NOVEL_CONTENT, null, cv);
			Log.i(TAG, "Novel Content Inserted, New id: " + id);
		} else {
			if (content.isUpdatingFromInternet()) {
				cv.put(DBHelper.COLUMN_LAST_X, "" + temp.getLastXScroll());
				cv.put(DBHelper.COLUMN_LAST_Y, "" + temp.getLastYScroll());
			} else {
				cv.put(DBHelper.COLUMN_LAST_X, "" + content.getLastXScroll());
				cv.put(DBHelper.COLUMN_LAST_Y, "" + content.getLastYScroll());
			}

			// Log.d(TAG, "Updating Novel Content: " + content.getPage() + " id: " + temp.getId());
			if (content.getLastUpdate() == null)
				cv.put(DBHelper.COLUMN_LAST_UPDATE, "" + (int) (temp.getLastUpdate().getTime() / 1000));
			else
				cv.put(DBHelper.COLUMN_LAST_UPDATE, "" + (int) (content.getLastUpdate().getTime() / 1000));
			int result = helper.update(db, DBHelper.TABLE_NOVEL_CONTENT, cv, DBHelper.COLUMN_ID + " = ? ", new String[] { "" + temp.getId() });
			Log.i(TAG, "Novel Content:" + content.getPage() + " Updated, Affected Row: " + result);
		}

		// update the pageModel
		PageModel pageModel = content.getPageModel();
		if (pageModel != null) {
			pageModel.setDownloaded(true);
			pageModel = PageModelHelper.insertOrUpdatePageModel(db, pageModel, false);
		}

		//content = getNovelContent(db, content.getPage());
		return content;
	}

	/*
	 * Delete Stuff
	 */

	public static boolean deleteNovelContent(SQLiteDatabase db, NovelContentModel content) {
		if (content != null && content.getId() > 0) {
			int result = helper.delete(db, DBHelper.TABLE_NOVEL_CONTENT, DBHelper.COLUMN_ID + " = ?", new String[] { "" + content.getId() });
			Log.w(TAG, "NovelContent Deleted: " + result);
			return result > 0 ? true : false;
		}
		return false;
	}

	public static boolean deleteNovelContent(SQLiteDatabase db, PageModel ref) {
		if (ref != null && !Util.isStringNullOrEmpty(ref.getPage())) {
			int result = helper.delete(db, DBHelper.TABLE_NOVEL_CONTENT, DBHelper.COLUMN_PAGE + " = ?", new String[] { "" + ref.getPage() });
			Log.w(TAG, "NovelContent Deleted: " + result);
			return result > 0 ? true : false;
		}
		return false;
	}
} public class PageModelHelper {

	private static final String TAG = PageModelHelper.class.toString();
	private static DBHelper helper = NovelsDao.getInstance().getDBHelper();

	// New column should be appended as the last column
	public static final String DATABASE_CREATE_PAGES = "create table if not exists " + DBHelper.TABLE_PAGE + "(" + DBHelper.COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " // 0
			+ DBHelper.COLUMN_PAGE + " text, " // 1
			+ DBHelper.COLUMN_TITLE + " text, " // 2
			+ DBHelper.COLUMN_TYPE + " text, " // 3
			+ DBHelper.COLUMN_PARENT + " text, " // 4
			+ DBHelper.COLUMN_LAST_UPDATE + " integer, " // 5
			+ DBHelper.COLUMN_LAST_CHECK + " integer, " // 6
			+ DBHelper.COLUMN_IS_WATCHED + " boolean, " // 7
			+ DBHelper.COLUMN_IS_FINISHED_READ + " boolean, " // 8
			+ DBHelper.COLUMN_IS_DOWNLOADED + " boolean, " // 9
			+ DBHelper.COLUMN_ORDER + " integer, " // 10
			+ DBHelper.COLUMN_STATUS + " text, " // 11
			+ DBHelper.COLUMN_IS_MISSING + " boolean, " // 12
			+ DBHelper.COLUMN_IS_EXTERNAL + " boolean, " // 13
			+ DBHelper.COLUMN_IMGURL + " text, "// 14 
 			+ DBHelper.COLUMN_SUMMARY + " text, "//15
			+ DBHelper.COLUMN_LANGUAGE + " text default '" + Constants.LANG_ENGLISH + "');"; // 16

	public static PageModel cursorToPageModel(Cursor cursor) {
		PageModel page = new PageModel();
		page.setId(cursor.getInt(0));
		page.setPage(cursor.getString(1));
		page.setTitle(cursor.getString(2));
		page.setType(cursor.getString(3));
		page.setParent(cursor.getString(4));
		page.setLastUpdate(new Date(cursor.getLong(5) * 1000));
		page.setLastCheck(new Date(cursor.getLong(6) * 1000));
		page.setWatched(cursor.getInt(7) == 1 ? true : false);
		page.setFinishedRead(cursor.getInt(8) == 1 ? true : false);
		page.setDownloaded(cursor.getInt(9) == 1 ? true : false);
		page.setOrder(cursor.getInt(10));
		page.setStatus(cursor.getString(11));
		page.setMissing(cursor.getInt(12) == 1 ? true : false);
		page.setExternal(cursor.getInt(13) == 1 ? true : false);
		page.setImgurl(cursor.getString(14));
		page.setSummary(cursor.getString(15));
		page.setLanguage(cursor.getString(16));

		if (cursor.getColumnCount() > 17) {
			page.setUpdateCount(cursor.getInt(18));
		}
		return page;
	}

	/*
	 * Queries Stuff
	 */

	public static ArrayList<PageModel> getAllContentPageModel(SQLiteDatabase db) {
		ArrayList<PageModel> result = new ArrayList<PageModel>();

		String sql = "select a.* " +
				" from " + DBHelper.TABLE_PAGE + " a " +
				" join " + DBHelper.TABLE_NOVEL_CONTENT + " b " +
				"   on " + "a." + DBHelper.COLUMN_PAGE + " = b." + DBHelper.COLUMN_PAGE;
		Cursor cursor = helper.rawQuery(db, sql, null);
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				PageModel page = cursorToPageModel(cursor);
				result.add(page);
				cursor.moveToNext();
			}
		} finally {
			if (cursor != null)
				cursor.close();
		}
		return result;
	}

	public static PageModel getMainPage(SQLiteDatabase db) {
		// Log.d(TAG, "Select Main_Page");
		PageModel page = getPageModel(db, Constants.ROOT_NOVEL);
		return page;
	}
    public static PageModel getHistoryPage(SQLiteDatabase db) {
        PageModel page = getPageModel(db, "Category:history");
        return page;
    }
    public static PageModel getKehuanPage(SQLiteDatabase db) {
        PageModel page = getPageModel(db, "Category:kehuan");
        return page;
    }
    public static PageModel getXuanhuanPage(SQLiteDatabase db) {
        PageModel page = getPageModel(db, "Category:xuanhuan");
        return page;
    }
    public static PageModel getQitaPage(SQLiteDatabase db) {
        PageModel page = getPageModel(db, "Category:qita");
        return page;
    }
    public static PageModel getUpdatePage(SQLiteDatabase db) {
        PageModel page = getPageModel(db, "Category:update");
        return page;
    }
    public static PageModel getMoodPage(SQLiteDatabase db) {
        PageModel page = getPageModel(db, "Category:mood");
        return page;
    }
	public static PageModel getTeaserPage(SQLiteDatabase db) {
		PageModel page = getPageModel(db, "Category:Teasers");
		return page;
	}

	public static PageModel getOriginalPage(SQLiteDatabase db) {
		PageModel page = getPageModel(db, "Category:Original");
		return page;
	}

	public static PageModel getAlternativePage(SQLiteDatabase db, String language) {
		/* Return PageModel depends on language */
		PageModel page = null;
		page = getPageModel(db, AlternativeLanguageInfo.getAlternativeLanguageInfo().get(language).getCategoryInfo());
		return page;
	}

	public static PageModel getPageModel(SQLiteDatabase db, String page) {
		// Log.d(TAG, "Select Page: " + page);
		PageModel pageModel = null;
		Cursor cursor = null;
		try {
			cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_PAGE + " where " + DBHelper.COLUMN_PAGE + " = ? ", new String[] { page });
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				pageModel = cursorToPageModel(cursor);
				// Log.d(TAG, "Found Page: " + pageModel.toString());
				break;
			}
		} finally {
			if (cursor != null)
				cursor.close();
		}

		// check again for case insensitive
		if (pageModel == null) {
			try {
				cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_PAGE + " where lower(" + DBHelper.COLUMN_PAGE + ") = lower(?) ", new String[] { page });
				cursor.moveToFirst();
				while (!cursor.isAfterLast()) {
					pageModel = cursorToPageModel(cursor);
					// Log.d(TAG, "Found Page: " + pageModel.toString());
					break;
				}
			} finally {
				if (cursor != null)
					cursor.close();
			}
		}
		return pageModel;
	}

	public static PageModel getPageModel(SQLiteDatabase db, int id) {
		PageModel pageModel = null;
		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_PAGE + " where " + DBHelper.COLUMN_ID + " = ? ", new String[] { "" + id });
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				pageModel = cursorToPageModel(cursor);
				// Log.d(TAG, "Found Page: " + pageModel.toString());
				break;
			}
		} finally {
			if (cursor != null)
				cursor.close();
		}
		return pageModel;
	}

	public static ArrayList<PageModel> selectAllByColumn(SQLiteDatabase db, String whereQuery, String[] values) {
		return selectAllByColumn(db, whereQuery, values, null);
	}

	public static ArrayList<PageModel> selectAllByColumn(SQLiteDatabase db, String whereQuery, String[] values, String orderQuery) {
		ArrayList<PageModel> pages = new ArrayList<PageModel>();

		String sql = "select * from " + DBHelper.TABLE_PAGE + " where " + whereQuery;
		if (orderQuery != null && orderQuery.length() > 0) {
			sql += " order by " + orderQuery;
		}

		Cursor cursor = helper.rawQuery(db, sql, values);
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				PageModel page = cursorToPageModel(cursor);
				pages.add(page);
				cursor.moveToNext();
			}
		} finally {
			if (cursor != null)
				cursor.close();
		}
		return pages;
	}

	public static PageModel selectFirstBy(SQLiteDatabase db, String column, String value) {
		// Log.d(TAG, "Select First: Column = " + column + " Value = " + value);
		PageModel page = null;

		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_PAGE + " where " + column + " = ? ", new String[] { value });
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				page = cursorToPageModel(cursor);
				// Log.d(TAG, "Found: " + page.toString());
				break;
			}
		} finally {
			if (cursor != null)
				cursor.close();
		}
		return page;
	}

	/*
	 * Insert Stuff
	 */
	public static ArrayList<PageModel> insertAllNovel(SQLiteDatabase db, ArrayList<PageModel> list) {
		ArrayList<PageModel> updatedList = new ArrayList<PageModel>();
		for (Iterator<PageModel> i = list.iterator(); i.hasNext();) {
			PageModel p = i.next();
			p = insertOrUpdatePageModel(db, p, false);
			updatedList.add(p);
		}
		return updatedList;
	}

	/**
	 * Insert/Update Page Model, with note:
	 * - isDownloaded flag is set from NovelContentModelHelper (insert/delete).
	 * - if PageModel.id > 0, the content will be updated from input page.
	 * 
	 * @param db
	 * @param page
	 * @param updateStatus
	 * @return
	 */
	public static PageModel insertOrUpdatePageModel(SQLiteDatabase db, PageModel page, boolean updateStatus) {
		// Log.d(TAG, page.toString());

		PageModel temp = selectFirstBy(db, DBHelper.COLUMN_PAGE, page.getPage());

		ContentValues cv = new ContentValues();
		cv.put(DBHelper.COLUMN_PAGE, page.getPage());
		cv.put(DBHelper.COLUMN_LANGUAGE, page.getLanguage());
		cv.put(DBHelper.COLUMN_TITLE, page.getTitle());
		cv.put(DBHelper.COLUMN_ORDER, page.getOrder());
		cv.put(DBHelper.COLUMN_PARENT, page.getParent());
		cv.put(DBHelper.COLUMN_TYPE, page.getType());
		cv.put(DBHelper.COLUMN_IMGURL, page.getImgurl());
		cv.put(DBHelper.COLUMN_SUMMARY, page.getSummary());
		if (updateStatus)
			cv.put(DBHelper.COLUMN_STATUS, page.getStatus());
		cv.put(DBHelper.COLUMN_IS_MISSING, page.isMissing());
		cv.put(DBHelper.COLUMN_IS_EXTERNAL, page.isExternal());

		if (temp == null) {
			// Fresh Data
			// Log.d(TAG, "Inserting: " + page.toString());
			if (page.getLastUpdate() == null)
				cv.put(DBHelper.COLUMN_LAST_UPDATE, 0);
			else
				cv.put(DBHelper.COLUMN_LAST_UPDATE, "" + (int) (page.getLastUpdate().getTime() / 1000));
			if (page.getLastCheck() == null)
				cv.put(DBHelper.COLUMN_LAST_CHECK, "" + (int) (new Date().getTime() / 1000));
			else
				cv.put(DBHelper.COLUMN_LAST_CHECK, "" + (int) (page.getLastCheck().getTime() / 1000));

			cv.put(DBHelper.COLUMN_IS_WATCHED, page.isWatched());
			cv.put(DBHelper.COLUMN_IS_FINISHED_READ, page.isFinishedRead());
			cv.put(DBHelper.COLUMN_IS_DOWNLOADED, page.isDownloaded());

			long id = helper.insertOrThrow(db, DBHelper.TABLE_PAGE, null, cv);
			Log.i(TAG, "Page Model Inserted, New Id: " + id);
		} else {
			// Log.d(TAG, "Updating: " + temp.toString());
			if (page.getLastUpdate() == null)
				cv.put(DBHelper.COLUMN_LAST_UPDATE, "" + (int) (temp.getLastUpdate().getTime() / 1000));
			else
				cv.put(DBHelper.COLUMN_LAST_UPDATE, "" + (int) (page.getLastUpdate().getTime() / 1000));
			if (page.getLastCheck() == null)
				cv.put(DBHelper.COLUMN_LAST_CHECK, "" + (int) (temp.getLastCheck().getTime() / 1000));
			else
				cv.put(DBHelper.COLUMN_LAST_CHECK, "" + (int) (page.getLastCheck().getTime() / 1000));

			if (page.getId() > 0) {
				// new data model have an id, use the input value.
				cv.put(DBHelper.COLUMN_IS_WATCHED, page.isWatched());
				cv.put(DBHelper.COLUMN_IS_FINISHED_READ, page.isFinishedRead());
				cv.put(DBHelper.COLUMN_IS_DOWNLOADED, page.isDownloaded());
			} else {
				cv.put(DBHelper.COLUMN_IS_WATCHED, temp.isWatched());
				cv.put(DBHelper.COLUMN_IS_FINISHED_READ, temp.isFinishedRead());
				cv.put(DBHelper.COLUMN_IS_DOWNLOADED, temp.isDownloaded());
			}

			int result = helper.update(db, DBHelper.TABLE_PAGE, cv, DBHelper.COLUMN_ID + " = ?", new String[] { "" + temp.getId() });
			Log.i(TAG, "Page Model: " + page.getPage() + " Updated, Affected Row: " + result);
		}

		// get the updated data.
		page = getPageModel(db, page.getPage());
		return page;
	}

	/*
	 * Delete Stuff
	 */
	public static void deletePageModel(SQLiteDatabase db, PageModel tempPage) {
		int result = helper.delete(db, DBHelper.TABLE_PAGE, DBHelper.COLUMN_ID + " = ?", new String[] { "" + tempPage.getId() });
		Log.w(TAG, "PageModel Deleted: " + result);
	}

} public class UpdateInfoModelHelper {
	private static final String TAG = UpdateInfoModelHelper.class.toString();
	private static DBHelper helper = NovelsDao.getInstance().getDBHelper();

	// New column should be appended as the last column
	public static final String DATABASE_CREATE_UPDATE_HISTORY = "create table if not exists "
			  + DBHelper.TABLE_UPDATE_HISTORY + "(" + DBHelper.COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "	// 0
			  						+ DBHelper.COLUMN_PAGE + " text not null, "							// 1
			  						+ DBHelper.COLUMN_UPDATE_TITLE + " text not null, "					// 2
			  						+ DBHelper.COLUMN_UPDATE_TYPE + " integer not null, "				// 3
			  						+ DBHelper.COLUMN_LAST_UPDATE + " integer);";						// 4

	public static UpdateInfoModel cursorToUpdateInfoModel(Cursor cursor) {
		UpdateInfoModel update = new UpdateInfoModel();
		update.setId(cursor.getInt(0));
		update.setUpdatePage(cursor.getString(1));
		update.setUpdateTitle(cursor.getString(2));
		int type = cursor.getInt(3);
		update.setUpdateType(UpdateType.values()[type]);
		update.setUpdateDate(new Date(cursor.getLong(4)*1000));
		return update;
	}

	/*
	 * Query Stuff
	 */

	public static ArrayList<UpdateInfoModel> getAllUpdateHistory(SQLiteDatabase db) {
		ArrayList<UpdateInfoModel> updates = new ArrayList<UpdateInfoModel>();

		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_UPDATE_HISTORY
					                   + " order by case "
					                   + "   when " + DBHelper.COLUMN_UPDATE_TYPE + " = " + UpdateType.UpdateTos.ordinal() + " then 0 "
					                   + "   when " + DBHelper.COLUMN_UPDATE_TYPE + " = " + UpdateType.NewNovel.ordinal() + " then 1 "
					                   + "   when " + DBHelper.COLUMN_UPDATE_TYPE + " = " + UpdateType.New.ordinal() + " then 2 "
					                   + "   when " + DBHelper.COLUMN_UPDATE_TYPE + " = " + UpdateType.Updated.ordinal() + " then 3 "
					                   + "   else 4 end "
					                   + ", " + DBHelper.COLUMN_LAST_UPDATE + " desc "
					                   + ", " + DBHelper.COLUMN_PAGE, null);
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				UpdateInfoModel update = cursorToUpdateInfoModel(cursor);
				updates.add(update);
				cursor.moveToNext();
			}
		} finally{
			if(cursor != null) cursor.close();
		}

		return updates;
	}

	public static UpdateInfoModel getUpdateHistory(SQLiteDatabase db, UpdateInfoModel update) {
		UpdateInfoModel result = null;

		Cursor cursor = helper.rawQuery(db, "select * from " + DBHelper.TABLE_UPDATE_HISTORY
										 + " where " + DBHelper.COLUMN_PAGE + " = ? "
				                   , new String[] {update.getUpdatePage()});
		try {
			cursor.moveToFirst();
			while (!cursor.isAfterLast()) {
				result = cursorToUpdateInfoModel(cursor);
				break;
			}
		} finally{
			if(cursor != null) cursor.close();
		}

		return result;
	}

	/*
	 * Insert Stuff
	 */

	public static int insertUpdateHistory(SQLiteDatabase db, UpdateInfoModel update) {
		UpdateInfoModel tempUpdate = getUpdateHistory(db, update);

		ContentValues cv = new ContentValues();
		cv.put(DBHelper.COLUMN_PAGE, update.getUpdatePage());
		cv.put(DBHelper.COLUMN_UPDATE_TITLE, update.getUpdateTitle());
		cv.put(DBHelper.COLUMN_UPDATE_TYPE, update.getUpdateType().ordinal());
		cv.put(DBHelper.COLUMN_LAST_UPDATE, (int) (update.getUpdateDate().getTime() / 1000));

		if(tempUpdate == null) {
			return (int) helper.insertOrThrow(db, DBHelper.TABLE_UPDATE_HISTORY, null, cv);
		}
		else {
			return helper.update(db, DBHelper.TABLE_UPDATE_HISTORY, cv, DBHelper.COLUMN_ID + " = ? ", new String[] {"" + tempUpdate.getId()});
		}
	}

	/*
	 * Delete Stuff
	 */

	public static void deleteAllUpdateHistory(SQLiteDatabase db) {
		db.execSQL("DROP TABLE IF EXISTS " + DBHelper.TABLE_UPDATE_HISTORY);
		db.execSQL(DATABASE_CREATE_UPDATE_HISTORY);
		Log.d(TAG, "Recreate " + DBHelper.TABLE_UPDATE_HISTORY);
	}

	public static int deleteUpdateHistory(SQLiteDatabase db, UpdateInfoModel updateInfo) {
		Log.d(TAG, "Deleting UpdateInfoModel id: " + updateInfo.getId());
		return helper.delete(db, DBHelper.TABLE_UPDATE_HISTORY, DBHelper.COLUMN_ID + " = ? "
				 , new String[] {updateInfo.getId() + ""});
	}
} public class PageModel {
	public static final String TYPE_NOVEL = "Novel";
	public static final String TYPE_OTHER = "Other";
	public static final String TYPE_CONTENT = "Content";
	public static final String TYPE_TOS = "Copyrights";
	private static final String TAG = PageModel.class.toString();
	private int id = -1;
	private String page;
	private String language; /* Attribute for language marker */
	private String title;
	private String type;
	private Date lastUpdate;
	private String parent;
	private PageModel parentPageModel;
	private PageModel pageModel;
	private Date lastCheck;
	private boolean isWatched;
	private boolean isFinishedRead = false;
	private boolean isDownloaded;
	private BookModel book;
	private int order;
	private String status;
	private boolean isHighlighted = false;
	private boolean isMissing = false;
	private boolean isExternal = false;
    private String summary;
    private String imgurl;
	private String redirectedTo;

	private int updateCount;

	// not saved to db
	private boolean isUpdated = false;

	public PageModel() {
	}

	public PageModel(String pageModel) {
		this.page = pageModel;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getPage() {
		return page;
	}

	public void setPage(String page) {
		this.page = page;
	}

	public String getLanguage() {
		if (language == null)
			language = Constants.LANG_ENGLISH;
		return language;
	}

	public void setLanguage(String language) {
		this.language = language;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public Date getLastUpdate() {
		return lastUpdate;
	}

	public void setLastUpdate(Date lastUpdate) {
		this.lastUpdate = lastUpdate;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getParent() {
		return parent;
	}

	public void setParent(String parent) {
		this.parent = parent;
	}

	public Date getLastCheck() {
		return lastCheck;
	}

	public void setLastCheck(Date lastCheck) {
		this.lastCheck = lastCheck;
	}

	public boolean isWatched() {
		return isWatched;
	}

	public void setWatched(boolean isWatched) {
		this.isWatched = isWatched;
	}

	public boolean isFinishedRead() {
		return isFinishedRead;
	}

	public void setFinishedRead(boolean isFinishedRead) {
		this.isFinishedRead = isFinishedRead;
	}

	public int getOrder() {
		return order;
	}

	public void setOrder(int order) {
		this.order = order;
	}

	public PageModel getParentPageModel() throws Exception {
		if (this.parentPageModel == null) {
			NovelsDao dao = NovelsDao.getInstance();
			PageModel tempPage = new PageModel();
			if (this.type.contentEquals(TYPE_CONTENT)) {
				String tempParent = parent.substring(0, parent.indexOf(Constants.NOVEL_BOOK_DIVIDER));
				tempPage.setPage(tempParent);
			}
			else {
				tempPage.setPage(this.parent);
			}
			this.parentPageModel = dao.getPageModel(tempPage, null);
		}
		return parentPageModel;
	}

	public void setParentPageModel(PageModel parentPageModel) {
		this.parentPageModel = parentPageModel;
	}

	public PageModel getPageModel() throws Exception {
		if (this.pageModel == null) {
			NovelsDao dao = NovelsDao.getInstance();
			PageModel tempPage = new PageModel();
			tempPage.setPage(this.page);
			this.pageModel = dao.getPageModel(tempPage, null);
		}
		return pageModel;
	}

	public void setPageModel(PageModel pageModel) {
		this.pageModel = pageModel;
	}

	public boolean isDownloaded() {
		return isDownloaded;
	}

	public void setDownloaded(boolean isDownloaded) {
		this.isDownloaded = isDownloaded;
	}

	public BookModel getBook() {
		if (this.getType() != null && this.getType().equals(TYPE_CONTENT)) {
			if (this.book == null) {
				NovelsDao dao = NovelsDao.getInstance();
				try {
					String bookTitle = parent.substring(parent.indexOf(Constants.NOVEL_BOOK_DIVIDER) + Constants.NOVEL_BOOK_DIVIDER.length());
					NovelCollectionModel details = dao.getNovelDetails(getParentPageModel(), null);
					for (Iterator<BookModel> iBook = details.getBookCollections().iterator(); iBook.hasNext();) {
						BookModel tempBook = iBook.next();
						if (tempBook.getTitle().equals(bookTitle)) {
							this.book = tempBook;
							break;
						}
					}
				} catch (Exception e) {
					Log.e(TAG, "Unable to get book for: " + getPage(), e);
				}
			}
			return this.book;
		}
		return null;
	}

	public void setBook(BookModel book) {
		this.book = book;
	}

	public boolean isUpdated() {
		return isUpdated;
	}

	public void setUpdated(boolean isUpdated) {
		this.isUpdated = isUpdated;
	}

	public String getRedirectedTo() {
		return redirectedTo;
	}

	public void setRedirectedTo(String redirectedTo) {
		this.redirectedTo = redirectedTo;
	}

	public boolean isHighlighted() {
		return isHighlighted;
	}

	public void setHighlighted(boolean isHighlighted) {
		this.isHighlighted = isHighlighted;
	}

	public boolean isMissing() {
		return isMissing;
	}

	public void setMissing(boolean isMissing) {
		this.isMissing = isMissing;
	}

	public String getStatus() {
		return status;
	}

	public void setStatus(String status) {
		this.status = status;
	}

	public boolean isTeaser() {
		if (status != null && status.length() > 0) {
			return status.contains(Constants.STATUS_TEASER);
		}
		return false;
	}

	public boolean isStalled() {
		if (status != null && status.length() > 0) {
			return status.contains(Constants.STATUS_STALLED);
		}
		return false;
	}

	public boolean isAbandoned() {
		if (status != null && status.length() > 0) {
			return status.contains(Constants.STATUS_ABANDONED);
		}
		return false;
	}

	public boolean isPending() {
		if (status != null && status.length() > 0) {
			return status.contains(Constants.STATUS_PENDING);
		}
		return false;
	}

	public boolean isExternal() {
		return isExternal;
	}

	public void setExternal(boolean isExternal) {
		this.isExternal = isExternal;
	}

	public int getUpdateCount() {
		return updateCount;
	}

	public void setUpdateCount(int updateCount) {
		this.updateCount = updateCount;
	}

	public boolean isRedlink() {
		if (Util.isStringNullOrEmpty(this.page))
			return false;
		return this.page.endsWith("&action=edit&redlink=1");
	}

    public String getSummary() {
        return summary;
    }

    public void setSummary(String summary) {
        this.summary = summary;
    }

    public String getImgurl() {
        return imgurl;
    }

    public void setImgurl(String imgurl) {
        this.imgurl = imgurl;
    }


}
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;
import com.dotcool.reader.AlternativeLanguageInfo;
import com.dotcool.reader.Constants;
import com.dotcool.reader.helper.db.BookModelHelper;
import com.dotcool.reader.helper.db.BookmarkModelHelper;
import com.dotcool.reader.helper.db.ImageModelHelper;
import com.dotcool.reader.helper.db.NovelCollectionModelHelper;
import com.dotcool.reader.helper.db.NovelContentModelHelper;
import com.dotcool.reader.helper.db.PageModelHelper;
import com.dotcool.reader.helper.db.UpdateInfoModelHelper;
import com.dotcool.reader.model.PageModel;
//package com.nandaka.bakareaderclone.helper;
package com.dotcool.reader.helper;




public class DBHelper extends SQLiteOpenHelper {
	public static final String TAG = DBHelper.class.toString();
	public static final String TABLE_PAGE = "pages";
	public static final String COLUMN_ID = "_id";
	public static final String COLUMN_PAGE = "page";
	public static final String COLUMN_LANGUAGE = "language";
	public static final String COLUMN_BOOK_ID = "bookid";
	public static final String COLUMN_CHAPTER_ID = "chapter";
	public static final String COLUMN_LAST_UPDATE = "last_update";
	public static final String COLUMN_LAST_CHECK = "last_check";
	public static final String COLUMN_TITLE = "title";
	public static final String COLUMN_TYPE = "type";
	public static final String COLUMN_PARENT = "parent";
	public static final String COLUMN_IS_WATCHED = "is_watched";
	public static final String COLUMN_IS_FINISHED_READ = "is_finished_read";
	public static final String COLUMN_IS_DOWNLOADED = "is_downloaded";
	public static final String COLUMN_ORDER = "_index";
	public static final String COLUMN_STATUS = "status";
	public static final String COLUMN_IMGURL = "imgurl";
    public static final String COLUMN_SUMMARY = "summary";
	public static final String COLUMN_IS_MISSING = "is_missing";
	public static final String COLUMN_IS_EXTERNAL = "is_external";

	public static final String TABLE_IMAGE = "images";
	public static final String COLUMN_IMAGE_NAME = "name";
	public static final String COLUMN_FILEPATH = "filepath";
	public static final String COLUMN_URL = "url";
	public static final String COLUMN_REFERER = "referer";
	public static final String COLUMN_IS_BIG_IMAGE = "is_big_image";

	public static final String TABLE_NOVEL_DETAILS = "novel_details";
	public static final String COLUMN_SYNOPSIS = "synopsis";

	public static final String TABLE_NOVEL_BOOK = "novel_books";

	public static final String TABLE_NOVEL_CONTENT = "novel_books_content";
	public static final String COLUMN_CONTENT = "content";
	public static final String COLUMN_LAST_X = "lastXScroll";
	public static final String COLUMN_LAST_Y = "lastYScroll";
	public static final String COLUMN_ZOOM = "lastZoom";

	public static final String TABLE_NOVEL_BOOKMARK = "novel_bookmark";
	public static final String COLUMN_PARAGRAPH_INDEX = "p_index";
	public static final String COLUMN_EXCERPT = "excerpt";
	public static final String COLUMN_CREATE_DATE = "create_date";

	public static final String TABLE_UPDATE_HISTORY = "update_history";
	public static final String COLUMN_UPDATE_TITLE = "update_title";
	public static final String COLUMN_UPDATE_TYPE = "update_type";

	public static final String DATABASE_NAME = "pages.db";
	public static final int DATABASE_VERSION = 26;

	// Use /files/database to standarize with newer android.
	public static final String DB_ROOT_SD = Environment.getExternalStorageDirectory().getAbsolutePath().toString() + "/Android/data/" + Constants.class.getPackage().getName() + "/files/databases";

	public DBHelper(Context context) {
		super(context, getDbPath(context), null, DATABASE_VERSION);
	}

	public static String getDbPath(Context context) {
		String dbPath = null;
		File path = context.getExternalFilesDir(null);
		if (path != null)
			dbPath = path.getAbsolutePath() + "/databases/" + DATABASE_NAME;
		else {
			path = new File(DB_ROOT_SD);
			if (!(path.mkdirs() || path.isDirectory()))
				Log.e(TAG, "DB Path doesn't exists/failed to create.");
			Toast.makeText(context, String.format("Failed to create/open db directory %s", DB_ROOT_SD), Toast.LENGTH_LONG).show();
			dbPath = DB_ROOT_SD + "/" + DATABASE_NAME;
		}
		Log.d(TAG, "DB Path : " + dbPath);
		return dbPath;
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL(PageModelHelper.DATABASE_CREATE_PAGES);
		db.execSQL(ImageModelHelper.DATABASE_CREATE_IMAGES);
		db.execSQL(NovelCollectionModelHelper.DATABASE_CREATE_NOVEL_DETAILS);
		db.execSQL(BookModelHelper.DATABASE_CREATE_NOVEL_BOOKS);