package jp.sourceforge.nicoro;

import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

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

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.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;

// TODO 複数動画のキャッシュを順番に実行できるように

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

	public static final String INTENT_NAME_VIDEO_URL = "VIDEO_URL";
	public static final String INTENT_NAME_VIDEO_NUMBER = "VIDEO_NUMBER";
//	public static final String INTENT_NAME_COOKIE_NICO_HISTORY = "COOKIE_NICO_HISTORY";
	public static final String INTENT_NAME_COOKIE_USER_SESSION = "COOKIE_USER_SESSION";

	public static final int CACHE_STATE_INVALID = -1;
	public static final int CACHE_STATE_NOT_RUN = 0;
	public static final int CACHE_STATE_WAIT_START = 1;
	public static final int CACHE_STATE_RUNNING = 2;

	private static final int MSG_ID_ADD_START_CACHE = 0;
	private static final int MSG_ID_VIDEO_LOADER_FINISHED = 1;
    private static final int MSG_ID_VIDEO_LOADER_NOTIFY_PROGRESS = 2;
	private static final int MSG_ID_VIDEO_LOADER_OCCURRED_ERROR = 3;
	private static final int MSG_ID_VIDEO_LOADER_STOP_CACHE_CURRENT = 4;
	private static final int MSG_ID_VIDEO_LOADER_STOP_CACHE_QUEUE = 5;
	private static final int MSG_ID_START_FIRST = 6;

	private static class CacheRequest {
		public String videoNumber;
		public String cookieUserSession;
		public boolean forceLow;
		public int notificationIDStartVideoCache;
		public int notificationIDFinishVideoCache;
	}

	private static class NotifyProgressData {
	    String videoNumber;
	    boolean isLow;
	    int seekOffsetWrite;
	    int contentLength;
	}

	private CacheRequest createCacheRequest(String vn, String cus,
			boolean fl) {
		CacheRequest cacheRequest = new CacheRequest();
		cacheRequest.videoNumber = vn;
		cacheRequest.cookieUserSession = cus;
		cacheRequest.forceLow = fl;
		cacheRequest.notificationIDStartVideoCache = mNotificationIDCounter;
    	++mNotificationIDCounter;
    	cacheRequest.notificationIDFinishVideoCache = mNotificationIDCounter;
    	++mNotificationIDCounter;
    	assert cacheRequest.notificationIDStartVideoCache % 2 == 0;
    	return cacheRequest;
	}

	private VideoLoader mVideoLoader;
	private CacheRequest mCacheRequestCurrent = null;
	private LinkedList<CacheRequest> mCacheRequestQueue = new LinkedList<CacheRequest>();

	private int mNotificationIDCounter = 0;

