package jp.sourceforge.nicoro;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Locale;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;

import static jp.sourceforge.nicoro.Log.LOG_TAG;
import jp.sourceforge.nicoro.R;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.WebBackForwardList;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;

public class NicoroWebBrowser extends Activity {
	private static final boolean DEBUG_LOGV = Release.IS_DEBUG && false;
	private static final boolean DEBUG_LOGD = Release.IS_DEBUG && true;

	private static final String DOMAIN_NICO_JP = "www.nicovideo.jp";
	private static final String DOMAIN_NICO_TW = "tw.nicovideo.jp";
	private static final String DOMAIN_NICO_ES = "es.nicovideo.jp";
	private static final String DOMAIN_NICO_DE = "de.nicovideo.jp";
	private static final String DOMAIN_NICO_NINE = "nine.nicovideo.jp";

	private static final String PATTERN_VIDEO_URL =
		"http://(?:www|tw|es|de|nine).nicovideo.jp/watch/([a-z0-9]+)";
	private static final String PATTERN_GET_USERID_FROM_USERSESSION =
		"user_session=user_session_([^_]+)_";
	private static final String PATTERN_IS_NOT_HTML =
		"(\\.jpg|\\.jpeg|\\.JPG|\\.JPEG|\\.png|\\.PNG|\\.gif|\\.GIF|\\.js|\\.JS|\\.css|\\.CSS)$";
	private static final String PATTERN_JIKKYO_URL =
		"http://jk.nicovideo.jp/watch/([a-z0-9]+)";
	private static final String PATTERN_JIKKYO_APP =
		"^nicojk:(.+)";
    private static final String PATTERN_LIVE_URL =
        "http://live.nicovideo.jp/watch/([a-z0-9]+)";
    private static final String PATTERN_GET_TITLE =
        "^(.+)(?:\\s*-\\s*)?ニコニコ生放送\\s*$";

	private static final int MSG_ID_RUN_CACHING = 0;
	private static final int MSG_ID_FINISHED_CACHE = 1;
	private static final int MSG_ID_UPDATE_TITLE_URL = 2;
	private static final int MSG_ID_STARTED_CACHE = 3;
    private static final int MSG_ID_ACTION_START_PLAY = 4;
    private static final int MSG_ID_ACTION_START_CACHE = 5;
    private static final int MSG_ID_ACTION_START_JIKKYO = 6;
    private static final int MSG_ID_ACTION_START_LIVE = 7;
    private static final int MSG_ID_GO_BACK_WEBVIEW = 8;

    private static final int WEBVIEW_POOL_MIN_SIZE = 5;

    private class NicoroWebChromeClient extends WebChromeClient {
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, Log.buf().append("WebChromeClient#onProgressChanged: newProgress=")
                        .append(newProgress).toString());
            }
            super.onProgressChanged(view, newProgress);
            NicoroWebBrowser.this.setProgress(newProgress * 100);

            if (newProgress > 30 && mJsObj.urlRss == null) {
//                assert view == mWebView;
                view.loadUrl(mResJsFindRss);
            }
        }
        @Override
        public void onReceivedTitle(WebView view, String title) {
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, Log.buf().append("WebChromeClient#onReceivedTitle: title=")
                        .append(title)
                        .toString());
            }
            mHandler.sendEmptyMessage(MSG_ID_UPDATE_TITLE_URL);
        }
        @Override
        public boolean onCreateWindow(WebView view, boolean dialog,
                boolean userGesture, Message resultMsg) {
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, Log.buf().append("WebChromeClient#onCreateWindow: dialog=")
                        .append(dialog).append(" userGesture=")
                        .append(userGesture).append(" resultMsg=")
                        .append(resultMsg)
                        .toString());
            }
            assert view == mWebView;
            WebView newWebView = (WebView) getLayoutInflater().inflate(
                    R.layout.nicoro_webbrowser_webview,
                    (ViewGroup) view.getParent(), false);
            initializeWebView(newWebView);
            Util.replaceView(view, newWebView);

            mWebView = newWebView;
            mWebViewPool.addLast(view);

            ((WebView.WebViewTransport) resultMsg.obj).setWebView(newWebView);
            resultMsg.sendToTarget();
            return true;
        }
        @Override
        public void onCloseWindow(WebView window) {
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, Log.buf().append("WebChromeClient#onCloseWindow: window=")
                        .append(window)
                        .toString());
            }
            assert window == mWebView;
            if (mWebViewPool.isEmpty()) {
                finish();
            } else {
                WebView lastWebView = mWebViewPool.removeLast();
                Util.replaceView(window, lastWebView);
                mWebView = lastWebView;
                updateLastUrl(lastWebView.getUrl());
                window.destroy();
            }
        }
    }
    private class NicoroWebViewClient extends WebViewClient {
        @Override
        public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, Log.buf().append("WebViewClient#doUpdateVisitedHistory: url=")
                        .append(url).append(" isReload=").append(isReload)
                        .toString());
            }
            if (!isReload) {
                if (url.equals("http://jk.nicovideo.jp/")) {
                    // ヒント表示
                    Util.showInfoToast(getApplicationContext(),
                            R.string.toast_explain_launch_jikkyo);
                }
            }
        }

        @Override
        public void onLoadResource(WebView view, String url) {
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, Log.buf().append("WebViewClient#onLoadResource: url=")
                        .append(url)
                        .toString());
            }
            onChangePage(view, url);
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, Log.buf().append("WebViewClient#onPageStarted: url=")
                        .append(url).append(" favicon=").append(favicon)
                        .toString());
            }
//            onChangePage(view, url);

//            // 念のため前のをキャンセル
//            mHandler.removeMessages(MSG_ID_UPDATE_TITLE_URL);
//            mHandler.sendEmptyMessageDelayed(MSG_ID_UPDATE_TITLE_URL,
//                    1000L);

//            boolean wasLogout;
//            if (url.equals("https://secure.nicovideo.jp/secure/logout")) {
//                // ログアウト
//                mCookieUserSession = null;
//                mUserId = null;
////                  CookieManager cookieManager = mCookieManager;
////                  cookieManager.removeExpiredCookie();
////                  cookieManager.setCookie("nicovideo.jp", "user_session=");
//                wasLogout = true;
//            } else {
//                wasLogout = false;
//            }
//
//            if (!wasLogout) {
//                if (mCookieUserSession == null && url.indexOf("nicovideo.jp") >= 0) {
//                    updateCookieUserSession();
//                }
//            }

            mJsObj.urlRss = null;
            mJsObj.streamDescription = null;
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, Log.buf().append("WebViewClient#onPageFinished: url=")
                        .append(url)
                        .toString());
            }
//            // 念のためもう一度実行
//            mHandler.removeMessages(MSG_ID_UPDATE_TITLE_URL);
//            mHandler.sendEmptyMessage(MSG_ID_UPDATE_TITLE_URL);

            view.loadUrl(mResJsReplaceFlvplayerContainer);
            view.loadUrl(mResJsReplacePlayerbox);
            view.loadUrl(mResJsGetStreamDescription);
            view.loadUrl(mJsMarginBottom);
