package jp.sourceforge.nicoro;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.Header;
import org.apache.http.HttpHost;
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.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.BasicNameValuePair;
import org.apache.http.protocol.HTTP;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
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 abstract class ParseGetAPIBase {
		protected HashMap<String, String> mData = new HashMap<String, String>();
		
		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);
			initialize(infoBody);
		}
		
		protected void initialize(String infoBody) {
			String begin = infoBody;
			while (true) {
				int indexKeyEnd = begin.indexOf('=');
				if (indexKeyEnd < 0) {
					break;
				}
				int indexValueBegin = indexKeyEnd + 1;
				int indexValueEnd = begin.indexOf('&', indexValueBegin);
				
				String key = begin.substring(
						0, indexKeyEnd);
				String value;
				if (indexValueEnd < 0) {
					value = begin.substring(
							indexValueBegin);
				} else {
					value = begin.substring(
							indexValueBegin, indexValueEnd);
				}
				if (DEBUG_LOGV) {
					Log.v(LOG_TAG, Log.buf().append("get: key=").append(key)
							.append(" value=").append(value)
							.toString());
				}
				mData.put(key, value);
				if (indexValueEnd < 0) {
					break;
				}
				begin = begin.substring(indexValueEnd + 1);
			}
		}
		
		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 static class ParseGetFLV extends ParseGetAPIBase {
		public static final String HOSTNAME_VIDEO = "www.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_VIDEO, videoNumber, cookie, userAgent);
		}
		
		public String getUrl() {
			String url = mData.get("url");
			if (url.indexOf("?s=") >= 0) {
				// swfはURL調整
				url += "as3";
			}
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, url);
			}
			
			// テスト：エコノミー
