package jp.sourceforge.nicoro;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jp.gr.java_conf.shiseissi.commonlib.FileUtil;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicLineFormatter;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.json.JSONObject;

import android.content.Context;
import android.content.Intent;
//import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.SystemClock;
//import android.preference.PreferenceManager;

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

public class NicoroAPIManager {
	private static final boolean DEBUG_LOGV = Release.IS_DEBUG & true;
	private static final boolean DEBUG_LOGD = Release.IS_DEBUG & true;

	public static final String PATTERN_NICOVIDEO_URL =
		"^https?://.+\\.nicovideo\\.jp/";
    public static final String PATTERN_NICOSEIGA_URL =
        "^https?://.+\\.nicoseiga\\.jp/";

    private static final String PATTERN_USER_SESSION =
        "(user_session=.+)[; ]?";

    static final String HOST_API = "flapi.nicovideo.jp";
    static final String HOST_SECURE = "secure.nicovideo.jp";
    static final String HOST_TOP = "www.nicovideo.jp";
    static final String HOST_SP = "sp.nicovideo.jp";

    /**
     * オリジナル画質（高画質）
     */
    public static final int ECO_TYPE_HIGH = 0;
    /**
     * 中画質
     */
    public static final int ECO_TYPE_MID = 1;
    /**
     * 低画質
     */
    public static final int ECO_TYPE_LOW = 2;

    public static final int ECO_TYPE_INVALID = -1;

	public static abstract class ParseGetAPIBase {
		protected HashMap<String, String> mData = new HashMap<String, String>();
		private HttpUriRequest mHttpRequest;

		protected abstract String createUri(String videoNumber);

		protected void initialize(DefaultHttpClient httpClient,
				String hostName, String number,
				String cookie, String userAgent) throws ClientProtocolException, IOException {
//			String infoBody = getGetflvResult(httpClient, hostName,
//					createUri(number), cookie, userAgent);
			mHttpRequest = Util.createRequestGetSingleLineDataFromAPI(
					createUri(number), cookie, userAgent);
			String infoBody = Util.getSingleLineDataFromAPIWithoutDecode(httpClient,
					hostName, mHttpRequest);
            if (infoBody == null) {
                Log.e(LOG_TAG, Log.buf().append("API failed: hostName=")
                        .append(hostName).append(" number=").append(number)
                        .toString());
            } else {
                initialize(infoBody);
            }
		}

		protected void initialize(String infoBody) {
		    createHashMap(mData, infoBody);
		}

		protected static HashMap<String, String> createHashMap(
		        HashMap<String, String> map, String data) {
		    final int dataLength = data.length();
            int start = 0;
            while (start < dataLength) {
                int indexKeyEnd = data.indexOf('=', start);
                if (indexKeyEnd < 0) {
                    break;
                }
                int indexValueBegin = indexKeyEnd + 1;
                int indexValueEnd = data.indexOf('&', indexValueBegin);
                if (indexValueEnd < 0) {
                    indexValueEnd = dataLength;
                }

                String key = data.substring(
                        start, indexKeyEnd);
                try {
                    key = URLDecoder.decode(key, HTTP.UTF_8);
                } catch (IllegalArgumentException e) {
                    Log.w(LOG_TAG, Log.buf().append("Failed to decode urlencoded string: ")
                            .append(key).toString(), e);
                } catch (UnsupportedEncodingException e) {
                    Log.e(LOG_TAG, e.toString(), e);
                }
                String value = data.substring(
                        indexValueBegin, indexValueEnd);
                try {
                    value = URLDecoder.decode(value, HTTP.UTF_8);
                } catch (IllegalArgumentException e) {
                    Log.w(LOG_TAG, Log.buf().append("Failed to decode urlencoded string: ")
                            .append(value).toString(), e);
                } catch (UnsupportedEncodingException e) {
                    Log.e(LOG_TAG, e.toString(), e);
                }
                if (DEBUG_LOGV) {
                    Log.vLong(LOG_TAG, Log.buf().append("get: key=").append(key)
                            .append(" value=").append(value)
                            .toString());
                }
                map.put(key, value);
                start = indexValueEnd + 1;
            }
            return map;
		}

//		private static String getGetflvResult(DefaultHttpClient httpClient,
//				String hostName, String uri, String cookie, String userAgent)
//		throws ClientProtocolException, IOException {
//			String result = Util.getSingleLineDataFromAPI(httpClient,
//					hostName, uri, cookie, userAgent);
//			return result;
//		}

		public String get(String key) {
			return mData.get(key);
		}

		public void abort() {
		    Util.abortHttpUriRequest(mHttpRequest);
		}
	}

	/**
	 * 動画URL等の取得
	 */
	public static class ParseGetFLV extends ParseGetAPIBase {
        public static final String HOSTNAME_VIDEO = HOST_API;

        private HashMap<String, String> mNgUp;

		@Override
		protected String createUri(String videoNumber) {
		    // XXX 標準画質も &device=android 付けるべき？
			return "/api/getflv?v=" + videoNumber;
		}

		public void initialize(DefaultHttpClient httpClient,
				String videoNumber,
				String cookie, String userAgent) throws ClientProtocolException, IOException {
			super.initialize(httpClient, HOSTNAME_VIDEO, videoNumber, cookie, userAgent);
		}

		public String getUrl() {
			String url = mData.get("url");
			if (url == null) {
				return null;
			}
			if (isGetflvUrlSwf(url)) {
				// swfはURL調整
				url += "as3";
			}
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, url);
			}