//            view.loadUrl(mResJsReplaceBlank);
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, Log.buf().append("WebViewClient#shouldOverrideUrlLoading: url=")
                        .append(url)
                        .toString());
            }
            Matcher matcherJikkyoApp = mMatcherJikkyoApp.get().reset(url);
            if (matcherJikkyoApp.find()) {
                // 実況アプリ起動ボタン
                startJikkyo(matcherJikkyoApp.group(1));
                return true;
            }
            if (!mMatcherIsNicovideo.get().reset(url).find()
                    && !mMatcherIsNicoseiga.get().reset(url).find()) {
                // 外部サイトへのリンクの場合はIntent投げる
                Intent intent = new Intent(Intent.ACTION_VIEW,
                        Uri.parse(url));
                intent.addCategory(Intent.CATEGORY_BROWSABLE);
                startActivityIfNeeded(intent, 0);
                return true;
            }

            return false;
        }
    }

	private WebView mWebView;
	private ViewGroup mButtonVideoControl;
	private Button mButtonAction;
	private VariableLabelView mLabelStatus1;
	private VariableLabelView mLabelStatus2;
	private VariableLabelView mLabelTitleUrl;

	private ProgressDialog mProgressDialogStartPlay;

	private CookieSyncManager mCookieSyncManager;
	private CookieManager mCookieManager;

    private LinkedList<WebView> mWebViewPool = new LinkedList<WebView>();
    private WatchCache mWatchCache = new WatchCache(new Runnable() {
        @Override
        public void run() {
            if (mWebViewPool.size() > WEBVIEW_POOL_MIN_SIZE) {
                try {
                    WebView oldWebView = mWebViewPool.removeFirst();
                    oldWebView.destroy();
                } catch (NoSuchElementException e) {
                    if (DEBUG_LOGD) {
                        Log.d(LOG_TAG, e.toString(), e);
                    }
                }
            }
        }
    });

	/** 動画視聴向けCookie */
	private String mCookieNicoHistory;

	// 設定
	private SharedPreferences mSharedPreferences;
	private SharedPreferences.OnSharedPreferenceChangeListener mSharedPreferencesChangeListener;
	/** 認証向けCookie */
	private String mCookieUserSession;
	private String mLastUrl;
	private String mUserAgent;

	private String mUserId;

	private String mVideoNumber;
	private String mJikkyoNumber;
    private String mLiveNumber;

	private boolean mWasDestroyed;

	private ThumbInfoCacher mThumbInfoCacher;

	private MatcherThreadLocal mMatcherVideoUrl =
	    new MatcherThreadLocal(PATTERN_VIDEO_URL);
	private MatcherThreadLocal mMatcherGetUserID =
	    new MatcherThreadLocal(PATTERN_GET_USERID_FROM_USERSESSION);
	private MatcherThreadLocal mMatcherIsNotHtml =
	    new MatcherThreadLocal(PATTERN_IS_NOT_HTML);
	private MatcherThreadLocal mMatcherJikkyoApp =
	    new MatcherThreadLocal(PATTERN_JIKKYO_APP);
	private MatcherThreadLocal mMatcherJikkyoUrl =
	    new MatcherThreadLocal(PATTERN_JIKKYO_URL);
	private MatcherThreadLocal mMatcherIsNicovideo =
	    new MatcherThreadLocal(NicoroAPIManager.PATTERN_NICOVIDEO_URL);
    private MatcherThreadLocal mMatcherIsNicoseiga =
        new MatcherThreadLocal(NicoroAPIManager.PATTERN_NICOSEIGA_URL);
    private MatcherThreadLocal mMatcherLiveUrl =
        new MatcherThreadLocal(PATTERN_LIVE_URL);

	private DecimalFormat mDecimalFormatMB;

	private MenuInflater mMenuInflater;

	private JsObj mJsObj;

	private Locale mCurrentLocale;
	// リソースのキャッシュ
	private String mResStringStatusLowShort;
	private String mResStringStatusHighShort;
	private String mResStringStatusHighLowShort;
	private String mResStringStatus1NotLogin;
	private String mResStringStatus1CompletedCache;
	private String mResStringStatus1RunningCache;
	private String mResStringStatus1WaitingCache;
	private String mResStringStatus1NoneCache;
	private String mResStringStatus2RunningCache;
	private String mResStringStatus2WaitingCache;
	private String mResStringStatus2StartingCache;
	private String mResStringProgressStartPlay;

	private String mResJsFindRss;
	private String mResJsReplaceFlvplayerContainer;
    private String mResJsReplacePlayerbox;
    private String mResJsReplaceBlank;
    private String mResJsGetStreamDescription;

	private String mJsMarginBottom;

	private Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
		    if (mWasDestroyed) {
		        return;
		    }

	        WebView webView = mWebView;
			switch (msg.what) {
			case MSG_ID_STARTED_CACHE:
				// キャッシュ開始
				updateStatus1();
				updateStatus2((String) msg.obj);
				if (webView != null) {
	                webView.loadUrl(mResJsReplaceFlvplayerContainer);
				}
				break;
			case MSG_ID_RUN_CACHING:
				// キャッシュ実行中
				updateStatus2((String) msg.obj);
				break;
			case MSG_ID_FINISHED_CACHE:
				// キャッシュ完了
				updateStatus1();
				updateStatus2("");
                if (webView != null) {
                    webView.loadUrl(mResJsReplaceFlvplayerContainer);
                }
				break;
			case MSG_ID_UPDATE_TITLE_URL:
				// ページのタイトルとURL表示を更新
                if (webView != null) {
					String title = webView.getTitle();
					if (title == null) {
		    			mHandler.sendEmptyMessageDelayed(MSG_ID_UPDATE_TITLE_URL,
		    					1000L);
					} else {
		    			String newText = title + " : " + webView.getUrl();
		    			if (!newText.equals(mLabelTitleUrl.getText())) {
		    				mLabelTitleUrl.setText(newText);
		    			}
					}
				}
				break;
			case MSG_ID_ACTION_START_PLAY:
			    startPlay(msg.arg1 != 0);
			    break;
			case MSG_ID_ACTION_START_CACHE:
			    startCache(msg.arg1 != 0);
			    break;
			case MSG_ID_ACTION_START_JIKKYO:
			    assert mJikkyoNumber != null;
			    startJikkyo(mJikkyoNumber);
			    break;
            case MSG_ID_ACTION_START_LIVE:
                assert mLiveNumber != null;
                startLive(mLiveNumber);
                break;
			case MSG_ID_GO_BACK_WEBVIEW:
			    goBackWebView();
			    break;
			default:
				assert false : msg.what;
				break;
			}
		}
	};

	private IVideoCacheService mVideoCacheService;
	private ServiceConnection mVideoCacheServiceConnection = new ServiceConnection() {
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			mVideoCacheService = IVideoCacheService.Stub.asInterface(service);

			try {
				mVideoCacheService.addListener(mVideoCacheServiceCallback);
			} catch (RemoteException e) {
				Log.e(LOG_TAG, e.toString(), e);
			}
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			try {
				mVideoCacheService.removeListener(mVideoCacheServiceCallback);
			} catch (RemoteException e) {
				Log.e(LOG_TAG, e.toString(), e);
			}

			mVideoCacheService = null;
		}

	};
	private IVideoCacheServiceCallback mVideoCacheServiceCallback =
		new IVideoCacheServiceCallback.Stub() {
			private AtomicReference<StringBuffer> mStringBuffer =
				new AtomicReference<StringBuffer>(new StringBuffer(64));

			@Override
			public void onNotifyProgress(String videoNumber, boolean isLow,
					int seekOffsetWrite, int contentLength)
					throws RemoteException {
				if (DEBUG_LOGV) {
					Log.v(LOG_TAG, Log.buf()
							.append("onNotifyProgress() videoNumber=").append(videoNumber)
							.append(" isLow=").append(isLow)
							.append(" seekOffsetWrite=").append(seekOffsetWrite)
							.append(" contentLength=").append(contentLength)
							.toString());
				}
				// このコールバックは複数スレッドから同時に呼ばれることはない設計
				StringBuffer message = mStringBuffer.getAndSet(null);
				assert message != null;
				message.setLength(0);

				message.append(videoNumber);
				if (isLow) {
					message.append(mResStringStatusLowShort);
				}
				message.append(mResStringStatus2RunningCache).append(": ");
				getRunCachingProgressMessage(message,
						seekOffsetWrite, contentLength);
				addWaitingCacheMessage(message);
				mHandler.obtainMessage(MSG_ID_RUN_CACHING,
						message.toString()).sendToTarget();

				StringBuffer temp = mStringBuffer.getAndSet(message);
				assert temp == null;
			}
			@Override
			public void onStart(String videoNumber, boolean isLow)
			throws RemoteException {
				StringBuilder message = new StringBuilder(32);
				message.append(videoNumber);
				if (isLow) {
					message.append(mResStringStatusLowShort);
				}
				message.append(mResStringStatus2StartingCache);
				addWaitingCacheMessage(message);
				mHandler.obtainMessage(MSG_ID_STARTED_CACHE,
						message.toString()).sendToTarget();
			}
			@Override
			public void onFinished(String videoNumber, boolean isLow)
			throws RemoteException {
				mHandler.sendEmptyMessage(MSG_ID_FINISHED_CACHE);
			}
			@Override
			public void onAllFinished() throws RemoteException {
//				unbindService(mVideoCacheServiceConnection);
			}

			StringBuffer addWaitingCacheMessage(StringBuffer message)
			throws RemoteException {
				int waitRequestSize = mVideoCacheService.getWaitRequestSize();
				if (waitRequestSize > 0) {
					message.append(" （")
						.append(mResStringStatus2WaitingCache)
						.append(":")
						.append(waitRequestSize)
						.append("）");
				}
				return message;
			}
			StringBuilder addWaitingCacheMessage(StringBuilder message)
			throws RemoteException {
				int waitRequestSize = mVideoCacheService.getWaitRequestSize();
				if (waitRequestSize > 0) {
					message.append(" （")
						.append(mResStringStatus2WaitingCache)
						.append(":")
						.append(waitRequestSize)
						.append("）");
				}
				return message;
			}
	};