//	private DefaultHttpClient mHttpClient;

	private volatile int mSeekOffsetWrite = -1;
	private volatile int mContentLength = -1;

	private AtomicReference<NotifyProgressData> mRefNotifyProgressData =
	    new AtomicReference<NotifyProgressData>(new NotifyProgressData());

	private Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, "handleMessage msg.what=" + msg.what);
			}
			String videoNumber;
			CacheRequest cacheRequest;
			switch (msg.what) {
			case MSG_ID_ADD_START_CACHE:
				cacheRequest = (CacheRequest) msg.obj;
				if (mCacheRequestCurrent == null) {
					startCacheRequestCurrent(cacheRequest);
				} else {
					mCacheRequestQueue.add(cacheRequest);
				}
				break;
			case MSG_ID_VIDEO_LOADER_FINISHED: {
			    videoNumber = (String) msg.obj;
			    boolean isLow = (msg.arg1 != 0);

	            final int numCallbacks = mCallbacks.beginBroadcast();
	            for (int i = 0; i < numCallbacks; ++i) {
	                try {
	                    mCallbacks.getBroadcastItem(i).onFinished(
	                            videoNumber, isLow);
	                } catch (RemoteException e) {
	                    Log.e(LOG_TAG, e.toString(), e);
	                }
	            }
	            mCallbacks.finishBroadcast();

				cacheRequest = mCacheRequestQueue.poll();
				if (cacheRequest != null) {
					startCacheRequestCurrent(cacheRequest);
				}
			} break;
			case MSG_ID_VIDEO_LOADER_NOTIFY_PROGRESS: {
			    NotifyProgressData notifyProgressData =
			        (NotifyProgressData) msg.obj;
			    assert notifyProgressData != null;
			    assert mRefNotifyProgressData.get() == null;

	            final int numCallbacks = mCallbacks.beginBroadcast();
	            for (int i = 0; i < numCallbacks; ++i) {
	                try {
	                    mCallbacks.getBroadcastItem(i).onNotifyProgress(
	                            notifyProgressData.videoNumber,
	                            notifyProgressData.isLow,
	                            notifyProgressData.seekOffsetWrite,
	                            notifyProgressData.contentLength);
	                } catch (RemoteException e) {
	                    Log.e(LOG_TAG, e.toString(), e);
	                }
	            }
	            mCallbacks.finishBroadcast();

			    mRefNotifyProgressData.set(notifyProgressData);
			} break;
			case MSG_ID_VIDEO_LOADER_OCCURRED_ERROR:
				Util.showErrorToast(getApplicationContext(), (String) msg.obj);

				// エラー時も次のキャッシュ開始
				cacheRequest = mCacheRequestQueue.poll();
				if (cacheRequest != null) {
					startCacheRequestCurrent(cacheRequest);
				}
				break;
			case MSG_ID_VIDEO_LOADER_STOP_CACHE_CURRENT:
				// 次のキャッシュ開始
				cacheRequest = mCacheRequestQueue.poll();
				if (cacheRequest != null) {
					startCacheRequestCurrent(cacheRequest);
				}
				break;
			case MSG_ID_VIDEO_LOADER_STOP_CACHE_QUEUE:
				// 待ち行列にあれば削除
				videoNumber = (String) msg.obj;
				for (Iterator<CacheRequest> it = mCacheRequestQueue.iterator(); it.hasNext(); ) {
					CacheRequest cr = it.next();
					if (cr.videoNumber.equals(videoNumber)) {
						it.remove();
					}
				}
				break;
			case MSG_ID_START_FIRST:
				// 一番最初のキャッシュ開始
				startFirst((Intent) msg.obj);
				break;
			default:
				assert false : msg.what;
				break;
			}
		}
	};

	private RemoteCallbackList<IVideoCacheServiceCallback> mCallbacks =
		new RemoteCallbackList<IVideoCacheServiceCallback>();
	private final IVideoCacheService.Stub mBinder = new IVideoCacheService.Stub() {
		@Override
		public int getContentLength(String videoNumber, boolean isLow)
		throws RemoteException {
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, Log.buf().append("getContentLength() mContentLength=")
						.append(mContentLength).toString());
			}
			// TODO mCacheRequestCurrentのチェックも同一メインスレッドで行わないとまずい？
			CacheRequest cacheRequestCurrent = mCacheRequestCurrent;
			VideoLoader videoLoader = mVideoLoader;
			if (cacheRequestCurrent != null && videoLoader != null
					&& cacheRequestCurrent.videoNumber.equals(videoNumber)
					&& videoLoader.isLow() == isLow) {
				return mContentLength;
			} else {
				return -1;
			}
		}
		@Override
		public int getProgress(String videoNumber, boolean isLow)
		throws RemoteException {
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, Log.buf().append("getProgress() mSeekOffsetWrite=")
						.append(mSeekOffsetWrite).toString());
			}
			CacheRequest cacheRequestCurrent = mCacheRequestCurrent;
			VideoLoader videoLoader = mVideoLoader;
			if (cacheRequestCurrent != null && videoLoader != null
					&& cacheRequestCurrent.videoNumber.equals(videoNumber)
					&& videoLoader.isLow() == isLow) {
				return mSeekOffsetWrite;
			} else {
				return -1;
			}
		}
		@Override
		public void stopCache(String videoNumber) {
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, Log.buf().append("stopCache() videoNumber=")
						.append(videoNumber).toString());
			}
			// 通常と低画質の二つのキャッシュが同時に動作している可能性がある

			// 待ち行列にあれば削除
			mHandler.obtainMessage(
					MSG_ID_VIDEO_LOADER_STOP_CACHE_QUEUE, videoNumber)
					.sendToTarget();

			CacheRequest cacheRequestCurrent = mCacheRequestCurrent;
			if (cacheRequestCurrent != null
					&& cacheRequestCurrent.videoNumber.equals(videoNumber)) {
				VideoLoader videoLoader = mVideoLoader;
				if (videoLoader != null) {
					videoLoader.finish();
				}

		    	// 開始時のNotification削除
				NotificationManager notificationManager =
					(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
				notificationManager.cancel(cacheRequestCurrent.notificationIDStartVideoCache);

				mCacheRequestCurrent = null;
				// 次のキャッシュがあれば開始
				mHandler.sendEmptyMessage(MSG_ID_VIDEO_LOADER_STOP_CACHE_CURRENT);
			}
		}
		@Override
		public int getCacheState(String videoNumber, boolean isLow) {
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, Log.buf().append("getCacheState() videoNumber=")
						.append(videoNumber).append(" isLow=").append(isLow)
						.toString());
			}
			CacheRequest cacheRequestCurrent = mCacheRequestCurrent;
			if (cacheRequestCurrent != null
					&& cacheRequestCurrent.videoNumber.equals(videoNumber)) {
				VideoLoader videoLoader = mVideoLoader;
				if (videoLoader == null) {
					if (cacheRequestCurrent.forceLow == isLow) {
						if (DEBUG_LOGV) {
							Log.v(LOG_TAG, "CACHE_STATE_RUNNING: mCacheRequestCurrent.forceLow == isLow");
						}
						return CACHE_STATE_RUNNING;
					}
				} else {
					boolean isLowVideoLoader = videoLoader.isLow();
					if (isLowVideoLoader == isLow) {
						if (DEBUG_LOGV) {
							Log.v(LOG_TAG, "CACHE_STATE_RUNNING: mVideoLoader.isLow() == isLow");
						}
						return CACHE_STATE_RUNNING;
					} else {
						if (!cacheRequestCurrent.forceLow
								&& isLowVideoLoader) {
							// 高画質要求で低画質動作中
							if (DEBUG_LOGV) {
								Log.v(LOG_TAG, "CACHE_STATE_RUNNING: !mCacheRequestCurrent.forceLow && mVideoLoader.isLow()");
							}
							return CACHE_STATE_RUNNING;
						}
					}
				}
			}

			for (CacheRequest cr : mCacheRequestQueue) {
				// キャッシュ待ちは本当に低画質になるか分からないので
				// forceLowで判断
				if (videoNumber.equals(cr.videoNumber)
						&& isLow == cr.forceLow) {
					if (DEBUG_LOGV) {
						Log.v(LOG_TAG, "CACHE_STATE_WAIT_START");
					}
					return CACHE_STATE_WAIT_START;
				}
			}
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, "CACHE_STATE_NOT_RUN");
			}
			return CACHE_STATE_NOT_RUN;
		}
		@Override
		public void addStartCache(String videoNumber,
				boolean forceLow, String cookieUserSession) {
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, Log.buf().append("addStartCache() videoNumber=")
						.append(videoNumber).append(" forceLow=")
						.append(forceLow).append(" cookieUserSession=")
						.append(cookieUserSession).toString());
			}
			CacheRequest cacheRequest = createCacheRequest(
					videoNumber, cookieUserSession, forceLow);
			mHandler.obtainMessage(
					MSG_ID_ADD_START_CACHE, cacheRequest)
					.sendToTarget();
		}
		@Override
		public int getWaitRequestSize() {
			return mCacheRequestQueue.size();
		}

		@Override
		public void addListener(IVideoCacheServiceCallback callback) {
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, Log.buf().append("addListener() callback=")
						.append(callback).toString());
			}
			mCallbacks.register(callback);
		}
		@Override
		public void removeListener(IVideoCacheServiceCallback callback) {
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, Log.buf().append("removeListener() callback=")
						.append(callback).toString());
			}
			mCallbacks.unregister(callback);
		}
	};

	private final VideoLoader.EventListener mVideoLoaderEventListener =
		new VideoLoader.EventListener() {
		@Override
		public void onStarted(VideoLoader streamLoader) {
			// 何もしない
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, "VideoCacheService onStarted");
			}
		}
		@Override
		public void onCached(VideoLoader streamLoader) {
			// 何もせず
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, "VideoCacheService onCached");
			}
		}
		@Override
		public void onFinished(VideoLoader streamLoader) {
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, "VideoCacheService onFinished");
			}
            CacheRequest cacheRequestCurrent = mCacheRequestCurrent;
            if (cacheRequestCurrent == null) {
                Log.e(LOG_TAG, "VideoCacheService#onFinished: mCacheRequestCurrent is null!");
                return;
            }
            assert cacheRequestCurrent.videoNumber.equals(
                    streamLoader.getVideoNumber());
            // キャッシュ完了のNotification表示
            setNotificationFinishCache(cacheRequestCurrent);

            mCacheRequestCurrent = null;
			mHandler.obtainMessage(MSG_ID_VIDEO_LOADER_FINISHED,
			        (streamLoader.isLow() ? 1 : 0), 0,
			        cacheRequestCurrent.videoNumber).sendToTarget();