			return url;
		}
		public String getMs() {
			return mData.get("ms");
		}
		public String getThreadId() {
			return mData.get("thread_id");
		}
        public String getNicosId() {
            return mData.get("nicos_id");
        }

        public HashMap<String, String> getNgUp() {
            if (mNgUp == null) {
                String data = mData.get("ng_up");
                if (data != null) {
                    mNgUp = new HashMap<String, String>();
                    createHashMap(mNgUp, data);

                    if (DEBUG_LOGV) {
                        Log.vLong(LOG_TAG, Log.buf().append("ng_up=")
                                .append(mNgUp.toString()).toString());
                    }
                }
            }
            return mNgUp;
        }
	}

    /**
     * 低画質動画のURL等の取得
     */
	public static class ParseGetFLVLow extends ParseGetFLV {
		@Override
		protected String createUri(String videoNumber) {
            // XXX 低画質も &device=android 付けるべき？
			return super.createUri(videoNumber) + "&eco=1";
		}
	}

    /**
     * 中画質動画のURL等の取得
     */
    public static class ParseGetFLVMid extends ParseGetFLV {
        @Override
        protected String createUri(String videoNumber) {
            return super.createUri(videoNumber) + "&eco=2&device=android";
        }
    }

    /**
     * 実況のURL等の取得
     */
	public static class ParseGetFLVJikkyo extends ParseGetAPIBase {
		public static final String HOSTNAME_JIKKYO = "jk.nicovideo.jp";

		@Override
		protected String createUri(String videoNumber) {
			return "/api/getflv?v=" + videoNumber;
		}

		public void initialize(DefaultHttpClient httpClient,
				String videoNumber,
				String cookie, String userAgent) throws ClientProtocolException, IOException {
			super.initialize(httpClient, HOSTNAME_JIKKYO, videoNumber, cookie, userAgent);
		}
	}

	/**
	 * コメントサーバーにアクセスするthreadkeyの取得
	 */
	public static class ParseGetThreadKey extends ParseGetAPIBase {
		public static final String HOSTNAME = HOST_API;

		@Override
		protected String createUri(String threadId) {
			return "/api/getthreadkey?thread=" + threadId;
		}

		public void initialize(DefaultHttpClient httpClient, String threadId,
				String cookie, String userAgent) throws ClientProtocolException, IOException {
			super.initialize(httpClient, HOSTNAME, threadId, cookie, userAgent);
		}

		public String getThreadKey() {
			return mData.get("threadkey");
		}
		public String getForce184() {
			return mData.get("force_184");
		}
	}

	/**
	 * コメント書き込みに使用するpostkeyの取得
	 */
	public static class ParseGetPostKey {
	    private static final String HOSTNAME = HOST_API;

        private HashMap<String, String> mData = new HashMap<String, String>();
        private HttpUriRequest mHttpRequest;

        private String createUri(int blockNumber, String threadId) {
            return new StringBuilder("/api/getpostkey/?yugi=&block_no=")
            .append(blockNumber).append("&thread=").append(threadId).toString();
        }

        public void initialize(DefaultHttpClient httpClient,
                int blockNumber, String threadId,
                String cookie, String userAgent) throws ClientProtocolException, IOException {
            initialize(httpClient, HOSTNAME, blockNumber, threadId,
                    cookie, userAgent);
        }

        private void initialize(DefaultHttpClient httpClient,
                String hostName, int blockNumber, String threadId,
                String cookie, String userAgent) throws ClientProtocolException, IOException {
            mHttpRequest = Util.createRequestGetSingleLineDataFromAPI(
                    createUri(blockNumber, threadId), cookie, userAgent);
            String infoBody = Util.getSingleLineDataFromAPIWithoutDecode(httpClient,
                    hostName, mHttpRequest);
            if (infoBody == null) {
                Log.e(LOG_TAG, Log.buf().append("API failed: hostName=")
                        .append(hostName).append(" blockNumber=")
                        .append(blockNumber).append(" threadId=")
                        .append(threadId).toString());
            } else {
                initialize(infoBody);
            }
        }

        private void initialize(String infoBody) {
            ParseGetAPIBase.createHashMap(mData, infoBody);
        }

        public String getPostKey() {
            return mData.get("postkey");
        }
        public String getYugi() {
            return mData.get("yugi");
        }

        public void abort() {
            Util.abortHttpUriRequest(mHttpRequest);
        }
	}

	/**
	 * 認証Cookie
	 */
    public static class AuthorizeCookie {
        public String cookie;
        public String cookieSp;
        public int authflag;

        @Override
        public String toString() {
            return "[ cookie: " + cookie + " cookieSp: " + cookieSp + " authflag: " + authflag + " ]";
        }
    }

    /**
     * 認証Cookieの取得
     * @param httpClient
     * @param userAgent
     * @return
     * @throws UnsupportedEncodingException
     */
	public static AuthorizeCookie getAuthorizeCookie(DefaultHttpClient httpClient,
			String userAgent) throws UnsupportedEncodingException {
		HttpPost httpRequest =
			createRequestGetAuthorizeCookie("", "", userAgent);
		return getAuthorizeCookie(httpClient, httpRequest);
	}

	/**
	 * 認証Cookieの取得
	 * @param httpClient
	 * @param httpRequest
	 * @return
	 */
	public static AuthorizeCookie getAuthorizeCookie(DefaultHttpClient httpClient,
			HttpPost httpRequest) {
        int httpStatusCode = 0;
        HttpResponse httpResponse = null;
        for (int retry = 1; retry <= 5; ++retry) {
            try {
                httpResponse = HttpManager.executeWrapper(httpClient,
                        new HttpHost(HOST_SECURE, 443, "https"), httpRequest);
                if (DEBUG_LOGD) {
                    Log.d(LOG_TAG, "getAuthorizeCookie httpResponse>");
                    Util.logHeaders(LOG_TAG, httpResponse.getAllHeaders());
                }
                StatusLine statusLine = httpResponse.getStatusLine();
                httpStatusCode = statusLine.getStatusCode();
                if (httpStatusCode != HttpStatus.SC_FORBIDDEN
                        && httpStatusCode != HttpStatus.SC_GATEWAY_TIMEOUT) {
                    break;
                }
            } catch (ClientProtocolException e) {
                if (DEBUG_LOGD) {
                    Log.d(LOG_TAG, e.toString(), e);
                }
            } catch (IOException e) {
                if (DEBUG_LOGD) {
                    Log.d(LOG_TAG, e.toString(), e);
                }
            }
            SystemClock.sleep(retry * 1000);
        }
        if (httpStatusCode != HttpStatus.SC_OK || httpResponse == null) {
            return null;
        }
		List<Cookie> cookies = httpClient.getCookieStore().getCookies();
		StringBuilder cookieAuthorize = new StringBuilder(64);
		boolean first = true;
		for (Cookie c : cookies) {
			if (first) {
				first = false;
			} else {
				cookieAuthorize.append("; ");
			}
			if (DEBUG_LOGD) {
				Log.d(LOG_TAG, Log.buf().append(c.getName()).append("=").append(c.getValue()).toString());
			}
			cookieAuthorize.append(c.getName()).append("=").append(c.getValue());
		}
		AuthorizeCookie ret = new AuthorizeCookie();
		ret.cookie = cookieAuthorize.toString();
		ret.authflag = getNiconicoAuthflag(httpResponse);
		return ret;
	}

	/**
	 * 認証Cookieの取得
	 * @param httpClient
	 * @param httpRequest
	 * @param httpRequestSp
	 * @return
	 */
    public static AuthorizeCookie getAuthorizeCookie(DefaultHttpClient httpClient,
            HttpPost httpRequest, HttpPost httpRequestSp) {
        AuthorizeCookie cookie = getAuthorizeCookie(httpClient, httpRequest);
        if (cookie == null) {
            return null;
        }
        AuthorizeCookie cookieSp = getAuthorizeCookie(httpClient, httpRequestSp);
        if (cookieSp == null) {
            return null;
        }

        AuthorizeCookie ret = new AuthorizeCookie();
        ret.cookie = cookie.cookie;
        ret.cookieSp = cookieSp.cookie;
        ret.authflag = cookie.authflag;
        return ret;
    }

    /**
     * 認証Cookie取得に使用するHTTPリクエストの作成
     * @param mail
     * @param password
     * @param userAgent
     * @return
     * @throws UnsupportedEncodingException
     */
	public static HttpPost createRequestGetAuthorizeCookie(
			String mail, String password, String userAgent
			) throws UnsupportedEncodingException {
		HttpPost httpRequest = new HttpPost("/secure/login?site=niconico");
		List<BasicNameValuePair> postParams = new ArrayList<BasicNameValuePair>();
		postParams.add(new BasicNameValuePair("next_url", ""));
		postParams.add(new BasicNameValuePair("mail", mail));
		postParams.add(new BasicNameValuePair("password", password));
		httpRequest.setEntity(new UrlEncodedFormEntity(postParams, HTTP.UTF_8));
		if (userAgent != null) {
			httpRequest.setHeader("User-Agent", userAgent);
		}
		return httpRequest;
	}
	/**
     * sp.liveの認証Cookie取得に使用するHTTPリクエストの作成
	 * @param mail
	 * @param password
	 * @param userAgent
	 * @return
	 * @throws UnsupportedEncodingException
	 */
    public static HttpPost createRequestGetAuthorizeSpCookie(
            String mail, String password, String userAgent
            ) throws UnsupportedEncodingException {
        HttpPost httpRequest = new HttpPost("/secure/login?site=niconico");
        List<BasicNameValuePair> postParams = new ArrayList<BasicNameValuePair>();
        postParams.add(new BasicNameValuePair("next_url", ""));
        postParams.add(new BasicNameValuePair("site", "nicolivesp"));
        postParams.add(new BasicNameValuePair("mail", mail));
        postParams.add(new BasicNameValuePair("password", password));
        httpRequest.setEntity(new UrlEncodedFormEntity(postParams, HTTP.UTF_8));
        if (userAgent != null) {
            httpRequest.setHeader("User-Agent", userAgent);
        }
        return httpRequest;
    }