//	private AsyncCheckCookieUserSession mAsyncCheckCookieUserSession = null;
//	private class AsyncCheckCookieUserSession extends AsyncTask<Void, Void, Boolean> {
//		private HttpUriRequest mHttpRequest;
//
//		@Override
//		protected void onPreExecute() {
//			mHttpRequest =
//				NicoroAPIManager.createRequestIsCookieUserSessionValid(
//						mCookieUserSession, mUserAgent);
//		}
//
//		@Override
//		protected Boolean doInBackground(Void... params) {
//	        return NicoroAPIManager.checkIsCookieUserSessionValid(mHttpRequest);
//		}
//
//		@Override
//		protected void onPostExecute(Boolean result) {
//			reflectIsCookieUserSessionValid(result);
//			updateStatus1();
//
//			mAsyncCheckCookieUserSession = null;
//		}
//
//		public void stop() {
//			if (mHttpRequest != null && !mHttpRequest.isAborted()) {
//				mHttpRequest.abort();
//			}
//		}
//	}

	private AsyncStartPlay mAsyncStartPlay = null;
	private class AsyncStartPlay extends AsyncTask<Void, Void, Intent> {
		private HttpUriRequest mHttpRequestNicoHistory;
		private HttpUriRequest mHttpRequestUserSession;
		private HttpPost mHttpRequestAuthorizeCookie;
		private NicoroAPIManager.VideoPlayerIntentCreator mIntentCreator;
		private boolean mForceLow;
		private String mUrl;
		private String mVideoNumber;
		private Exception mException;

		@Override
		protected void onPreExecute() {
		    WebView webView = mWebView;
		    if (webView == null) {
                if (DEBUG_LOGD) {
                    Log.d(LOG_TAG, "AsyncStartPlay: mWebView is null");
                }
                mException = new FailPreparePlayVideoException();
                return;
		    }
//			String url = webView.getOriginalUrl();
			String url = webView.getUrl();
			if (url == null) {
				if (DEBUG_LOGD) {
					Log.d(LOG_TAG, "AsyncStartPlay: mWebView.getUrl() is null");
				}
				mException = new FailPreparePlayVideoException();
				return;
			}
			mUrl = url;

//			updateCookieUserSession();
//			if (DEBUG_LOGD) {
//				Log.d(LOG_TAG, "mCookieUserSession: " + mCookieUserSession);
//			}

			String videoNumber = Util.getFirstMatch(
			        mMatcherVideoUrl.get().reset(url));
			if (videoNumber == null) {
				mException = new FailPreparePlayVideoException();
				return;
			}
			mVideoNumber = videoNumber;

			if (mForceLow) {
				createHttpRequestGetHistory(videoNumber);
			}
		}

		@Override
		protected Intent doInBackground(Void... params) {
			if (mException != null) {
				return null;
			}
			return doInBackgroundMain(false, mForceLow);
		}

		private Intent doInBackgroundMain(boolean neverRetry,
				boolean forceHistory) {
			try {
				if (forceHistory) {
					// 視聴履歴Cookie取り直す
					mCookieNicoHistory = NicoroAPIManager.getCookieNicoHistory(
							Util.createHttpClient(), mHttpRequestNicoHistory);
				} else {
					mCookieNicoHistory = Util.getCookieValueFromManager(
							mUrl, "nicohistory");
				}
				if (DEBUG_LOGD) {
					Log.d(LOG_TAG, "mCookieNicoHistory: " + mCookieNicoHistory);
				}

				if (mCookieUserSession == null
						|| mUserId == null
						|| mCookieNicoHistory == null) {
					throw new FailPreparePlayVideoException(
							getString(R.string.errormessage_cookie_fail));
				}

//				mLastVideoNumber = videoNumber;
				mIntentCreator =
					new NicoroAPIManager.VideoPlayerIntentCreator();
				Intent intent = mIntentCreator.create(
						getApplicationContext(),
						mVideoNumber, mCookieUserSession, mCookieNicoHistory,
						mUserId, mUserAgent, mForceLow);
				return intent;
			} catch (FailPreparePlayVideoException e) {
				if (DEBUG_LOGD) {
					Log.d(LOG_TAG, e.toString(), e);
				}
				if (!neverRetry && shouldRetry()) {
		        	return doInBackgroundMain(true, true);
				}
				mException = e;
			} catch (IOException e) {
				Log.d(LOG_TAG, e.toString(), e);
				mException = e;
			}
			return null;
		}

		@Override
		protected void onPostExecute(Intent result) {
			assert mProgressDialogStartPlay != null;
			if (mProgressDialogStartPlay != null) {
				mProgressDialogStartPlay.dismiss();
			}
			if (result == null) {
				String message;
				if (mException == null) {
					assert false;
		        	// 原因不明で再生失敗
	        		message = getString(R.string.errormessage_play_fail_unknown);
				} else if (mException instanceof FailPreparePlayVideoException) {
					message = mException.getMessage();
		        	if (message == null) {
			        	// 原因不明で再生失敗
		        		message = getString(R.string.errormessage_play_fail_unknown);
		        	}
				} else {
					message = mException.toString();
				}
				Util.showErrorDialog(NicoroWebBrowser.this,
						message,
						false);
			} else {
				Intent intent = result;
				// キャッシュ処理が動作していれば停止
				if (mVideoCacheService != null) {
					try {
						mVideoCacheService.stopCache(mVideoNumber);
					} catch (RemoteException e) {
						Log.e(LOG_TAG, e.toString(), e);
					}
				}
				startActivityIfNeeded(intent, 0);
	            WebView webView = mWebView;
	            if (webView != null) {
	                webView.stopLoading();
	            }
			}

			mAsyncStartPlay = null;
		}

		public AsyncStartPlay executeWrapper(boolean forceLow) {
			mForceLow = forceLow;
			return (AsyncStartPlay) execute();
		}

		public void stop() {
			if (mHttpRequestNicoHistory != null && !mHttpRequestNicoHistory.isAborted()) {
				mHttpRequestNicoHistory.abort();
			}
			if (mHttpRequestUserSession != null
					&& !mHttpRequestUserSession.isAborted()) {
				mHttpRequestUserSession.abort();
			}
			if (mHttpRequestAuthorizeCookie != null
					&& !mHttpRequestAuthorizeCookie.isAborted()) {
				mHttpRequestAuthorizeCookie.abort();
			}
			if (mIntentCreator != null) {
				mIntentCreator.abort();
			}
		}

		private void createHttpRequestGetHistory(String videoNumber) {
			mHttpRequestNicoHistory = NicoroAPIManager.createGetCookieNicoHistory(
					videoNumber, mCookieUserSession,
					mForceLow, mUserAgent);
		}

		private boolean shouldRetry() {
			mHttpRequestUserSession = NicoroAPIManager.createRequestIsCookieUserSessionValid(
					mCookieUserSession, mUserAgent);
	        if (NicoroAPIManager.checkIsCookieUserSessionValid(
					mHttpRequestUserSession)) {
	        	// Cookieは有効→原因不明のエラー
	        	return false;
	        }
        	// 何らかの理由でログアウト状態→Cookie再取得を試みる
        	String ma = NicoroConfig.getMA(mSharedPreferences);
        	String pw = NicoroConfig.getPW(mSharedPreferences);
        	if (ma == null || pw == null) {
        		return false;
        	}
        	try {
				mHttpRequestAuthorizeCookie =
					NicoroAPIManager.createRequestGetAuthorizeCookie(
						ma, pw, mUserAgent);
				DefaultHttpClient httpClient =
					Util.createHttpClient();
				String cookieUserSession = NicoroAPIManager.getAuthorizeCookie(
						httpClient, mHttpRequestAuthorizeCookie);
				if (cookieUserSession == null) {
	        		return false;
				}
		        SharedPreferences.Editor editor = mSharedPreferences.edit();
		        editor.putString(NicoroConfig.COOKIE_USER_SESSION,
		        		cookieUserSession);
		        editor.commit();
		        // 念のためメンバ変数更新をUIスレッドで実行
				runOnUiThread(new Runnable() {
					@Override
					public void run() {
						updateCookieUserSession();
					}
				});
			} catch (UnsupportedEncodingException e) {
				Log.e(LOG_TAG, e.toString(), e);
        		return false;
			} catch (ClientProtocolException e) {
				Log.e(LOG_TAG, e.toString(), e);
        		return false;
			} catch (IOException e) {
				Log.e(LOG_TAG, e.toString(), e);
        		return false;
			}
			createHttpRequestGetHistory(mVideoNumber);
			return true;
		}
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		mWasDestroyed = false;

		Resources res = getResources();
		mCurrentLocale = res.getConfiguration().locale;
		cacheResourceString();

		StringBuilder builder = new StringBuilder(256);
		builder.setLength(0);
		mResJsFindRss = Util.loadRawJs(builder, res,
				R.raw.find_rss).toString();

		builder.setLength(0);
		mResJsReplaceFlvplayerContainer = Util.loadRawJs(builder, res,
				R.raw.replace_flvplayer_container).toString();

        builder.setLength(0);
		mResJsReplacePlayerbox = Util.loadRawJs(builder, res,
		        R.raw.replace_playerbox).toString();

        builder.setLength(0);
        mResJsGetStreamDescription = Util.loadRawJs(builder, res,
                R.raw.get_stream_description).toString();

		builder.setLength(0);
		mJsMarginBottom = builder.append("javascript: document.body.style.marginBottom = \"")
			.append(res.getDimensionPixelSize(R.dimen.button_video_control_height))
			.append("px\"").toString();

        builder.setLength(0);
        mResJsReplaceBlank = Util.loadRawJs(builder, res,
                R.raw.replace_blank).toString();

        getWindow().requestFeature(Window.FEATURE_PROGRESS);

        // 例外対策兼ねて手動で作る
        CookieSyncManager cookieSyncManager =
        	CookieSyncManager.createInstance(getApplicationContext());
        mCookieSyncManager = cookieSyncManager;

        CookieManager cookieManager = CookieManager.getInstance();
		mCookieManager = cookieManager;
		cookieManager.setAcceptCookie(true);
		cookieManager.removeExpiredCookie();
//		cookieManager.removeAllCookie();

        if (mSharedPreferences == null) {
        	mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        }

        mThumbInfoCacher = NicoroApplication.getInstance(this
                ).getThumbInfoCacher();

        // レイアウト崩れ対策
        setCookieNicovideo("nofix=1");
        // 実況アプリインストール済み状態に
        mCookieManager.setCookie("jk.nicovideo.jp", "jikkyo_installed=1");

    	cookieSyncManager.sync();

    	mMenuInflater = getMenuInflater();

    	initializeView(savedInstanceState);
	}

	@Override
	protected void onResume() {
		super.onResume();

		mCookieSyncManager.startSync();

		updateCookieUserSession();
		mCookieSyncManager.sync();

		// Cookie更新後にURL設定
		WebView webView = mWebView;
		if (webView != null) {
		    String urlWebView = webView.getUrl();
		    if (urlWebView == null || !urlWebView.equals(mLastUrl)) {
                webView.loadUrl(mLastUrl);
        	}
		}

		// URL読み込み済みでもWebViewのgetUrlがnullになることがある模様→WebViewでのイベント発生まで処理遅らせるべき？

		updateStatus1();
		updateStatus2("");
        if (webView != null) {
            webView.loadUrl(mResJsReplaceFlvplayerContainer);
        }
	}

	@Override
	protected void onPause() {
		super.onPause();

		mCookieSyncManager.stopSync();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
        mSharedPreferences.unregisterOnSharedPreferenceChangeListener(mSharedPreferencesChangeListener);
        if (mVideoCacheService != null) {
        	unbindService(mVideoCacheServiceConnection);
        }

//        if (mAsyncCheckCookieUserSession != null) {
//        	mAsyncCheckCookieUserSession.cancel(false);
//        	mAsyncCheckCookieUserSession.stop();
//        }
        if (mAsyncStartPlay != null) {
        	mAsyncStartPlay.cancel(false);
        	mAsyncStartPlay.stop();
	    }
        mWasDestroyed = true;
        WebView webView = mWebView;
        if (webView != null) {
            webView.destroy();
        }
        mWebView = null;
        for (WebView wv : mWebViewPool) {
            wv.destroy();
        }
        mWebViewPool.clear();
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);

		if (!newConfig.locale.equals(mCurrentLocale)) {
			cacheResourceString();
		}
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		// 1.6向けなのでonKeyDownでback key監視
		if (keyCode == KeyEvent.KEYCODE_BACK) {
		    if (goBackWebView()) {
		        return true;
		    }
		}
		return super.onKeyDown(keyCode, event);
	}

	@Override
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		return super.onKeyUp(keyCode, event);
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
        WebView webView = mWebView;
        if (webView != null) {
            webView.saveState(outState);
        }
	}

	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		super.onRestoreInstanceState(savedInstanceState);
        WebView webView = mWebView;
        if (webView != null) {
            webView.restoreState(savedInstanceState);
        }
	}

	@Override
	protected void onNewIntent(Intent intent) {
	    super.onNewIntent(intent);
        String url = getUrlFromIntentActionView(intent);
        if (url != null && !url.equals(mLastUrl)) {
            updateLastUrl(url);
            WebView webView = mWebView;
            if (webView != null) {
                webView.loadUrl(mLastUrl);
            } else {
                assert false;
            }
        }
	}

	private void initializeView(Bundle savedInstanceState) {
        setContentView(R.layout.nicoro_webbrowser);

    	mButtonVideoControl = (ViewGroup) findViewById(R.id.button_video_control);

    	mButtonAction = (Button) findViewById(R.id.button_action);
    	mButtonAction.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				startAction();
//				openContextMenu(v);
			}
    	});
    	registerForContextMenu(mButtonAction);

    	mLabelStatus1 = (VariableLabelView) findViewById(R.id.status1);
    	mLabelStatus2 = (VariableLabelView) findViewById(R.id.status2);

        mLabelTitleUrl = (VariableLabelView) findViewById(R.id.web_title_url);

        mJsObj = new JsObj();
        WebView webView = (WebView) findViewById(R.id.webview);
        mWebView = webView;
        initializeWebView(webView);

        WebBackForwardList backForwardList = null;
        if (savedInstanceState != null) {
        	backForwardList = webView.restoreState(savedInstanceState);
        }

        String url = getUrlFromIntentActionView(getIntent());
        if (url == null) {
            if (backForwardList != null) {
                url = backForwardList.getCurrentItem().getUrl();
            } else {
                url = "http://www.nicovideo.jp/";
            }
            url = mSharedPreferences.getString(NicoroConfig.LAST_URL, url);
        }
        // 過去バージョンの不具合対策
        if (!isInNicoVideoSite(url)) {
            url = "http://www.nicovideo.jp/";
        }
        updateLastUrl(url);

        mUserAgent = mSharedPreferences.getString(NicoroConfig.USER_AGENT,
        		null);
        if (mUserAgent == null) {
        	mUserAgent = webView.getSettings().getUserAgentString();
        	SharedPreferences.Editor editor = mSharedPreferences.edit();
        	editor.putString(NicoroConfig.USER_AGENT, mUserAgent);
        	editor.commit();
        }

        mSharedPreferencesChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
			@Override
			public void onSharedPreferenceChanged(
					SharedPreferences sharedPreferences, String key) {
				if (key.equals(getString(R.string.pref_key_browser_zoom_controls))) {
			        boolean zoomControl = mSharedPreferences.getBoolean(key, true);
                    WebView webView = mWebView;
                    if (webView != null) {
                        webView.getSettings().setBuiltInZoomControls(zoomControl);
                    }
				}
			}
        };
        mSharedPreferences.registerOnSharedPreferenceChangeListener(mSharedPreferencesChangeListener);

        mDecimalFormatMB = new DecimalFormat();
        mDecimalFormatMB.applyPattern("0");
        mDecimalFormatMB.setMaximumFractionDigits(2);
        mDecimalFormatMB.setMinimumFractionDigits(2);
        mDecimalFormatMB.setMinimumIntegerDigits(3);