//			// 自分自身の終了
//			stopSelf();
		}
		@Override
		public void onNotifyProgress(int seekOffsetWrite, int contentLength) {
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, "VideoCacheService onNotifyProgress");
			}
			mSeekOffsetWrite = seekOffsetWrite;
			mContentLength = contentLength;

			CacheRequest cacheRequestCurrent = mCacheRequestCurrent;
			VideoLoader videoLoader = mVideoLoader;
			if (cacheRequestCurrent == null || videoLoader == null) {
			    return;
			}
		    NotifyProgressData notifyProgressData =
		        mRefNotifyProgressData.getAndSet(null);
		    if (notifyProgressData == null) {
		        // 前の値で更新処理中：今回は飛ばす
		        return;
		    }
		    notifyProgressData.videoNumber = cacheRequestCurrent.videoNumber;
		    notifyProgressData.isLow = videoLoader.isLow();
		    notifyProgressData.seekOffsetWrite = seekOffsetWrite;
		    notifyProgressData.contentLength = contentLength;
		    mHandler.obtainMessage(MSG_ID_VIDEO_LOADER_NOTIFY_PROGRESS,
		            notifyProgressData).sendToTarget();
		}
		@Override
		public void onOccurredError(VideoLoader streamLoader,
				String errorMessage) {
			if (DEBUG_LOGV) {
				Log.v(LOG_TAG, "VideoCacheService onOccurredError");
			}
	    	// 開始時のNotification削除
			CacheRequest cacheRequestCurrent = mCacheRequestCurrent;
//			assert cacheRequestCurrent != null;
			if (cacheRequestCurrent != null) {
				NotificationManager notificationManager =
					(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
				notificationManager.cancel(cacheRequestCurrent.notificationIDStartVideoCache);

				mCacheRequestCurrent = null;
			}
			mHandler.obtainMessage(
					MSG_ID_VIDEO_LOADER_OCCURRED_ERROR, errorMessage)
					.sendToTarget();
		}
	};

	@Override
	public void onCreate() {
		super.onCreate();

//		mHttpClient = Util.createHttpClient();
	}

	@Override
	public void onStart(Intent intent, int startId) {
		if (DEBUG_LOGV) {
			Log.v(LOG_TAG, "VideoCacheService onStart");
		}
		super.onStart(intent, startId);
//		startFirst(intent);
		// 作成済みbinderの処理が必要なため処理遅らせる
		mHandler.obtainMessage(MSG_ID_START_FIRST, intent).sendToTarget();
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		VideoLoader videoLoader = mVideoLoader;
    	if (videoLoader != null) {
    		videoLoader.finish();
    		mVideoLoader = null;
    	}
//    	mHttpClient = null;
		NotificationManager notificationManager =
			(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//		notificationManager.cancel(NOTIFICATION_ID_START_VIDEO_CACHE);
		for (int i = 0; i < mNotificationIDCounter; i += 2) {
			notificationManager.cancel(i);
		}
	}

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

	@Override
	public IBinder onBind(Intent intent) {
		if (DEBUG_LOGV) {
			Log.v(LOG_TAG, "VideoCacheService onBind");
		}
//		startFirst(intent);
		// 作成済みbinderの処理が必要なため処理遅らせる
		mHandler.obtainMessage(MSG_ID_START_FIRST, intent).sendToTarget();
		return mBinder;
	}

	@Override
	public void onRebind(Intent intent) {
		if (DEBUG_LOGV) {
			Log.v(LOG_TAG, "VideoCacheService onRebind");
		}
		super.onRebind(intent);
	}

	@Override
	public boolean onUnbind(Intent intent) {
		return super.onUnbind(intent);
//		return true;
	}

    @Override
    protected void finalize() throws Throwable {
    	try {
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, "VideoCacheService#finalize start");
            }
    		super.finalize();
    	} finally {
    		VideoLoader videoLoader = mVideoLoader;
        	if (videoLoader != null) {
        		videoLoader.finish();
        	}
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, "VideoCacheService#finalize end");
            }
    	}
    }

    private void startFirst(Intent intent) {
    	assert mCacheRequestCurrent == null;
    	CacheRequest cacheRequest = createCacheRequest(
    			intent.getStringExtra(
    	    			AbstractNicoroPlayer.INTENT_NAME_VIDEO_NUMBER),
    			intent.getStringExtra(
    	    			INTENT_NAME_COOKIE_USER_SESSION),
    			intent.getBooleanExtra(
    	    			AbstractNicoroPlayer.INTENT_NAME_FORCE_LOW, false));
    	startCacheRequestCurrent(cacheRequest);
    }

    private void startCacheRequestCurrent(CacheRequest cacheRequest) {
		try {
	    	assert cacheRequest != null;
			mCacheRequestCurrent = cacheRequest;
			VideoLoader videoLoader = createVideoLoader(
					cacheRequest, getApplicationContext());
			mVideoLoader = videoLoader;
			if (videoLoader == null) {
				// TODO エラー処理
				if (DEBUG_LOGD) {
					Log.d(LOG_TAG, "createVideoLoader failed");
				}
				Util.showErrorToast(getApplicationContext(),
						R.string.toast_cache_fail_start);
				mCacheRequestCurrent = null;
				return;
			}
			videoLoader.setEventListener(mVideoLoaderEventListener);
			videoLoader.startLoad();

			// Notificationでキャッシュ中の表示
			setNotificationStartCache(cacheRequest);

			final int numCallbacks = mCallbacks.beginBroadcast();
			for (int i = 0; i < numCallbacks; ++i) {
				try {
					mCallbacks.getBroadcastItem(i).onStart(
							cacheRequest.videoNumber,
							videoLoader.isLow());
				} catch (RemoteException e) {
					Log.e(LOG_TAG, e.toString(), e);
				}
			}
			mCallbacks.finishBroadcast();
		} catch (ClientProtocolException e) {
			// TODO エラー処理
			Log.e(LOG_TAG, e.toString(), e);
			Util.showErrorToast(getApplicationContext(),
					R.string.toast_cache_fail_start);
			mCacheRequestCurrent = null;
		} catch (IOException e) {
			// TODO エラー処理
			Log.e(LOG_TAG, e.toString(), e);
			Util.showErrorToast(getApplicationContext(),
					R.string.toast_cache_fail_start);
			mCacheRequestCurrent = null;
		}
    }

    private void setNotificationStartCache(CacheRequest cacheRequest) {
		// とりあえずブラウザ画面起動
		Intent intent = new Intent(
				Intent.ACTION_VIEW,
				Uri.parse("http://www.nicovideo.jp/watch/" + cacheRequest.videoNumber),
				getApplicationContext(), NicoroWebBrowser.class);
		intent.addCategory(Intent.CATEGORY_BROWSABLE);

		int resTitle;
        if (cacheRequest.forceLow) {
            resTitle = R.string.notification_title_start_cache_force_low;
        } else {
            resTitle = R.string.notification_title_start_cache;
        }
        String title = getString(resTitle,
                cacheRequest.videoNumber);
		Notification notification = new Notification(
				android.R.drawable.stat_notify_sync,
//				R.drawable.icon,
//				0,
				title,
				System.currentTimeMillis());
//		notification.flags = Notification.FLAG_ONGOING_EVENT;
//		Intent intentNotification = new Intent(getApplicationContext(), NicoroWebBrowser.class);
		PendingIntent pendingIntent = PendingIntent.getActivity(
				getApplicationContext(),
				0,
//				intentNotification,
				intent,
//				PendingIntent.FLAG_NO_CREATE);
				PendingIntent.FLAG_UPDATE_CURRENT);
		notification.setLatestEventInfo(getApplicationContext(),
				title,
				getString(R.string.notification_text_start_cache,
				        cacheRequest.videoNumber),
				pendingIntent/*null*/);
		NotificationManager notificationManager =
			(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		notificationManager.notify(
				cacheRequest.notificationIDStartVideoCache,
				notification);
    }

    private void setNotificationFinishCache(CacheRequest cacheRequest) {
    	// 開始時のNotification削除
		NotificationManager notificationManager =
			(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
		notificationManager.cancel(cacheRequest.notificationIDStartVideoCache);

		// とりあえずブラウザ画面起動
		Intent intent = new Intent(
				Intent.ACTION_VIEW,
				Uri.parse("http://www.nicovideo.jp/watch/" + cacheRequest.videoNumber),
				getApplicationContext(), NicoroWebBrowser.class);
		intent.addCategory(Intent.CATEGORY_BROWSABLE);

        int resTitle;
        if (cacheRequest.forceLow) {
            resTitle = R.string.notification_title_finish_cache_force_low;
        } else {
            resTitle = R.string.notification_title_finish_cache;
        }
		String title = getString(resTitle, cacheRequest.videoNumber);
		Notification notification = new Notification(
				R.drawable.icon,
				title,
				System.currentTimeMillis());
		PendingIntent pendingIntent = PendingIntent.getActivity(
				getApplicationContext(),
				0,
				intent,
				PendingIntent.FLAG_UPDATE_CURRENT);
		notification.setLatestEventInfo(getApplicationContext(),
				title,
				getString(R.string.notification_text_finish_cache,
				        cacheRequest.videoNumber),
				pendingIntent);
//		notification.flags = Notification.FLAG_NO_CLEAR;
		notification.flags = Notification.FLAG_AUTO_CANCEL;
		notificationManager.notify(
				cacheRequest.notificationIDFinishVideoCache,
				notification);
    }

//	private VideoLoader createVideoLoader(Intent intent,
//			Context context) throws ClientProtocolException, IOException {
////        String url = intent.getStringExtra(INTENT_NAME_VIDEO_URL);
//        String videoNumber = intent.getStringExtra(AbstractNicoroPlayer.INTENT_NAME_VIDEO_NUMBER);
////        String cookieNicoHistory = intent.getStringExtra(INTENT_NAME_COOKIE_NICO_HISTORY);
//        String cookieUserSession = intent.getStringExtra(INTENT_NAME_COOKIE_USER_SESSION);
//        boolean forceLow = intent.getBooleanExtra(AbstractNicoroPlayer.INTENT_NAME_FORCE_LOW, false);
//        return createVideoLoader(videoNumber, cookieUserSession,
//        		forceLow, context);
//	}

	private VideoLoader createVideoLoader(CacheRequest cacheRequest,
			Context context) throws ClientProtocolException, IOException {
        return createVideoLoader(cacheRequest.videoNumber,
        		cacheRequest.cookieUserSession,
        		cacheRequest.forceLow,
        		context);
	}

	private VideoLoader createVideoLoader(String videoNumber,
			String cookieUserSession, boolean forceLow,
			Context context) throws ClientProtocolException, IOException {

		DefaultHttpClient httpClient = Util.createHttpClient();
		httpClient.getCookieStore().clear();
        String cookieNicoHistory = NicoroAPIManager.getCookieNicoHistory(
        		httpClient, videoNumber, cookieUserSession, forceLow, null);

		httpClient.getCookieStore().clear();
		NicoroAPIManager.ParseGetFLV parseGetFLV;
		if (forceLow) {
			parseGetFLV = new NicoroAPIManager.ParseGetFLVLow();
		} else {
			parseGetFLV = new NicoroAPIManager.ParseGetFLV();
		}
		parseGetFLV.initialize(httpClient,
				videoNumber,
				cookieUserSession, null);
		final String getflvUrl = parseGetFLV.getUrl(forceLow);
        if (DEBUG_LOGD) {
        	Log.d(LOG_TAG, Log.buf().append(" url=").append(getflvUrl)
        			.append(" videoNumber=").append(videoNumber)
        			.append(" cookieNicoHistory=").append(cookieNicoHistory)
        			.append(" cookieUserSession=").append(cookieUserSession).toString());
        }
		VideoLoader videoLoader = null;
        if (getflvUrl != null && cookieNicoHistory != null) {
        	videoLoader = new VideoLoader(getflvUrl, cookieNicoHistory, videoNumber,
        			context, cookieUserSession);
        }
		return videoLoader;
	}
}