//	public String getVideoCookie(DefaultHttpClient httpClient, String uri,
//	        String cookie, String userAgent) throws ClientProtocolException, IOException {
//		HttpRequest httpRequest = new HttpHead(uri);
//		httpRequest.addHeader("Cookie", cookie);
//		if (userAgent != null) {
//			httpRequest.setHeader("User-Agent", userAgent);
//		}
//		HttpResponse httpResponse = httpClient.execute(
//				new HttpHost(HOST_TOP, 80),
//				httpRequest
//				);
//		if (DEBUG_LOGD) {
//			Log.d(LOG_TAG, "getVideoCookie httpResponse>>>");
//            Log.d(LOG_TAG, BasicLineFormatter.formatStatusLine(httpResponse.getStatusLine(), null));
//			Util.logHeaders(LOG_TAG, httpResponse.getAllHeaders());
//			Log.d(LOG_TAG, "<<< httpResponse end");
//		}
//		StringBuilder cookieVideo = new StringBuilder(64);
//		if (httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
//			List<Cookie> cookies = httpClient.getCookieStore().getCookies();
//			boolean first = true;
//			for (Cookie c : cookies) {
//				if (first) {
//					first = false;
//				} else {
//					cookieVideo.append("; ");
//				}
//				if (DEBUG_LOGD) {
//					Log.d(LOG_TAG, Log.buf().append(c.getName())
//							.append("=").append(c.getValue()).toString());
//				}
//				cookieVideo.append(c.getName()).append("=").append(c.getValue());
//			}
//
//		}
//		if (DEBUG_LOGD) {
//			Log.d(LOG_TAG, Log.buf().append("getVideoCookie: ")
//					.append(cookieVideo).toString());
//		}
//		return cookieVideo.toString();
//	}

	/**
	 * UserSessionのCookieが有効か確認する<br>
	 * ネットワークからの情報取得が完了するまで処理が返らないので注意
	 *
	 * @param httpRequest
	 * @return
	 */
	public static boolean checkIsCookieUserSessionValid(
			HttpUriRequest httpRequest) {
		DefaultHttpClient httpClient = Util.createHttpClient();
		try {
	        httpClient.getCookieStore().clear();
			HttpResponse httpResponse = HttpManager.executeWrapper(httpClient,
			        httpRequest);
			StatusLine statusLine = httpResponse.getStatusLine();
			if (DEBUG_LOGD) {
				Log.d(LOG_TAG, BasicLineFormatter.formatStatusLine(statusLine, null));
				Util.logHeaders(LOG_TAG, httpResponse.getAllHeaders());
			}

			int httpStatusCode = statusLine.getStatusCode();
			if (httpStatusCode != HttpStatus.SC_OK) {
				return false;
			}

			return checkNiconicoAuthflag(httpResponse);
		} catch (ClientProtocolException e) {
			Log.d(LOG_TAG, e.toString(), e);
			return false;
		} catch (IOException e) {
			Log.d(LOG_TAG, e.toString(), e);
			return false;
		} catch (NumberFormatException e) {
			Log.d(LOG_TAG, e.toString(), e);
			return false;
		} finally {
		    httpClient.getConnectionManager().shutdown();
		}
	}

    /**
     * authflagの状態をサーバーから取得する<br>
     * ネットワークからの情報取得が完了するまで処理が返らないので注意
     *
     * @param httpRequest
     * @return
     */
    public static int getAuthFlag(HttpUriRequest httpRequest) {
        DefaultHttpClient httpClient = Util.createHttpClient();
        int ret = getAuthFlag(httpRequest, httpClient);
        httpClient.getConnectionManager().shutdown();
        return ret;
    }

    /**
     * authflagの状態をサーバーから取得する<br>
     * ネットワークからの情報取得が完了するまで処理が返らないので注意
     *
     * @param httpRequest
     * @param httpClient
     * @return
     */
    public static int getAuthFlag(HttpUriRequest httpRequest,
            DefaultHttpClient httpClient) {
        httpClient.getCookieStore().clear();
        int httpStatusCode = 0;
        HttpResponse httpResponse = null;
        for (int retry = 1; retry <= 5; ++retry) {
            try {
                httpResponse = HttpManager.executeWrapper(httpClient,
                        httpRequest);
                StatusLine statusLine = httpResponse.getStatusLine();
                if (DEBUG_LOGD) {
                    Log.d(LOG_TAG, BasicLineFormatter.formatStatusLine(statusLine, null));
                    Util.logHeaders(LOG_TAG, httpResponse.getAllHeaders());
                }

                httpStatusCode = statusLine.getStatusCode();
                if (httpStatusCode != HttpStatus.SC_FORBIDDEN
                        && httpStatusCode != HttpStatus.SC_GATEWAY_TIMEOUT) {
                    break;
                }
            } catch (ClientProtocolException e) {
                if (DEBUG_LOGD) {
                    Log.d(LOG_TAG, e.toString(), e);
                }
            } catch (IOException e) {
                if (DEBUG_LOGD) {
                    Log.d(LOG_TAG, e.toString(), e);
                }
            }
            SystemClock.sleep(retry * 1000);
        }
        if (httpStatusCode != HttpStatus.SC_OK || httpResponse == null) {
            return 0;
        }
        return getNiconicoAuthflag(httpResponse);
    }

	/**
     * UserSessionのCookieが有効か確認する<br>
	 * ネットワークからの情報取得が完了するまで処理が返らないので注意
	 *
	 * @param cookieUserSession
	 * @param userAgent
	 * @return
	 */
	public static boolean checkIsCookieUserSessionValid(
			String cookieUserSession, String userAgent) {
		if (cookieUserSession == null || cookieUserSession.length() == 0) {
			return false;
		}

		HttpUriRequest httpRequest = createRequestIsCookieUserSessionValid(
				cookieUserSession, userAgent);
		return checkIsCookieUserSessionValid(httpRequest);
	}

	/**
     * UserSessionのCookieが有効か確認するのに使用するHTTPリクエストの作成
	 * @param cookieUserSession
	 * @param userAgent
	 * @return
	 */
	public static HttpUriRequest createRequestIsCookieUserSessionValid(
			String cookieUserSession, String userAgent) {
		HttpUriRequest httpRequest = new HttpHead("http://www.nicovideo.jp/");
		httpRequest.addHeader("Cookie", cookieUserSession);
		if (userAgent != null) {
			httpRequest.setHeader("User-Agent", userAgent);
		}
		if (DEBUG_LOGD) {
			Util.logHeaders(LOG_TAG, httpRequest.getAllHeaders());
		}
		return httpRequest;
	}
	/**
     * sp.liveのUserSessionのCookieが有効か確認するのに使用するHTTPリクエストの作成
	 * @param cookieUserSession
	 * @param userAgent
	 * @return
	 */
    public static HttpUriRequest createRequestIsCookieUserSpSessionValid(
            String cookieUserSession, String userAgent) {
        HttpUriRequest httpRequest = new HttpHead("http://sp.live.nicovideo.jp/");
        httpRequest.addHeader("Cookie", cookieUserSession);
        if (userAgent != null) {
            httpRequest.setHeader("User-Agent", userAgent);
        }
        if (DEBUG_LOGD) {
            Util.logHeaders(LOG_TAG, httpRequest.getAllHeaders());
        }
        return httpRequest;
    }

    /**
     * authflagをHTTPレスポンスから取り出す
     * @param httpResponse
     * @return
     */
    public static int getNiconicoAuthflag(HttpResponse httpResponse) {
        Header authflag = httpResponse.getFirstHeader("x-niconico-authflag");
        if (authflag == null) {
            return 0;
        }
        return Util.parseInt(authflag.getValue(), 0);
    }

    /**
     * authflagが立っているか確認
     * @param httpResponse
     * @return
     */
	public static boolean checkNiconicoAuthflag(HttpResponse httpResponse) {
	    return getNiconicoAuthflag(httpResponse) != 0;
	}

	/**
	 * 動画再生で {@link PlayerActivity} を起動するIntentの作成
	 */
	public static class VideoPlayerParameterCreator {
		private ParseGetFLV mParseGetFLV;
		private ParseGetThreadKey mParseGetThreadKey;

		private DefaultHttpClient mHttpClient;

		public VideoPlayerParameterCreator() {
		    this(null);
		}
        public VideoPlayerParameterCreator(DefaultHttpClient httpClient) {
            mHttpClient = httpClient;
        }

		public Intent createIntent(Context context,
				WatchVideo video, String cookieUserSession,
				String cookieNicoHistory, String userId,
				String userAgent, int forceEco
				) throws FailPreparePlayVideoException,
				ClientProtocolException, IOException {
		    Bundle extras = createExtras(context, video, cookieUserSession,
                    cookieNicoHistory, userId, userAgent, forceEco);

            Class<?> activityClass = PlayerActivity.class;
//          String getflvUrl = extras.getString(PlayerConstants.INTENT_NAME_VIDEO_URL);
//          // 動画タイプチェック＆Activity選択
//          if (isGetflvUrlFlv(getflvUrl)) {
//              // flv
//              activityClass = NicoroFFmpegPlayer.class;
//          } else if (isGetflvUrlMp4(getflvUrl)) {
//              // mp4
//              SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
//              if (sharedPreferences.getBoolean(
//                      context.getString(R.string.pref_key_mp4_mediaplayer), true)) {
//                  activityClass = NicoroMediaPlayer.class;
//              } else {
//                  activityClass = NicoroFFmpegPlayer.class;
//              }
//          } else if (isGetflvUrlSwf(getflvUrl)) {
//              activityClass = NicoroSwfPlayer.class;
//          } else {
//              // 想定外
//              Log.w(LOG_TAG, Log.buf().append("Unrecognized video URL:")
//                      .append(getflvUrl).toString());
//              // とりあえずデフォルトで起動を試みる
//              activityClass = NicoroFFmpegPlayer.class;
//          }

            return new Intent(context, activityClass)
                .putExtras(extras);
		}
        public Bundle createExtras(Context context,
                WatchVideo video, String cookieUserSession,
                String cookieNicoHistory, String userId,
                String userAgent, int forceEco
                ) throws FailPreparePlayVideoException,
                ClientProtocolException, IOException {
            DefaultHttpClient httpClient;
            if (mHttpClient == null) {
                httpClient = Util.createHttpClient();
            } else {
                httpClient = mHttpClient;
            }
            final String getflvUrl;
            final String getflvMs;
            final String getflvThreadId;
            final String getflvNicosId;
            final HashMap<String, String> ngUp;
			try {
    			httpClient.getCookieStore().clear();
    			assert mParseGetFLV == null;
    			if (forceEco == ECO_TYPE_LOW) {
    				mParseGetFLV = new ParseGetFLVLow();
    			} else if (forceEco == ECO_TYPE_MID) {
                    mParseGetFLV = new ParseGetFLVMid();
    			} else {
    				mParseGetFLV = new ParseGetFLV();
    			}
    			mParseGetFLV.initialize(httpClient,
    					video.v(),
    					cookieUserSession, userAgent);
    			assert mParseGetThreadKey == null;
    			mParseGetThreadKey = new ParseGetThreadKey();
    			getflvUrl = mParseGetFLV.getUrl();
    			getflvMs = mParseGetFLV.getMs();
    			getflvThreadId = mParseGetFLV.getThreadId();
    			getflvNicosId = mParseGetFLV.getNicosId();
    			ngUp = mParseGetFLV.getNgUp();
    			mParseGetThreadKey.initialize(httpClient,
    					getflvThreadId, cookieUserSession, userAgent);
			} finally {
			    if (mHttpClient == null) {
			        httpClient.getConnectionManager().shutdown();
			    }
			}

			if (getflvUrl == null
					|| getflvMs == null
					|| getflvThreadId == null) {
				throw new FailPreparePlayVideoException(
						context.getString(R.string.errormessage_getflv_fail));
			}

			NicoroConfig.saveCookieUserSession(context, cookieUserSession);

			Bundle bundle = new Bundle(11);
			bundle.putString(PlayerConstants.INTENT_NAME_VIDEO_URL,
			        getflvUrl);
			bundle.putString(PlayerConstants.INTENT_NAME_COOKIE,
			        cookieNicoHistory);
			bundle.putParcelable(PlayerConstants.INTENT_NAME_WATCH_VIDEO,
			        video);
			bundle.putString(PlayerConstants.INTENT_NAME_MESSAGE_URL,
			        getflvMs);
			bundle.putString(PlayerConstants.INTENT_NAME_THREAD_ID,
			        getflvThreadId);
			bundle.putString(PlayerConstants.INTENT_NAME_USER_ID,
			        userId);
			bundle.putString(PlayerConstants.INTENT_NAME_THREAD_KEY,
			        mParseGetThreadKey.getThreadKey());
			bundle.putString(PlayerConstants.INTENT_NAME_FORCE_184,
			        mParseGetThreadKey.getForce184());
            bundle.putString(PlayerConstants.INTENT_NAME_NICOS_ID,
                    getflvNicosId);
            bundle.putSerializable(PlayerConstants.INTENT_NAME_NG_UP,
                    ngUp);
            bundle.putInt(PlayerConstants.INTENT_NAME_FORCE_ECO,
                    forceEco);
			return bundle;
		}

		public void abort() {
			if (mParseGetFLV != null) {
				mParseGetFLV.abort();
			}
			if (mParseGetThreadKey != null) {
				mParseGetThreadKey.abort();
			}
		}
	}