//        webView.loadUrl(mLastUrl);
	}

    private void initializeWebView(WebView webView) {
        assert mJsObj != null;
        initializeWebSettings(webView.getSettings());
        webView.setWebChromeClient(new NicoroWebChromeClient());
        webView.setWebViewClient(new NicoroWebViewClient());
        webView.addJavascriptInterface(mJsObj, "nicoro");
    }

    private void initializeWebSettings(WebSettings webSettings) {
        webSettings.setJavaScriptEnabled(true);
        webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
        webSettings.setSavePassword(true);
        webSettings.setUseWideViewPort(true);
        // TODO 完全なマルチウインドウ対応は面倒そう
        webSettings.setSupportMultipleWindows(true);
        boolean zoomControl = mSharedPreferences.getBoolean(
                getString(R.string.pref_key_browser_zoom_controls), true);
        webSettings.setBuiltInZoomControls(zoomControl);
    }

	private void downloadVideo(DefaultHttpClient httpClient, String url, String cookie, String filePath) throws ClientProtocolException, IOException {
		HttpUriRequest httpRequest = new HttpGet(url);
		httpRequest.addHeader("Cookie", cookie);
		httpRequest.setHeader("User-Agent", mUserAgent);
		HttpResponse httpResponse = httpClient.execute(
				httpRequest
				);
		if (DEBUG_LOGD) {
			Log.d(LOG_TAG, "downloadVideo httpResponse>>>");
			Util.logHeaders(LOG_TAG, httpResponse.getAllHeaders());
			Log.d(LOG_TAG, "<<<httpResponse end");
		}
		if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
			HttpEntity httpEntity = httpResponse.getEntity();
			InputStream inDownload = null;
			FileOutputStream outDownload = null;
			try {
				inDownload = httpEntity.getContent();
				outDownload = new FileOutputStream(filePath);
				byte[] buffer = new byte[1024*8];
				while (true) {
					int read = inDownload.read(buffer);
					if (read < 0) {
						break;
					}
					outDownload.write(buffer, 0, read);
				}
			} finally {
				if (inDownload != null) {
					inDownload.close();
				}
				if (outDownload != null) {
					outDownload.close();
				}
			}
		}
		Log.d(LOG_TAG, "downloadVideo done.");
	}