//			urlVideo += "low";
			
			return url;
		}
		public String getMs() {
			return mData.get("ms");
		}
		public String getThreadId() {
			return mData.get("thread_id");
		}
	}

	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);
		}
	}
	
	public static class ParseGetThreadKey extends ParseGetAPIBase {
		public static final String HOSTNAME = "flapi.nicovideo.jp";
		
		@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");
		}
	}

	public String getAuthorizeCookie(DefaultHttpClient httpClient, String userAgent) throws ClientProtocolException, IOException {
		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", ""));
		postParams.add(new BasicNameValuePair("password", ""));
		httpRequest.setEntity(new UrlEncodedFormEntity(postParams, HTTP.UTF_8));
		if (userAgent != null) {
			httpRequest.setHeader("User-Agent", userAgent);
		}
		
		HttpResponse httpResponse = httpClient.execute(new HttpHost("secure.nicovideo.jp", 443, "https"), httpRequest);
		
		if (DEBUG_LOGD) {
			Log.d(LOG_TAG, "getAuthorizeCookie httpResponse>");
			Util.logHeaders(LOG_TAG, httpResponse.getAllHeaders());
		}
		if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
			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());
		}
		return cookieAuthorize.toString();
	}
	
	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("www.nicovideo.jp", 80),
				httpRequest
				);
		if (DEBUG_LOGD) {
			Log.d(LOG_TAG, "getVideoCookie httpResponse>>>");
			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();
	}
	
	public static boolean checkIsCookieUserSessionValid(String cookieUserSession, String userAgent) {
		if (cookieUserSession == null || cookieUserSession.length() == 0) {
			return false;
		}
		
		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());
		}
		
		DefaultHttpClient httpClient = Util.createHttpClient();
		httpClient.getCookieStore().clear();
		try {
			HttpResponse httpResponse = httpClient.execute(
					httpRequest
					);
			if (DEBUG_LOGD) {
				Log.d(LOG_TAG, httpResponse.getStatusLine().getReasonPhrase());
				Util.logHeaders(LOG_TAG, httpResponse.getAllHeaders());
			}
			
			int httpStatusCode = httpResponse.getStatusLine().getStatusCode();
			if (httpStatusCode != HttpStatus.SC_OK) {
				return false;
			}
			
			return checkNiconicoAuthflag(httpResponse);
		} catch (ClientProtocolException e) {
			Log.d(LOG_TAG, e.getMessage(), e);
			return false;
		} catch (IOException e) {
			Log.d(LOG_TAG, e.getMessage(), e);
			return false;
		} catch (NumberFormatException e) {
			Log.d(LOG_TAG, e.getMessage(), e);
			return false;
		}
	}
	
	public static boolean checkNiconicoAuthflag(HttpResponse httpResponse) {
		Header authflag = httpResponse.getFirstHeader("x-niconico-authflag");
		if (authflag == null) {
			return false;
		}
		try {
			return Integer.parseInt(authflag.getValue()) != 0;
		} catch (NumberFormatException e) {
			return false;
		}
	}
	
	public static Intent createVideoPlayerIntent(Context context,
			String videoNumber, String cookieUserSession,
			String cookieNicoHistory, String userId,
			String userAgent)
	throws FailPreparePlayVideoException {
		try {
			DefaultHttpClient httpClient = Util.createHttpClient();
			httpClient.getCookieStore().clear();
			ParseGetFLV parseGetFLV = new ParseGetFLV();
			parseGetFLV.initialize(httpClient,
					videoNumber,
					cookieUserSession, userAgent);
			ParseGetThreadKey parseGetThreadKey = new ParseGetThreadKey();
			final String getflvUrl = parseGetFLV.getUrl();
			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 (getflvUrl.indexOf("?v=") >= 0) {
				// flv
				activityClass = NicoroFFmpegPlayer.class;
			} else if (getflvUrl.indexOf("?m=") >= 0) {
				// 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 (getflvUrl.indexOf("?s=") >= 0) {
				// 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) {
				Intent intent = new Intent(context, activityClass);
				intent.putExtra(
						AbstractNicoroPlayer.INTENT_NAME_VIDEO_URL,
						getflvUrl);
				intent.putExtra(
						AbstractNicoroPlayer.INTENT_NAME_COOKIE,
						cookieNicoHistory);
				intent.putExtra(
						AbstractNicoroPlayer.INTENT_NAME_VIDEO_NUMBER,
						videoNumber);
				intent.putExtra(
						AbstractNicoroPlayer.INTENT_NAME_MESSAGE_URL,
						getflvMs);
				intent.putExtra(
						AbstractNicoroPlayer.INTENT_NAME_THREAD_ID,
						getflvThreadId);
				intent.putExtra(
						AbstractNicoroPlayer.INTENT_NAME_USER_ID,
						userId);
				intent.putExtra(
						AbstractNicoroPlayer.INTENT_NAME_THREAD_KEY,
						parseGetThreadKey.getThreadKey());
				intent.putExtra(
						AbstractNicoroPlayer.INTENT_NAME_FORCE_184,
						parseGetThreadKey.getForce184());
				intent.putExtra(
						AbstractNicoroPlayer.INTENT_NAME_COOKIE_USER_SESSION,
						cookieUserSession);
				return intent;
			}
		} catch (ClientProtocolException e) {
			Log.d(LOG_TAG, e.getMessage(), e);
		} catch (IOException e) {
			Log.d(LOG_TAG, e.getMessage(), e);
		}
		return null;
	}

	public static Intent createJikkyoPlayerIntent(Context context,
			String jikkyoNumber, String cookieUserSession,
			String cookieNicoHistory, String userId,
			String userAgent)
	throws FailPreparePlayVideoException {
//		try {
//			DefaultHttpClient httpClient = Util.createHttpClient();
//			httpClient.getCookieStore().clear();
//			ParseGetFLVJikkyo parseGetFLVJikkyo = new ParseGetFLVJikkyo();
//			parseGetFLVJikkyo.initialize(httpClient,
//					jikkyoNumber,
//					cookieUserSession, userAgent);
			
			Intent intent = new Intent(context, NicoroJikkyoPlayer.class);
//			intent.putExtra(
//					NicoroJikkyoPlayer.INTENT_NAME_COOKIE,
//					cookieNicoHistory);
			intent.putExtra(
					NicoroJikkyoPlayer.INTENT_NAME_JIKKYO_NUMBER,
					jikkyoNumber);
//			intent.putExtra(
//					NicoroJikkyoPlayer.INTENT_NAME_MESSAGE_URL,
//					parseGetFLVJikkyo.get("ms"));
//			intent.putExtra(
//					NicoroJikkyoPlayer.INTENT_NAME_MESSAGE_PORT,
//					parseGetFLVJikkyo.get("ms_port"));
//			intent.putExtra(
//					NicoroJikkyoPlayer.INTENT_NAME_THREAD_ID,
//					parseGetFLVJikkyo.get("thread_id"));
//			intent.putExtra(
//					NicoroJikkyoPlayer.INTENT_NAME_USER_ID,
//					parseGetFLVJikkyo.get("user_id"));
//			intent.putExtra(
//					NicoroJikkyoPlayer.INTENT_NAME_HTTP_PORT,
//					parseGetFLVJikkyo.get("http_port"));
			return intent;
//		} catch (ClientProtocolException e) {
//			Log.d(LOG_TAG, e.getMessage(), e);
//		} catch (IOException e) {
//			Log.d(LOG_TAG, e.getMessage(), e);
//		}
//		
//		return null;
	}
	
	public static String getCookieNicoHistory(DefaultHttpClient httpClient,
			String videoNumber, String cookieUserSession, String userAgent)
	throws ClientProtocolException, IOException {
		HttpRequest httpRequest = new HttpHead("/watch/" + videoNumber);
//		HttpRequest httpRequest = new HttpGet("/watch/" + videoNumber);
		httpRequest.addHeader("Cookie", cookieUserSession);
		if (userAgent != null) {
			httpRequest.setHeader("User-Agent", userAgent);
		}
		HttpResponse httpResponse = httpClient.execute(
				new HttpHost("www.nicovideo.jp", 80),
				httpRequest
				);
		if (DEBUG_LOGD) {
			Log.d(LOG_TAG, "==========getCookieNicoHistory httpResponse==========");
			Util.logHeaders(LOG_TAG, httpResponse.getAllHeaders());
			Log.d(LOG_TAG, "==========httpResponse end==========");
		}
		String cookieVideo = null;
		if (httpResponse.getStatusLine().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;
	}
}