//	public static Intent createVideoPlayerIntent(Context context,
//			String videoNumber, String cookieUserSession,
//			String cookieNicoHistory, String userId,
//			String userAgent, boolean forceLow)
//	throws FailPreparePlayVideoException {
//		try {
//			DefaultHttpClient httpClient = Util.createHttpClient();
//			httpClient.getCookieStore().clear();
//			ParseGetFLV parseGetFLV;
//			if (forceLow) {
//				parseGetFLV = new ParseGetFLVLow();
//			} else {
//				parseGetFLV = new ParseGetFLV();
//			}
//			parseGetFLV.initialize(httpClient,
//					videoNumber,
//					cookieUserSession, userAgent);
//			ParseGetThreadKey parseGetThreadKey = new ParseGetThreadKey();
//			final String getflvUrl = parseGetFLV.getUrl(forceLow);
//			final String getflvMs = parseGetFLV.getMs();
//			final String getflvThreadId = parseGetFLV.getThreadId();
//			parseGetThreadKey.initialize(httpClient,
//					getflvThreadId, cookieUserSession, userAgent);
//
//			if (getflvUrl == null
//					|| getflvMs == null
//					|| getflvThreadId == null) {
//				throw new FailPreparePlayVideoException();
//			}
//
//			Class<?> activityClass = null;
//			// 動画タイプチェック＆Activity選択
//			if (isGetflvUrlFlv(getflvUrl)) {
//				// flv
//				activityClass = NicoroFFmpegPlayer.class;
//			} else if (isGetflvUrlMp4(getflvUrl)) {
//				// mp4
//				SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
//				if (sharedPreferences.getBoolean(
//						context.getString(R.string.pref_key_mp4_mediaplayer), true)) {
//					activityClass = NicoroMediaPlayer.class;
//				} else {
//					activityClass = NicoroFFmpegPlayer.class;
//				}
//			} else if (isGetflvUrlSwf(getflvUrl)) {
//				// swf
////				// 未対応
////				Log.w(LOG_TAG, "SWF is unsupported");
////				FailPreparePlayVideoException e = new FailPreparePlayVideoException();
////				e.setExtraMessage("swf形式は未対応です");
////				throw e;
//				activityClass = NicoroSwfPlayer.class;
//			} else {
//				// 想定外
//				Log.w(LOG_TAG, Log.buf().append("Unrecognized video URL:")
//						.append(getflvUrl).toString());
//				// とりあえずデフォルトで起動を試みる
//				activityClass = NicoroFFmpegPlayer.class;
//			}
//
//			if (activityClass != null) {
//				return new Intent(context, activityClass)
//					.putExtra(
//							AbstractNicoroPlayer.INTENT_NAME_VIDEO_URL,
//							getflvUrl)
//					.putExtra(
//							AbstractNicoroPlayer.INTENT_NAME_COOKIE,
//							cookieNicoHistory)
//					.putExtra(
//							AbstractNicoroPlayer.INTENT_NAME_VIDEO_NUMBER,
//							videoNumber)
//					.putExtra(
//							AbstractNicoroPlayer.INTENT_NAME_MESSAGE_URL,
//							getflvMs)
//					.putExtra(
//							AbstractNicoroPlayer.INTENT_NAME_THREAD_ID,
//							getflvThreadId)
//					.putExtra(
//							AbstractNicoroPlayer.INTENT_NAME_USER_ID,
//							userId)
//					.putExtra(
//							AbstractNicoroPlayer.INTENT_NAME_THREAD_KEY,
//							parseGetThreadKey.getThreadKey())
//					.putExtra(
//							AbstractNicoroPlayer.INTENT_NAME_FORCE_184,
//							parseGetThreadKey.getForce184())
//					.putExtra(
//							AbstractNicoroPlayer.INTENT_NAME_COOKIE_USER_SESSION,
//							cookieUserSession);
//			}
//		} catch (ClientProtocolException e) {
//			Log.d(LOG_TAG, e.getMessage(), e);
//		} catch (IOException e) {
//			Log.d(LOG_TAG, e.getMessage(), e);
//		}
//		return null;
//	}

	/**
	 * {@link NicoroJikkyoPlayer} を起動するIntentの作成
	 */
	public static Intent createJikkyoPlayerIntent(Context context,
			String jikkyoNumber)
	throws FailPreparePlayVideoException {
		Intent intent = new Intent(context, NicoroJikkyoPlayer.class);
		intent.putExtra(
				NicoroJikkyoPlayer.INTENT_NAME_JIKKYO_NUMBER,
				jikkyoNumber);
		return intent;
	}

	/**
     * 生放送再生で {@link PlayerActivity} を起動するIntentの作成
	 * @param context
	 * @param liveVideo
	 * @param cookieUserSession
	 * @param title
	 * @param description
	 * @return
	 * @throws FailPreparePlayVideoException
	 */
    public static Intent createLivePlayerIntent(Context context,
            WatchVideo liveVideo, String cookieUserSession,
            String title, String description)
    throws FailPreparePlayVideoException {
        // descriptionの改行除去
        if (description != null) {
            description = description.replaceAll("[\\r\\n]+", " ");
        }

        NicoroConfig.saveCookieUserSession(context, cookieUserSession);

        Intent intent = new Intent(context, PlayerActivity.class)
            .putExtra(
                    PlayerConstants.INTENT_NAME_WATCH_VIDEO,
                    liveVideo)
            .putExtra(
                    PlayerConstants.INTENT_NAME_LIVE_NUMBER,
                    liveVideo.v())
            .putExtra(
                    PlayerConstants.INTENT_NAME_TITLE,
                    title)
            .putExtra(
                    PlayerConstants.INTENT_NAME_DESCRIPTION,
                    description)
                    ;
        return intent;
    }

    public static Intent createPlaylistPlayerIntent(Context context,
            JSONObject playlistJson, int sortOrder, int ecoType,
            String cookieUserSession, String userId) throws IOException {
        NicoroConfig.saveCookieUserSession(context, cookieUserSession);

        // playlistは長いことがあるのでgzipで圧縮
        Playlist playlist = new Playlist(playlistJson);
        byte[] playlistGzipBytes = Util.deflateSerializable(playlist);
        if (playlistGzipBytes == null) {
            throw new IOException("Playlisst deflate failed");
        }
        if (DEBUG_LOGD) {
            Log.d(LOG_TAG, Log.buf().append("playlist gzip size=")
                    .append(playlistGzipBytes.length)
                    .toString());
        }

        Intent intent = new Intent(context, PlayerActivity.class)
            .putExtra(PlayerConstants.INTENT_NAME_PLAYLIST,
                    playlistGzipBytes)
            .putExtra(PlayerConstants.INTENT_NAME_PLAYLIST_SORT_ORDER,
                    sortOrder)
            .putExtra(PlayerConstants.INTENT_NAME_FORCE_ECO,
                    ecoType)
            .putExtra(PlayerConstants.INTENT_NAME_USER_ID,
                    userId);
        return intent;
    }

	/**
     * nicohistoryのCookieをネットワークから取得<br>
	 * ネットワークからの情報取得が完了するまで処理が返らないので注意
	 *
	 * @param httpClient
	 * @param videoNumber
	 * @param cookieUserSession
	 * @param forceLow
	 * @param userAgent
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static String getCookieNicoHistory(DefaultHttpClient httpClient,
			String videoNumber, String cookieUserSession,
			int forceEco, String userAgent)
	throws ClientProtocolException, IOException {
		HttpUriRequest httpRequest = createGetCookieNicoHistory(
				videoNumber, cookieUserSession, forceEco, userAgent);
		return getCookieNicoHistory(httpClient, httpRequest, forceEco);
	}

    /**
     * nicohistoryのCookieをネットワークから取得<br>
     * ネットワークからの情報取得が完了するまで処理が返らないので注意
     *
     * @param httpClient
     * @param httpRequest
     * @param ecoType
     * @return
     * @throws ClientProtocolException
     * @throws IOException
     */
    public static String getCookieNicoHistory(DefaultHttpClient httpClient,
            HttpUriRequest httpRequest, int ecoType)
    throws ClientProtocolException, IOException {
        String host;
        // XXX とりあえず中画質だけsp使用
        if (ecoType == ECO_TYPE_MID) {
            host = HOST_SP;
        } else {
            host = HOST_TOP;
        }
        return getCookieNicoHistory(httpClient, httpRequest, host);
    }

	/**
	 * nicohistoryのCookieをネットワークから取得<br>
     * ネットワークからの情報取得が完了するまで処理が返らないので注意
     *
	 * @param httpClient
	 * @param httpRequest
	 * @param httpHost
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
    public static String getCookieNicoHistory(DefaultHttpClient httpClient,
            HttpUriRequest httpRequest, String httpHost)
    throws ClientProtocolException, IOException {
//        if (DEBUG_LOGD) {
//            Log.d(LOG_TAG, "==========getCookieNicoHistory httpRequest==========");
//            Util.logHeaders(LOG_TAG, httpRequest.getAllHeaders());
//            Log.d(LOG_TAG, "==========httpRequest end==========");
//        }

		HttpResponse httpResponse = HttpManager.executeWrapper(httpClient,
		        new HttpHost(httpHost, 80), httpRequest);
        StatusLine statusLine = httpResponse.getStatusLine();
		if (DEBUG_LOGD) {
			Log.d(LOG_TAG, "==========getCookieNicoHistory httpResponse==========");
            Log.d(LOG_TAG, BasicLineFormatter.formatStatusLine(statusLine, null));
			Util.logHeaders(LOG_TAG, httpResponse.getAllHeaders());
			Log.d(LOG_TAG, "==========httpResponse end==========");
		}
		String cookieVideo = null;
		if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
			List<Cookie> cookies = httpClient.getCookieStore().getCookies();
			for (Cookie c : cookies) {
				if ("nicohistory".equals(c.getName())) {
					cookieVideo = c.getName() + "=" + c.getValue();
					break;
				}
			}
		}
		if (DEBUG_LOGD) {
			Log.d(LOG_TAG, Log.buf().append("getCookieNicoHistory: ")
					.append(cookieVideo).toString());
		}
		return cookieVideo;
	}

    public static class CookieNicoHistoryAndBody {
        public String cookie;
        public String html;
    }

    public static CookieNicoHistoryAndBody getCookieNicoHistoryAndBody(DefaultHttpClient httpClient,
            HttpUriRequest httpRequest, int ecoType)
    throws ClientProtocolException, IOException {
        String host;
        // XXX とりあえず中画質だけsp使用
        if (ecoType == ECO_TYPE_MID) {
            host = HOST_SP;
        } else {
            host = HOST_TOP;
        }
        return getCookieNicoHistoryAndBody(httpClient, httpRequest, host);
    }

    public static CookieNicoHistoryAndBody getCookieNicoHistoryAndBody(DefaultHttpClient httpClient,
            HttpUriRequest httpRequest, String httpHost)
    throws ClientProtocolException, IOException {
//        if (DEBUG_LOGD) {
//            Log.d(LOG_TAG, "==========getCookieNicoHistoryAndBody httpRequest==========");
//            Util.logHeaders(LOG_TAG, httpRequest.getAllHeaders());
//            Log.d(LOG_TAG, "==========httpRequest end==========");
//        }

        HttpResponse httpResponse = HttpManager.executeWrapper(httpClient,
                new HttpHost(httpHost, 80), httpRequest);
        StatusLine statusLine = httpResponse.getStatusLine();
        if (DEBUG_LOGD) {
            Log.d(LOG_TAG, "==========getCookieNicoHistoryAndBody httpResponse==========");
            Log.d(LOG_TAG, BasicLineFormatter.formatStatusLine(statusLine, null));
            Util.logHeaders(LOG_TAG, httpResponse.getAllHeaders());
            Log.d(LOG_TAG, "==========httpResponse end==========");
        }
        String cookieVideo = null;
        StringBuilder builder = new StringBuilder();
        if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
            List<Cookie> cookies = httpClient.getCookieStore().getCookies();
            for (Cookie c : cookies) {
                if ("nicohistory".equals(c.getName())) {
                    cookieVideo = c.getName() + "=" + c.getValue();
                    break;
                }
            }

            HttpEntity httpEntity = null;
            InputStream inDownload = null;
            InputStreamReader inReader = null;
            try {
                httpEntity = httpResponse.getEntity();
                inDownload = httpEntity.getContent();
                // XXX とりあえずUTF-8決め打ち
                inReader = new InputStreamReader(inDownload, HTTP.UTF_8);
                char[] buf = new char[1024];
                while (true) {
                    int read = inReader.read(buf);
                    if (read < 0) {
                        break;
                    }
                    builder.append(buf, 0, read);
                }
            } finally {
                if (httpEntity != null) {
                    try {
                        httpEntity.consumeContent();
                    } catch (IOException e) {
                        Log.w(LOG_TAG, e.toString(), e);
                    }
                }
                FileUtil.closeIgnoreException(inDownload);
                FileUtil.closeIgnoreException(inReader);
            }
        }
        if (DEBUG_LOGD) {
            Log.d(LOG_TAG, Log.buf().append("getCookieNicoHistoryAndBody: cookie=")
                    .append(cookieVideo).toString());
        }

        CookieNicoHistoryAndBody result = new CookieNicoHistoryAndBody();
        result.cookie = cookieVideo;
        result.html = builder.toString();
        return result;
    }

    /**
     * nicohistoryのCookieをネットワークから取得
     * @param videoNumber
     * @param cookieUserSession
     * @param ecoType
     * @param userAgent
     * @return
     */
	public static HttpUriRequest createGetCookieNicoHistory(
			String videoNumber, String cookieUserSession,
			int ecoType, String userAgent) {
		String uri = "/watch/" + videoNumber;
		if (ecoType == ECO_TYPE_LOW) {
			uri += "?eco=1";
		} else if (ecoType == ECO_TYPE_MID) {
            uri += "?eco=2";
		}
		HttpUriRequest httpRequest = new HttpHead(uri);
//		HttpRequest httpRequest = new HttpGet(uri);
		String cookie;
		if (ecoType == ECO_TYPE_MID) {
		    cookie = cookieUserSession;
		} else {
            // XXX PC扱いでないと 403 Forbiddenが出る動画あり
		    cookie = cookieUserSession + ";usepc=1";
		}
		httpRequest.addHeader("Cookie", cookie);
		if (userAgent != null) {
			httpRequest.setHeader("User-Agent", userAgent);
		}
		return httpRequest;
	}

	/**
	 * nicohistoryのCookieとHTML本文をネットワークから取得
	 * @param videoNumber
	 * @param cookieUserSession
	 * @param ecoType
	 * @param userAgent
	 * @return
	 */
	public static HttpUriRequest createGetCookieNicoHistoryAndBody(
            String videoNumber, String cookieUserSession,
            int ecoType, String userAgent) {
        String uri = "/watch/" + videoNumber;
        if (ecoType == ECO_TYPE_LOW) {
            uri += "?eco=1";
        } else if (ecoType == ECO_TYPE_MID) {
            uri += "?eco=2";
        }
        HttpUriRequest httpRequest = new HttpGet(uri);
//      HttpRequest httpRequest = new HttpGet(uri);
        String cookie;
        if (ecoType == ECO_TYPE_MID) {
            cookie = cookieUserSession;
        } else {
            // XXX PC扱いでないと 403 Forbiddenが出る動画あり
            cookie = cookieUserSession + ";usepc=1";
        }
        httpRequest.addHeader("Cookie", cookie);
        if (userAgent != null) {
            httpRequest.setHeader("User-Agent", userAgent);
        }
        return httpRequest;
    }

	/**
	 * URLから動画がflv形式か判定
	 * @param url
	 * @return
	 */
	public static boolean isGetflvUrlFlv(String url) {
		return url.indexOf("?v=") >= 0;
	}
	/**
	 * URLから動画がmp4形式か判定
	 * @param url
	 * @return
	 */
	public static boolean isGetflvUrlMp4(String url) {
		return url.indexOf("?m=") >= 0;
	}
	/**
	 * URLから動画がswf形式か判定
	 * @param url
	 * @return
	 */
	public static boolean isGetflvUrlSwf(String url) {
		return url.indexOf("?s=") >= 0;
	}
	/**
	 * URLから動画が低画質か判定
	 * @param url
	 * @return
	 */
	public static boolean isGetflvUrlLow(String url) {
		return url.endsWith("low");
	}
	/**
	 * URLから動画が中画質か判定
	 * @param url
	 * @return
	 */
    public static boolean isGetflvUrlMid(String url) {
        return url.endsWith("mid");
    }

    /**
     * 動画番号からswf形式か判定
     * @param videoNumber
     * @return
     */
	public static boolean isVideoNumberSwf(String videoNumber) {
		return videoNumber.startsWith("nm");
	}

	/**
	 * 高画質でないか判定
	 * @param ecoType
	 * @return
	 */
	public static boolean isNotHigh(int ecoType) {
	    return ecoType != ECO_TYPE_HIGH;
	}

	/**
	 * 動画再生リストのJSON取得
	 * @param loader
	 * @param number
	 */
	public static void executePlaylistJSONLoader(NicoJSONCustomLoader loader,
	        String number) {
	    loader.executeWrapper(HOST_API, "/api/getplaylist/mylist/" + number);
	}

    public static String getUserSessionFromCookie(String cookie) {
        Matcher matcher = Pattern.compile(PATTERN_USER_SESSION).matcher(cookie);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }
}