//	private void updateCookieUserSession() {
//		String cookieUserSession = Util.getCookieValueFromManager(
//				"nicovideo.jp", "user_session");
//		if (cookieUserSession == null || cookieUserSession.length() == 0) {
//			// 確実にログアウト状態
//			mCookieUserSession = null;
//			mUserId = null;
//		} else if (!cookieUserSession.equals(mCookieUserSession)) {
//	        SharedPreferences.Editor editor = mSharedPreferences.edit();
//	        editor.putString(NicoroConfig.COOKIE_USER_SESSION, cookieUserSession);
//			editor.commit();
//			mCookieUserSession = cookieUserSession;
//			mMatcherGetUserID = Util.getMatcher(mMatcherGetUserID,
//					PATTERN_GET_USERID_FROM_USERSESSION,
//					mCookieUserSession);
//			mUserId = Util.getFirstMatch(mMatcherGetUserID);
//		}
//	}

    private void onChangePage(WebView view, String url) {
        // 外部サイトなら特別処理省略
        if (!isInNicoVideoSite(url)) {
            return;
        }

		// 画像他なら特別処理省略
		if (mMatcherIsNotHtml.get().reset(url).find()) {
			return;
		}

		// ページ移動に備えて基本的にWebViewのcurrent URLで確認
		String urlCurrent = view.getUrl();
		if (urlCurrent == null) {
			urlCurrent = url;
		}

		String videoNumber = getVideoNumberFromUrl(urlCurrent);
		mVideoNumber = videoNumber;
		if (videoNumber != null) {
			ThumbInfo thumbInfo = mThumbInfoCacher.getThumbInfo(videoNumber);
			if (thumbInfo == null) {
				mThumbInfoCacher.loadThumbInfo(videoNumber, null);
			}
		}
		mJikkyoNumber = getJikkyoNumberFromUrl(urlCurrent);
		updateStatus1();

//		if (DEBUG_LOG) {
//    		CookieManager cookieManager = CookieManager.getInstance();
//    		String cookie = cookieManager.getCookie(urlCurrent);
//    		if (cookie != null) {
//    			Log.d(LOG_TAG, "URL=" + url
//    					+ " Cookie=" + cookie);
//    		}
//		}

		if (mMatcherIsNicovideo.get().reset(urlCurrent).find()) {
		    updateLastUrl(urlCurrent);
		}
	}

	private void updateStatus1() {
		StringBuilder builderStatus1 = mLabelStatus1.getTextBuilder();
		builderStatus1.setLength(0);

		if (mCookieUserSession == null) {
			builderStatus1.append(mResStringStatus1NotLogin);
		} else {
		    String videoNumber = mVideoNumber;
			if (videoNumber != null) {
				updateStatus1CacheState(builderStatus1, videoNumber);
			} else if (mJikkyoNumber != null) {
				builderStatus1.append("ch: ").append(mJikkyoNumber);
			}
		}

		mLabelStatus1.notifyUpdateText();
	}

	private void updateStatus1CacheState(StringBuilder builderStatus1,
	        String videoNumber) {
		builderStatus1.append(videoNumber).append(": ");
		boolean isFinishedCache =
			VideoLoader.isFinishedCache(videoNumber);
		boolean isSwf = videoNumber.startsWith("nm");
		boolean isFinishedCacheLow;
		if (isSwf) {
			// swfは低画質無し
			isFinishedCacheLow = false;
		} else {
			isFinishedCacheLow =
				VideoLoader.isFinishedCacheLow(videoNumber);
		}
		if (isFinishedCache && isFinishedCacheLow) {
			builderStatus1.append(mResStringStatus1CompletedCache)
				.append(mResStringStatusHighLowShort);
			return;
		}

		int cacheState = VideoCacheService.CACHE_STATE_INVALID;
		int cacheStateLow = VideoCacheService.CACHE_STATE_INVALID;
		if (mVideoCacheService != null) {
			try {
				if (!isFinishedCache) {
					cacheState =
						mVideoCacheService.getCacheState(
								videoNumber, false);
				}
				if (!isSwf && !isFinishedCacheLow) {
					cacheStateLow =
						mVideoCacheService.getCacheState(
								videoNumber, true);
				}
			} catch (RemoteException e) {
				Log.e(LOG_TAG, e.toString(), e);
			}
		}

		if (cacheState == VideoCacheService.CACHE_STATE_RUNNING) {
			builderStatus1.append(mResStringStatus1RunningCache);
			return;
		}
		if (cacheStateLow == VideoCacheService.CACHE_STATE_RUNNING) {
			builderStatus1.append(mResStringStatus1RunningCache)
				.append(mResStringStatusLowShort);
			return;
		}
		if (cacheState == VideoCacheService.CACHE_STATE_WAIT_START) {
			builderStatus1.append(mResStringStatus1WaitingCache);
			return;
		}
		if (cacheStateLow == VideoCacheService.CACHE_STATE_WAIT_START) {
			builderStatus1.append(mResStringStatus1WaitingCache)
				.append(mResStringStatusLowShort);
			return;
		}

		if (isSwf) {
			if (isFinishedCache) {
				builderStatus1.append(mResStringStatus1CompletedCache);
				return;
			}
		} else {
			// TODO: 低画質がそもそも存在しない動画のチェックがない
			if (isFinishedCache) {
				builderStatus1.append(mResStringStatus1CompletedCache)
					.append(mResStringStatusHighShort);
				return;
			}
			if (isFinishedCacheLow) {
				builderStatus1.append(mResStringStatus1CompletedCache)
					.append(mResStringStatusLowShort);
				return;
			}
		}

		builderStatus1.append(mResStringStatus1NoneCache);
		return;
	}

	private void updateStatus2(String message) {
		StringBuilder builderStatus2 = mLabelStatus2.getTextBuilder();
		builderStatus2.setLength(0);

		builderStatus2.append(message);

		mLabelStatus2.notifyUpdateText();
	}

	StringBuffer getRunCachingProgressMessage(StringBuffer buffer,
			int seekOffsetWrite, int contentLength) {
		if (seekOffsetWrite >= 0 && contentLength >= 0) {
			float seekOffsetWriteMB = seekOffsetWrite / (float) (1024 * 1024);
			float contentLengthMB = contentLength / (float) (1024 * 1024);
			FieldPosition fp = new FieldPosition(NumberFormat.INTEGER_FIELD);
			mDecimalFormatMB.format(seekOffsetWriteMB, buffer, fp);
			buffer.append("/");
			mDecimalFormatMB.format(contentLengthMB, buffer, fp);
			return buffer.append("MB");
		} else {
			return buffer.append("---.--/---.--MB");
		}
	}

	private void cacheResourceString() {
		Resources r = getResources();
		mResStringStatusLowShort = r.getString(R.string.status_low_short);
		mResStringStatusHighShort = r.getString(R.string.status_high_short);
		mResStringStatusHighLowShort = r.getString(R.string.status_high_low_short);
		mResStringStatus1NotLogin = r.getString(R.string.status1_not_login);
		mResStringStatus1CompletedCache = r.getString(R.string.status1_completed_cache);
		mResStringStatus1RunningCache = r.getString(R.string.status1_running_cache);
		mResStringStatus1WaitingCache = r.getString(R.string.status1_waiting_cache);
		mResStringStatus1NoneCache = r.getString(R.string.status1_none_cache);
		mResStringStatus2RunningCache = r.getString(R.string.status2_running_cache);
		mResStringStatus2WaitingCache = r.getString(R.string.status2_waiting_cache);
		mResStringStatus2StartingCache = r.getString(R.string.status2_starting_cache);
		mResStringProgressStartPlay = r.getString(R.string.progress_start_play);
	}

	private void startPlay(boolean forceLow) {
		mAsyncStartPlay = new AsyncStartPlay();
		mAsyncStartPlay.executeWrapper(forceLow);

		if (mProgressDialogStartPlay == null) {
			mProgressDialogStartPlay = Util.createProgressDialogLoading(
					this, mResStringProgressStartPlay,
					new DialogInterface.OnCancelListener() {
						@Override
						public void onCancel(DialogInterface dialog) {
					        if (mAsyncStartPlay != null) {
					        	mAsyncStartPlay.cancel(false);
					        	mAsyncStartPlay.stop();
						    }
						}
					});
		}
		mProgressDialogStartPlay.show();
	}
//	private void startPlay(boolean forceLow) {
//		try {
////			String url = mWebView.getOriginalUrl();
//			String url = mWebView.getUrl();
//			if (url == null) {
//				if (DEBUG_LOGD) {
//					Log.d(LOG_TAG, "mClickListenerPlay: mWebView.getUrl() is null");
//				}
//				return;
//			}
//
////			updateCookieUserSession();
////			if (DEBUG_LOGD) {
////				Log.d(LOG_TAG, "mCookieUserSession: " + mCookieUserSession);
////			}
//
//    		mMatcherVideoUrl = Util.getMatcher(mMatcherVideoUrl,
//    				PATTERN_VIDEO_URL,
//    				url);
//			String videoNumber = Util.getFirstMatch(mMatcherVideoUrl);
//			if (videoNumber == null) {
//				throw new FailPreparePlayVideoException();
//			}
//
//			if (forceLow) {
//				// 視聴履歴Cookie取り直す
//				mCookieNicoHistory = NicoroAPIManager.getCookieNicoHistory(
//						Util.createHttpClient(),
//						videoNumber, mCookieUserSession,
//						forceLow, mUserAgent);
//			} else {
//				mCookieNicoHistory = Util.getCookieValueFromManager(
//						url, "nicohistory");
//			}
//			if (DEBUG_LOGD) {
//				Log.d(LOG_TAG, "mCookieNicoHistory: " + mCookieNicoHistory);
//			}
//
//			if (mCookieUserSession == null
//					|| mUserId == null
//					|| mCookieNicoHistory == null) {
//				throw new FailPreparePlayVideoException();
//			}
//
////			mLastVideoNumber = videoNumber;
//			NicoroAPIManager.VideoPlayerIntentCreator intentCreator =
//				new NicoroAPIManager.VideoPlayerIntentCreator();
//			Intent intent = intentCreator.create(
//					getApplicationContext(),
//					videoNumber, mCookieUserSession, mCookieNicoHistory,
//					mUserId, mUserAgent, forceLow);
////			Intent intent = NicoroAPIManager.createVideoPlayerIntent(
////					getApplicationContext(),
////					videoNumber, mCookieUserSession, mCookieNicoHistory,
////					mUserId, mUserAgent, forceLow);
//			if (intent == null) {
//				throw new FailPreparePlayVideoException();
//			}
//			// キャッシュ処理が動作していれば停止
//			if (mVideoCacheService != null) {
//				try {
//					mVideoCacheService.stopCache(videoNumber);
//				} catch (RemoteException e) {
//					Log.e(LOG_TAG, e.getMessage(), e);
//				}
//			}
//			startActivityIfNeeded(intent, 0);
//		} catch (FailPreparePlayVideoException e) {
//			if (DEBUG_LOGD) {
//				Log.d(LOG_TAG, Log.buf().append("startPlay failed: mCookieUserSession=")
//						.append(mCookieUserSession)
//						.toString());
//			}
//	        if (!NicoroAPIManager.checkIsCookieUserSessionValid(mCookieUserSession, mUserAgent)) {
//	        	// TODO: メールアドレス等保存するようにしたから、Cookieの自動再取得を試みるべき
//
//	        	// 何らかの理由でログアウト状態
//	        	Util.showErrorToast(getApplicationContext(), R.string.toast_play_fail_not_login);
//	        	// ログイン画面へ
//	        	loadLoginForm();
//	        } else {
//	        	String extraMessage = e.getExtraMessage();
//	        	if (extraMessage == null) {
//		        	// 原因不明で再生失敗
//	        		extraMessage = getString(R.string.errormessage_play_fail_unknown);
//	        	}
//				Util.showErrorDialog(NicoroWebBrowser.this,
//						extraMessage,
//						false);
//	        }
//		} catch (IOException e) {
//			Log.d(LOG_TAG, e.getMessage(), e);
//			Util.showErrorDialog(NicoroWebBrowser.this,
//					e.getMessage(),
//					false);
//		}
//	}

	private void loadLoginForm() {
    	// ログイン画面へ
    	// TODO: ログイン後に元の動画のURLに移動しない
	    // TODO: というかこのメソッドもう使わないのでは？
        WebView webView = mWebView;
        if (webView != null) {
            webView.loadUrl("https://secure.nicovideo.jp/secure/login_form");
        }
	}

	private void startCache(boolean forceLow) {
//		updateCookieUserSession();
//		if (DEBUG_LOGD) {
//			Log.d(LOG_TAG, "mCookieUserSession: " + mCookieUserSession);
//		}

		String videoNumber = mVideoNumber;
		if (mVideoCacheService == null) {
//			Intent intent = new Intent(getApplicationContext(), VideoCacheService.class);
			Intent intent = new Intent(IVideoCacheService.class.getName());
			intent.putExtra(AbstractNicoroPlayer.INTENT_NAME_VIDEO_NUMBER,
					videoNumber);
			intent.putExtra(VideoCacheService.INTENT_NAME_COOKIE_USER_SESSION,
					mCookieUserSession);
			intent.putExtra(AbstractNicoroPlayer.INTENT_NAME_FORCE_LOW,
					forceLow);
			boolean bindResult = bindService(intent,
					mVideoCacheServiceConnection, Context.BIND_AUTO_CREATE);
//			if (DEBUG_LOGD) {
//				Log.d(LOG_TAG, "bindResult=" + bindResult);
//			}
			if (bindResult) {
				updateStatus1();
			} else {
	        	Util.showErrorToast(getApplicationContext(), R.string.toast_cache_fail_bind);
			}
		} else {
			try {
				mVideoCacheService.addStartCache(videoNumber,
						forceLow, mCookieUserSession);
				updateStatus1();
			} catch (RemoteException e) {
				Log.e(LOG_TAG, e.toString(), e);
	        	Util.showErrorToast(getApplicationContext(), R.string.toast_cache_fail_bind);
			}
		}
	}

	private void startJikkyo(String number) {
		// TODO
		try {
			Intent intent = NicoroAPIManager.createJikkyoPlayerIntent(
					getApplicationContext(),
					number, mCookieUserSession, mCookieNicoHistory,
					mUserId, mUserAgent);
			startActivityIfNeeded(intent, 0);
            WebView webView = mWebView;
            if (webView != null) {
                webView.stopLoading();
            }
		} catch (FailPreparePlayVideoException e) {
		}
	}

    private void startLive(String number) {
        // TODO
        WebView webView = mWebView;
        String title = null;
        if (webView != null) {
            title = webView.getTitle();
            Matcher matcher = Pattern.compile(PATTERN_GET_TITLE).matcher(title);
            if (matcher.find()) {
                title = matcher.group(1);
            }
        }
        String description = mJsObj.streamDescription;
        try {
            Intent intent = NicoroAPIManager.createLivePlayerIntent(
                    getApplicationContext(),
                    number, mCookieUserSession,
                    title, description,
                    mCookieNicoHistory,
                    mUserId, mUserAgent);
            startActivityIfNeeded(intent, 0);
            if (webView != null) {
                webView.stopLoading();
            }
        } catch (FailPreparePlayVideoException e) {
        }
    }

	private String getVideoNumberFromUrl(String url) {
	    Matcher matcher = mMatcherVideoUrl.get().reset(url);
		if (matcher.find()) {
			return matcher.group(1);
		} else {
			return null;
		}
	}

	private String getJikkyoNumberFromUrl(String url) {
	    Matcher matcher = mMatcherJikkyoUrl.get().reset(url);
		if (matcher.find()) {
			return matcher.group(1);
		} else {
			return null;
		}
	}

    private String getLiveNumberFromUrl(String url) {
        Matcher matcher = mMatcherLiveUrl.get().reset(url);
        if (matcher.find()) {
            return matcher.group(1);
        } else {
            return null;
        }
    }

	private void showBookmark() {
		Intent intent = new Intent(getApplicationContext(), Bookmarks.class);
		WebView webView = mWebView;
		if (webView != null) {
			intent.putExtra(Bookmarks.INTENT_NAME_TITLE, webView.getTitle());
			intent.putExtra(Bookmarks.INTENT_NAME_URL, webView.getUrl());
		} else {
			Util.showErrorToast(getApplicationContext(), R.string.toast_webview_not_ready);
		}
		startActivityIfNeeded(intent, 0);
	}

//	private void reflectIsCookieUserSessionValid(boolean result) {
//        if (result) {
//        	// 前回保存時のuser session Cookieを有効化
//			mMatcherGetUserID = Util.getMatcher(mMatcherGetUserID,
//					PATTERN_GET_USERID_FROM_USERSESSION,
//					mCookieUserSession);
//			mUserId = Util.getFirstMatch(mMatcherGetUserID);
////        	mCookieManager.setCookie("nicovideo.jp", mCookieUserSession);
//        } else {
//        	mCookieUserSession = null;
//        	mUserId = null;
//        	// TODO: CookieManagerでは特定のCookieを削除することはできない
//        	// →WebViewDatabaseの実装を参考にdbファイル直接編集するしかない？
////    		cookieManager.setCookie("nicovideo.jp", "user_session=");
//        }
//	}

	private void updateCookieUserSession() {
        mCookieUserSession = mSharedPreferences.getString(
        		NicoroConfig.COOKIE_USER_SESSION, null);
		if (DEBUG_LOGD) {
			Log.d(LOG_TAG, Log.buf().append("updateCookieUserSession: mCookieUserSession=")
					.append(mCookieUserSession)
					.toString());
		}
		setCookieNicovideo(mCookieUserSession);
    	if (mCookieUserSession != null) {
			mUserId = Util.getFirstMatch(
			        mMatcherGetUserID.get().reset(mCookieUserSession));
    	}
	}

	private String getUrlFromIntentActionView(Intent intent) {
        if (intent == null) {
        	return null;
        }
    	if (Intent.ACTION_VIEW.equals(intent.getAction())) {
        	Uri uri = intent.getData();
        	if (uri != null) {
        		return uri.toString();
        	}
    	}
    	return null;
	}

	private void setCookieNicovideo(String value) {
    	mCookieManager.setCookie("nicovideo.jp", value);
    	// 公式短縮URLにも設定しないとCookieがうまく引き継がれない模様
    	mCookieManager.setCookie("nico.ms", value);
	}

	private boolean goBackWebView() {
        WebView webView = mWebView;
        if (webView == null) {
            return false;
        }
        if (webView.canGoBack()) {
            webView.goBack();
            return true;
        }
        WebView lastWebView;
        if (mWebViewPool.isEmpty()) {
            return false;
        }
        lastWebView = mWebViewPool.removeLast();
        Util.replaceView(webView, lastWebView);
        mWebView = lastWebView;
        updateLastUrl(lastWebView.getUrl());
        webView.destroy();
        return true;
	}

    private void updateLastUrl(String url) {
        if (!TextUtils.equals(mLastUrl, url)) {
            SharedPreferences.Editor editor = mSharedPreferences.edit();
            editor.putString(NicoroConfig.LAST_URL, url);
            editor.commit();
            mLastUrl = url;
        }
    }

    private boolean isInNicoVideoSite(String url) {
        try {
            URL urlParse = new URL(url);
            String host = urlParse.getHost();
            String path = urlParse.getPath();
            if (host.equals("nico.ms")) {
                return true;
//            } else if (host.indexOf("nicoseiga.jp") >= 0) {
//                return true;
            } else if (host.indexOf("nicovideo.jp") < 0
                    || host.equals("rd.nicovideo.jp")
                    || host.equals("ads.nicovideo.jp")) {
                if (DEBUG_LOGV) {
                    Log.v(LOG_TAG, Log.buf().append("Outside url=")
                            .append(url).toString());
                }
                return false;
            } else if (host.equals("seiga.nicovideo.jp")
                    && path.startsWith("/image/source")) {
                if (DEBUG_LOGV) {
                    Log.v(LOG_TAG, Log.buf().append("Outside url=")
                            .append(url).toString());
                }
                return false;
            } else {
                return true;
            }
        } catch (MalformedURLException e) {
            Log.e(LOG_TAG, e.toString(), e);
            // TODO とりあえずtrue扱い
            return true;
        }
    }

	private final Runnable mActionPlay = new Runnable() {
		@Override
		public void run() {
			startPlay(false);
		}
	};
	private final Runnable mActionPlayLow = new Runnable() {
		@Override
		public void run() {
			startPlay(true);
		}
	};
	private final Runnable mActionCache = new Runnable() {
		@Override
		public void run() {
			startCache(false);
		}
	};
	private final Runnable mActionCacheLow = new Runnable() {
		@Override
		public void run() {
			startCache(true);
		}
	};
	private final Runnable mActionJikkyo = new Runnable() {
		@Override
		public void run() {
			startJikkyo(mJikkyoNumber);
		}
	};
    private final Runnable mActionLive = new Runnable() {
        @Override
        public void run() {
            startLive(mLiveNumber);
        }
    };
	private final Runnable mActionLogin = new Runnable() {
		@Override
		public void run() {
			loadLoginForm();
		}
	};
	private final Runnable mActionAddBookmark = new Runnable() {
		@Override
		public void run() {
		    String title = null;
		    String url = null;
		    WebView webView = mWebView;
		    if (webView != null) {
		        title = webView.getTitle();
		        url = webView.getUrl();
		    }
			if (title == null) {
				title = "";
			}
			if (url == null) {
				url = "";
			}
			Bookmarks.addBookmark(NicoroWebBrowser.this, title, url);
		}
	};
	private final Runnable mActionHelp = new Runnable() {
		@Override
		public void run() {
			startActivityIfNeeded(
					new Intent(getApplicationContext(), NicoroHelp.class),
					0);
		}
	};
	private final Runnable mActionShowBookmark = new Runnable() {
		@Override
		public void run() {
			showBookmark();
		}
	};
	private final Runnable mActionRelatedVideo = new Runnable() {
		@Override
		public void run() {
			String videoNumber = mVideoNumber;
			Intent intent = new Intent(getApplicationContext(),
					RelatedVideoActivity.class);
			intent.putExtra(AbstractNicoroPlayer.INTENT_NAME_VIDEO_NUMBER,
					videoNumber);
			startActivity(intent);
		}
	};
	private final Runnable mActionViewRss = new Runnable() {
		@Override
		public void run() {
			URL rss = getRssUrl();
			if (rss != null) {
				try {
					Intent intent = new Intent(Intent.ACTION_VIEW);
					intent.setDataAndType(Uri.parse(rss.toExternalForm()),
							"application/rss+xml");
					startActivity(intent);
					return;
				} catch (ActivityNotFoundException e) {
					Log.e(LOG_TAG, e.toString(), e);
				}
			}
			Util.showErrorToast(getApplicationContext(),
					R.string.toast_rss_fail);
		}
	};
	private final Runnable mActionShareRss = new Runnable() {
		@Override
		public void run() {
			URL rss = getRssUrl();
			if (rss != null) {
				try {
					Intent intent = new Intent(Intent.ACTION_SEND);
					intent.putExtra(Intent.EXTRA_TEXT, rss.toExternalForm());
					intent.setType("text/plain");
					intent = Intent.createChooser(intent, null);
					startActivity(intent);
					return;
				} catch (ActivityNotFoundException e) {
					Log.e(LOG_TAG, e.toString(), e);
				}
			}
			Util.showErrorToast(getApplicationContext(),
					R.string.toast_rss_fail);
		}
	};
	private URL getRssUrl() {
	    WebView webView = mWebView;
	    if (webView == null) {
            Log.e(LOG_TAG, "getRssUrl: webView is null");
	        return null;
	    }
		String url = webView.getUrl();
		if (url == null) {
			Log.e(LOG_TAG, "getRssUrl: mWebView.getUrl() return null");
			return null;
		}
		String urlRss = mJsObj.urlRss;
		if (urlRss == null) {
            Log.e(LOG_TAG, "getRssUrl: mJsObj.urlRss is null");
	        assert false;
		    return null;
		}
		try {
			URL forParse = new URL(url);
			return new URL(forParse.getProtocol(),
					forParse.getHost(), urlRss);
		} catch (MalformedURLException e) {
			Log.e(LOG_TAG, e.toString(), e);
			return null;
		}
	}

	private final Runnable mActionDeleteCache = new Runnable() {
		@Override
		public void run() {
    		new AlertDialog.Builder(NicoroWebBrowser.this)
	    		.setMessage(R.string.confirm_delete_cache_one)
	    		.setCancelable(true)
	    		.setPositiveButton(android.R.string.yes,
	    				new DialogInterface.OnClickListener() {
	    			@Override
	    			public void onClick(DialogInterface dialog, int which) {
	    				String videoNumber = mVideoNumber;
	    				if (videoNumber == null) {
	    				    assert false;
                            Util.showErrorToast(getApplicationContext(),
                                    R.string.toast_delete_cache_one_fail);
	    				} else {
    	    				// キャッシュ処理が動作していれば停止
    	    				if (mVideoCacheService != null) {
    	    					try {
    	    						mVideoCacheService.stopCache(videoNumber);
    	    					} catch (RemoteException e) {
    	    						Log.e(LOG_TAG, e.toString(), e);
    	    					}
    	    				}
    	    				VideoLoader.deleteCacheFile(videoNumber);
    	    				String toast = getString(R.string.toast_delete_cache_one,
    	    						videoNumber);
    	    				Util.showInfoToast(getApplicationContext(), toast);
    	    				// キャッシュ終了と同じ扱いで表示更新
    	    				mHandler.sendEmptyMessage(MSG_ID_FINISHED_CACHE);
	    				}
	    			}
	    		})
	    		.setNegativeButton(android.R.string.no, null)
	    		.show();
		}
	};

    private final Runnable mActionAccessHistory = new Runnable() {
        @Override
        public void run() {
            Intent intent = new Intent(getApplicationContext(),
                    AccessHistoryActivity.class);
            startActivity(intent);
        }
    };

	private void startAction() {
		final int listCapacity = 3;
		final ArrayList<String> items = new ArrayList<String>(listCapacity);
		final ArrayList<Runnable> runs = new ArrayList<Runnable>(listCapacity);

		mVideoNumber = null;
		mJikkyoNumber = null;

		String url = null;
		WebView webView = mWebView;
		if (webView != null) {
			url = webView.getUrl();
		}

		Resources res = getResources();
		if (mCookieUserSession == null) {
			// 非ログイン状態
			// TODO: nullでなくともログインしていない場合が
			items.add(res.getString(R.string.action_item_login));
			runs.add(mActionLogin);
		} else if (url != null) {
			String videoNumber = getVideoNumberFromUrl(url);
			if (videoNumber != null) {
				mVideoNumber = videoNumber;
				// TODO この取り方だとタイミング的にちょっと甘い
				ThumbInfo thumbInfo = mThumbInfoCacher.getThumbInfo(videoNumber);
				final boolean hasLow = ((thumbInfo == null)
						? true : thumbInfo.getSizeLow() > 0);

				// 動画
				final boolean isSwf = NicoroAPIManager.isVideoNumberSwf(videoNumber);
				if (isSwf) {
					// swf動画
					items.add(res.getString(R.string.action_item_play_swf));
					runs.add(mActionPlay);
				} else {
					// flvまたはmp4動画
					items.add(res.getString(R.string.action_item_play_normal));
					runs.add(mActionPlay);
					if (hasLow) {
						items.add(res.getString(R.string.action_item_play_normal_low));
						runs.add(mActionPlayLow);
					}
				}

				if (isCacheNeeded(videoNumber)) {
					items.add(res.getString(R.string.action_item_cache));
					runs.add(mActionCache);
				}
				if (!isSwf && hasLow) {
					if (isCacheNeededLow(videoNumber)) {
						items.add(res.getString(R.string.action_item_cache_low));
						runs.add(mActionCacheLow);
					}
				}

				items.add(res.getString(R.string.action_item_related_video));
				runs.add(mActionRelatedVideo);

				if (VideoLoader.hasAnyCacheFile(videoNumber)) {
					items.add(res.getString(R.string.action_item_delete_cache_one));
					runs.add(mActionDeleteCache);
				}
			}

			String jikkyoNumber = getJikkyoNumberFromUrl(url);
			if (jikkyoNumber != null) {
				mJikkyoNumber = jikkyoNumber;

				items.add(res.getString(R.string.action_item_jikkyo));
				runs.add(mActionJikkyo);
			}

			String liveNumber = getLiveNumberFromUrl(url);
			if (liveNumber != null) {
			    mLiveNumber = liveNumber;

                items.add(res.getString(R.string.action_item_live));
                runs.add(mActionLive);
			}
		}

		items.add(res.getString(R.string.action_item_add_bookmark));
		runs.add(mActionAddBookmark);

		items.add(res.getString(R.string.action_item_show_bookmark));
		runs.add(mActionShowBookmark);

		if (mJsObj.urlRss != null) {
			items.add(res.getString(R.string.action_item_view_rss));
			runs.add(mActionViewRss);
			items.add(res.getString(R.string.action_item_share_rss));
			runs.add(mActionShareRss);
		}

		items.add(res.getString(R.string.action_item_access_history));
		runs.add(mActionAccessHistory);

		items.add(res.getString(R.string.action_item_help));
		runs.add(mActionHelp);

		DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
			@Override
			public void onClick(DialogInterface dialog, int which) {
				runs.get(which).run();
			}
		};
		Util.showListDialog(this, res.getString(R.string.dialog_title_action),
				items.toArray(new String[items.size()]), listener);
	}

	private boolean isCacheNeeded(String videoNumber) {
        if (VideoLoader.isFinishedCache(videoNumber)) {
            return false;
        } else {
            if (mVideoCacheService == null) {
                // サービスが（まだ）見つからないなら、キャッシュ起動のアイテムは表示する
                return true;
            } else {
                try {
                    final int cacheState = mVideoCacheService.getCacheState(
                            videoNumber, false);
                    if (cacheState == VideoCacheService.CACHE_STATE_NOT_RUN) {
                        return true;
                    } else {
                        return false;
                    }
                } catch (RemoteException e) {
                    Log.e(LOG_TAG, e.toString(), e);
                    return true;
                }
            }
        }
	}

    private boolean isCacheNeededLow(String videoNumber) {
        if (VideoLoader.isFinishedCacheLow(videoNumber)) {
            return false;
        } else {
            if (mVideoCacheService == null) {
                // サービスが（まだ）見つからないなら、キャッシュ起動のアイテムは表示する
                return true;
            } else {
                try {
                    final int cacheState = mVideoCacheService.getCacheState(
                            videoNumber, true);
                    if (cacheState == VideoCacheService.CACHE_STATE_NOT_RUN) {
                        return true;
                    } else {
                        return false;
                    }
                } catch (RemoteException e) {
                    Log.e(LOG_TAG, e.toString(), e);
                    return true;
                }
            }
        }
    }

	// メニュー関連

//    @Override
//    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
//        assert v == mButtonAction;
//        mMenuInflater.inflate(R.menu.nicoro_webbrowser_action, menu);
//        menu.setHeaderTitle(R.string.dialog_title_action);
//
//        mVideoNumber = null;
//        mJikkyoNumber = null;
//
//        String url = null;
//        WebView webView = mWebView;
//        if (webView != null) {
//            url = webView.getUrl();
//        }
//
//        if (mCookieUserSession == null) {
//            // 非ログイン状態
//            // TODO: nullでなくともログインしていない場合が
//            menu.setGroupVisible(R.id.menu_action_group_not_login, true);
//        } else if (url != null) {
//            String videoNumber = getVideoNumberFromUrl(url);
//            if (videoNumber != null) {
////                menu.setGroupVisible(R.id.menu_action_group_video, true);
//
//                mVideoNumber = videoNumber;
//                // TODO この取り方だとタイミング的にちょっと甘い
//                ThumbInfo thumbInfo = mThumbInfoCacher.getThumbInfo(videoNumber);
//                final boolean hasLow = ((thumbInfo == null)
//                        ? true : thumbInfo.getSizeLow() > 0);
//
//                // 動画
//                final boolean isSwf = NicoroAPIManager.isVideoNumberSwf(videoNumber);
//                if (isSwf) {
//                    // swf動画
//                    menu.findItem(R.id.menu_action_play_swf).setVisible(true);
//                } else {
//                    // flvまたはmp4動画
//                    menu.findItem(R.id.menu_action_play_normal).setVisible(true);
//                    if (hasLow) {
//                        menu.findItem(R.id.menu_action_play_normal_low).setVisible(true);
//                    }
//                }
//
//                if (isCacheNeeded(videoNumber)) {
//                    menu.findItem(R.id.menu_action_cache).setVisible(true);
//                }
//                if (!isSwf && hasLow) {
//                    if (isCacheNeededLow(videoNumber)) {
//                        menu.findItem(R.id.menu_action_cache_low).setVisible(true);
//                    }
//                }
//
//                menu.findItem(R.id.menu_action_related_video).setVisible(true);
//
//                if (VideoLoader.hasAnyCacheFile(videoNumber)) {
//                    menu.findItem(R.id.menu_action_delete_cache_one).setVisible(true);
//                }
//            }
//
//            String jikkyoNumber = getJikkyoNumberFromUrl(url);
//            if (jikkyoNumber != null) {
//                menu.setGroupVisible(R.id.menu_action_group_jikkyo, true);
//
//                mJikkyoNumber = jikkyoNumber;
//            }
//        }
//
//        if (mJsObj.urlRss != null) {
//            menu.setGroupVisible(R.id.menu_action_group_rss, true);
//        }
//    }
//
//    @Override
//    public boolean onContextItemSelected(MenuItem item) {
//        switch (item.getItemId()) {
//            case R.id.menu_action_login:
//                mActionLogin.run();
//                break;
//            case R.id.menu_action_play_swf:
//                mActionPlay.run();
//                break;
//            case R.id.menu_action_play_normal:
//                mActionPlay.run();
//                break;
//            case R.id.menu_action_play_normal_low:
//                mActionPlayLow.run();
//                break;
//            case R.id.menu_action_cache:
//                mActionCache.run();
//                break;
//            case R.id.menu_action_cache_low:
//                mActionCacheLow.run();
//                break;
//            case R.id.menu_action_related_video:
//                mActionRelatedVideo.run();
//                break;
//            case R.id.menu_action_delete_cache_one:
//                mActionDeleteCache.run();
//                break;
//            case R.id.menu_action_jikkyo:
//                mActionJikkyo.run();
//                break;
//            case R.id.menu_action_add_bookmark:
//                mActionAddBookmark.run();
//                break;
//            case R.id.menu_action_show_bookmark:
//                mActionShowBookmark.run();
//                break;
//            case R.id.menu_action_view_rss:
//                mActionViewRss.run();
//                break;
//            case R.id.menu_action_share_rss:
//                mActionShareRss.run();
//                break;
//            case R.id.menu_action_access_history:
//                mActionAccessHistory.run();
//                break;
//            case R.id.menu_action_help:
//                mActionHelp.run();
//                break;
//            default:
//                return super.onContextItemSelected(item);
//        }
//        return true;
//    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        mMenuInflater.inflate(R.menu.nicoro_webbrowser_menu, menu);
    	return true;
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
    	return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
    	Intent intent;
    	WebView webView = mWebView;
    	switch (item.getItemId()) {
    	    case R.id.menu_top:
        	    if (webView != null) {
        	        webView.loadUrl("http://www.nicovideo.jp/");
        	    }
        		return true;
        	case R.id.menu_reload:
                if (webView != null) {
                    webView.reload();
                }
        		return true;
        	case R.id.menu_config:
    			intent = new Intent(getApplicationContext(), NicoroConfig.class);
    			startActivity(intent);
        		return true;
        	case R.id.menu_help:
    			intent = new Intent(getApplicationContext(), NicoroHelp.class);
    			startActivityIfNeeded(intent, 0);
        		return true;
        	case R.id.menu_exit:
        		finish();
        		return true;
        	case R.id.menu_more:
        		// none
        		return true;
        	case R.id.menu_clear_cache:
        		new AlertDialog.Builder(this)
    	    		.setTitle(R.string.menu_clear_cache)
    	    		.setMessage(R.string.confirm_clear_cache)
    	    		.setCancelable(true)
    	    		.setPositiveButton(android.R.string.yes,
    	    				new DialogInterface.OnClickListener() {
    	    			@Override
    	    			public void onClick(DialogInterface dialog, int which) {
    	    	    		VideoLoader.deleteAllCacheFile();
    	    			}
    	    		})
    	    		.setNegativeButton(android.R.string.no, null)
    	    		.show();
        		return true;
//            case R.id.menu_bookmarks:
//                showBookmark();
//                return true;
        	default:
        		assert false;
        		break;
    	}
    	return false;
    }

    private class JsObj {
        public String urlRss;
        public String streamDescription;

        @SuppressWarnings("unused")
        public void getRss(String rss) {
            urlRss = rss;
            if (DEBUG_LOGD) {
                Log.d(LOG_TAG, rss);
            }
        }

        @SuppressWarnings("unused")
        public String getFlvplayerContainerHtml() {
            StringBuilder builder = new StringBuilder();
            builder.append("<div style=\"text-align: left; border: dotted medium #000000; margin: 5px 8px; padding: 5px 7px;\"><strong>")
                .append(getString(R.string.js_flvplayer_container_menu_title))
                .append("</strong><form action=\"javascript:void(0)\"><ul>");

            String url = null;
            WebView webView = mWebView;
            if (webView != null) {
                url = webView.getUrl();
            }
            if (mCookieUserSession != null && url != null) {
                String videoNumber = getVideoNumberFromUrl(url);
                if (videoNumber != null) {
                    mVideoNumber = videoNumber;
                    // TODO この取り方だとタイミング的にちょっと甘い
                    ThumbInfo thumbInfo = mThumbInfoCacher.getThumbInfo(videoNumber);
                    final boolean hasLow = ((thumbInfo == null)
                            ? true : thumbInfo.getSizeLow() > 0);

                    Resources res = getResources();
                    // 動画
                    final boolean isSwf = NicoroAPIManager.isVideoNumberSwf(videoNumber);
                    if (isSwf) {
                        // swf動画
                        builder.append("<li><button onclick=\"nicoro.startPlay(); return false;\">")
                            .append(res.getString(R.string.action_item_play_swf))
                            .append("</button>");
                    } else {
                        // flvまたはmp4動画
                        builder.append("<li><button onclick=\"nicoro.startPlay(); return false;\">")
                            .append(res.getString(R.string.action_item_play_normal))
                            .append("</button>");
                        if (hasLow) {
                            builder.append("<li><button onclick=\"nicoro.startPlayLow(); return false;\">")
                                .append(res.getString(R.string.action_item_play_normal_low))
                                .append("</button>");
                        }
                    }

                    if (isCacheNeeded(videoNumber)) {
                        builder.append("<li><button onclick=\"nicoro.startCache(); return false;\">")
                            .append(res.getString(R.string.action_item_cache))
                            .append("</button>");
                    }
                    if (!isSwf && hasLow) {
                        if (isCacheNeededLow(videoNumber)) {
                            builder.append("<li><button onclick=\"nicoro.startCacheLow(); return false;\">")
                                .append(res.getString(R.string.action_item_cache_low))
                                .append("</button>");
                        }
                    }
                }
                String liveNumber = getLiveNumberFromUrl(url);
                if (liveNumber != null) {
                    mLiveNumber = liveNumber;
                    Resources res = getResources();
                    builder.append("<li><button onclick=\"nicoro.startLive(); return false;\">")
                        .append(res.getString(R.string.action_item_live))
                        .append("</button>");
                }
            }

            builder.append("</ul></form></div>");
            return builder.toString();
        }

        @SuppressWarnings("unused")
        public void startPlay() {
            mHandler.obtainMessage(MSG_ID_ACTION_START_PLAY, 0, 0)
                .sendToTarget();
        }
        @SuppressWarnings("unused")
        public void startPlayLow() {
            mHandler.obtainMessage(MSG_ID_ACTION_START_PLAY, 1, 0)
                .sendToTarget();
        }
        @SuppressWarnings("unused")
        public void startCache() {
            mHandler.obtainMessage(MSG_ID_ACTION_START_CACHE, 0, 0)
                .sendToTarget();
        }
        @SuppressWarnings("unused")
        public void startCacheLow() {
            mHandler.obtainMessage(MSG_ID_ACTION_START_CACHE, 1, 0)
                .sendToTarget();
        }

        @SuppressWarnings("unused")
        public String getPlayerboxHtml() {
            StringBuilder builder = new StringBuilder();
            Resources res = getResources();
            builder.append("<div style=\"text-align: left; border: dotted medium #000000; margin: 5px 8px; padding: 5px 7px;\"><strong>")
                .append(res.getString(R.string.js_flvplayer_container_menu_title))
                .append("</strong><form action=\"javascript:void(0)\"><ul>");

            String url = null;
            WebView webView = mWebView;
            if (webView != null) {
                url = webView.getUrl();
            }
            if (mCookieUserSession != null && url != null) {
                String jikkyoNumber = getJikkyoNumberFromUrl(url);
                if (jikkyoNumber != null) {
                    mJikkyoNumber = jikkyoNumber;
                    builder.append("<li><button onclick=\"nicoro.startJikkyo(); return false;\">")
                        .append(res.getString(R.string.action_item_jikkyo))
                        .append("</button>");
                }
            }

            builder.append("</ul></form></div>");
            return builder.toString();
        }

        @SuppressWarnings("unused")
        public void startJikkyo() {
            mHandler.sendEmptyMessage(MSG_ID_ACTION_START_JIKKYO);
        }

        @SuppressWarnings("unused")
        public void startLive() {
            mHandler.sendEmptyMessage(MSG_ID_ACTION_START_LIVE);
        }

        @SuppressWarnings("unused")
        public void goBack() {
            mHandler.sendEmptyMessage(MSG_ID_GO_BACK_WEBVIEW);
        }

        @SuppressWarnings("unused")
        public void getStreamDescription(String strDes) {
            if (DEBUG_LOGD) {
                Log.d(LOG_TAG, Log.buf().append("stream_description=")
                        .append(strDes).toString());
            }
            streamDescription = strDes;
        }

        @SuppressWarnings("unused")
        public void log(String text) {
            if (DEBUG_LOGD) {
                Log.d(LOG_TAG, text);
            }
        }
    }
}
