package jp.agentec.abook.abv.bl.download;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;

import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.db.SQLiteOpenHelper;
import jp.agentec.abook.abv.bl.common.db.impl.JDBCSQLiteOpenHelper;
import jp.agentec.abook.abv.bl.common.exception.ABVException;
import jp.agentec.abook.abv.bl.common.exception.NetworkDisconnectedException;
import jp.agentec.abook.abv.bl.common.nw.PCNetworkAdapter;
import jp.agentec.abook.abv.bl.data.ABVDataCache;
import jp.agentec.abook.abv.bl.data.DBConnector;
import jp.agentec.abook.abv.bl.data.dao.AbstractDao;
import jp.agentec.abook.abv.bl.data.dao.ContentDao;
import jp.agentec.abook.abv.bl.dto.ContentDto;
import jp.agentec.abook.abv.bl.logic.AbstractLogic;
import jp.agentec.abook.abv.bl.logic.ContentLogic;
import jp.agentec.adf.net.http.HttpDownloadSimpleNotification;
import jp.agentec.adf.net.http.HttpRequestSender;

import org.junit.BeforeClass;
import org.junit.Test;

/**
 * 
 * 注： test.dbはContentRefresherTestを行ったものを使用し、実際のsidをセットすること
 * 
 */
public class ContentDownloaderTest {
	private static long basicContentId = 1431; // DLのテストに使用するコンテンツID
	private static String sid = "4475bbc5a74a1029b35ab54f06727647";
	private ContentDownloader contentDownloader = ContentDownloader.getInstance();
	
	@BeforeClass
	public static void setup() {
		DBConnector conn = DBConnector.getInstance();
		SQLiteOpenHelper sqlLiteOpenHelper = new JDBCSQLiteOpenHelper("test.db", 0);
		conn.setSqlLiteOpenHelper(sqlLiteOpenHelper);
		((JDBCSQLiteOpenHelper) sqlLiteOpenHelper).onCreate(conn.getDatabase());

		ABVEnvironment.getInstance().networkAdapter = new PCNetworkAdapter();
		ABVDataCache.getInstance().getMemberInfo().sid = sid;
		ABVEnvironment.getInstance().acmsAddress = "https://web5.abook.bz/acms";
		ABVEnvironment.getInstance().downloadServerAddress = "https://web5.abook.bz/acms";
		HttpRequestSender.testUserAgent = "Android";
		ABVEnvironment.getInstance().rootDirectory = "/var/android";
		ABVEnvironment.getInstance().cacheDirectory= "/var/android/cache";
	}

	ContentDownloadListener contentDownloadListener = new ContentDownloadListener() {
		@Override
		public void onRefreshedContent(boolean result, long contentId, Exception e) {
			System.out.printf("onRefreshedContent result=%s, contentId=%s, e=%s", result, contentId, e);
		}
		
		@Override
		public void onDownloadingContentZip(ContentZipDownloadNotification notification) {
			System.out.println("onDownloadingContentZip " + notification);
		}
		
		@Override
		public void onDownloadedContentDetail(HttpDownloadSimpleNotification notification) {
			System.out.println("onDownloadedContentDetail " + notification);
		}
		
		@Override
		public void onAuthenticationFailed() {
			System.out.println("onAuthenticationFailed");
		}
	};

	@Test
	public void testAll() throws NetworkDisconnectedException, Exception {
		deleteAllContent();
		contentDownloader.addContentDownloadListener(contentDownloadListener);
		contentDownloader.setDownloadingPercentageNotifyInterval(500);
		
		///// 基本パターン ////
		// DL (immediate)
		downloadImmediate();
		// DL (reserve) - kickTask
		downloadReserve();
		downloadReserve2();
		// DL - pause - resume
		dlPauseResume();
		// DL - auto pause - kickTask 
		dlAutoPauseKick();
		// DL - cancle
		dlCancel();
		// DL - pause cancel
		dlPauseCancle();
		// DL - pause - resume - cancel
		dlPauseResumeCancle();
		// DL - cancle - DL
		dlCancleDl();
		// DL -(NW接続エラー)pause - resume
		dlNwError();
		// DL -(freeze)cancel
		dlFreezeCancel();
		// DL - (error) 
		dlError();
		
		//// 排他制御 ////
		// DL * 10
		dlSyncDl();
		// DL - pause * 10
		dlSyncDlPause();
		// DL - pause - resume * 10
		dlSyncDlPauseResume();
		// DL - resume
		dlSyncDlResume();
		
		//// 複数同時並行 ////
		dlConcurrent();
		
		//// 自動DL ////
		autoDownload();
		
		//// ContentInfo ////
		downloadContentInfo();
		
		//// ContentDetail ////
		downloadContentDetail();
		
		// 他
//		test();
		
		// 全部終わるまで待機
		while (contentDownloader.getActiveCount() > 0) {
			sleep(100);
		}
	}
	
	private void deleteAllContent() {
		for (ContentDto dto : AbstractDao.getDao(ContentDao.class).getAllContents()) {
			AbstractLogic.getLogic(ContentLogic.class).deleteContent(dto, false);
		}
	}

	private void downloadImmediate() throws NetworkDisconnectedException, ABVException {
		contentDownloader.download(basicContentId);
	}

	private void downloadReserve() {
		contentDownloader.reserveDownload(basicContentId, true);
	}

	private void downloadReserve2() throws InterruptedException {
		contentDownloader.reserveDownload(basicContentId, false);
		sleep(2000);
		contentDownloader.kickTask();
	}

	private void dlPauseResume() throws NetworkDisconnectedException, ABVException {
		contentDownloader.download(basicContentId);
		sleep(2000);
		
		contentDownloader.pause(basicContentId, false);
		sleep(3000);
		
		contentDownloader.resume(basicContentId);
	}

	private void dlAutoPauseKick() throws NetworkDisconnectedException, ABVException {
		contentDownloader.download(basicContentId);
		sleep(2000);
		
		contentDownloader.pauseAll();
		sleep(3000);
		
		contentDownloader.kickTask();
		sleep(3000);
	}


	private void dlCancel() throws NetworkDisconnectedException, ABVException {
		contentDownloader.download(basicContentId);
		sleep(2000);
		
		contentDownloader.cancel(basicContentId);
	}


	private void dlPauseCancle() throws NetworkDisconnectedException, ABVException {
		contentDownloader.download(basicContentId);
		sleep(2000);
		
		contentDownloader.pause(basicContentId, true);
		sleep(2000);

		contentDownloader.cancel(basicContentId);
	}


	private void dlPauseResumeCancle() throws NetworkDisconnectedException, ABVException {
		contentDownloader.download(basicContentId);
		sleep(2000);
		
		contentDownloader.pause(basicContentId, false);
		sleep(2000);

		contentDownloader.resume(basicContentId);
		sleep(2000);
		
		contentDownloader.cancel(basicContentId);
	}


	private void dlCancleDl() throws NetworkDisconnectedException, ABVException {
		contentDownloader.download(basicContentId);
		sleep(2000);
		contentDownloader.cancel(basicContentId);
		sleep(2000);
		contentDownloader.download(basicContentId);
		sleep(2000);
		contentDownloader.cancel(basicContentId);
		sleep(2000);
		contentDownloader.reserveDownload(basicContentId, true);
	}


	private void dlFreezeCancel() throws NetworkDisconnectedException, ABVException {
		contentDownloader.download(basicContentId);
		System.out.println("この間にNWケーブルを抜くこと！！！！");
		sleep(10000); // pause状態になっていることを確認
		
		contentDownloader.resume(basicContentId);
	}

	private void dlNwError() throws NetworkDisconnectedException, ABVException {
		// DL中にエラーが発生するように仕込む（Exceptionをスローするようにする）
		contentDownloader.download(basicContentId);
		// pause状態になっていることを確認
	}

	private void dlError() throws NetworkDisconnectedException, ABVException, IOException {
		// DL中にエラーが発生するように仕込む（downloadのファイルをあらかじめおいて別のプロセスで開いておく）
		File file = new File(ABVEnvironment.getInstance().rootDirectory + "/ABook/downloads/ContentDownload_1431.zip");
		FileOutputStream fos = new FileOutputStream(file);
		try {
			FileChannel fc = fos.getChannel();
			FileLock lock = fc.tryLock();
			try {
				if (lock == null) {// 他プロセスロック中
					throw new RuntimeException();
				}

				contentDownloader.download(basicContentId);

			} finally {
				if (lock != null) {
					lock.release(); // JDK1.7以降はclose()でも可[2014-07-19]
				}
			}
		} finally {
			fos.close();
		}
	}


	private void dlSyncDl() throws NetworkDisconnectedException, ABVException {
		contentDownloader.download(basicContentId);
		contentDownloader.download(basicContentId);
		contentDownloader.download(basicContentId);
		contentDownloader.download(basicContentId);
		sleep(1000);
		contentDownloader.download(basicContentId);
		sleep(1000);
		contentDownloader.download(basicContentId);
		sleep(1000);
		contentDownloader.download(basicContentId);
		sleep(1000);
		contentDownloader.download(basicContentId);
		sleep(1000);
		contentDownloader.download(basicContentId);
		contentDownloader.download(basicContentId);
		// 最初のみ処理されることをログで確認
	}


	private void dlSyncDlPause() throws NetworkDisconnectedException, ABVException {
		contentDownloader.download(basicContentId);
		contentDownloader.pause(basicContentId, false);
		contentDownloader.pause(basicContentId, false);
		contentDownloader.pause(basicContentId, false);
		contentDownloader.pause(basicContentId, false);
		contentDownloader.pause(basicContentId, false);
		sleep(1000);
		contentDownloader.pause(basicContentId, false);
		sleep(1000);
		contentDownloader.pause(basicContentId, false);
		sleep(1000);
		contentDownloader.pause(basicContentId, false);
		sleep(1000);
		contentDownloader.pause(basicContentId, false);
		sleep(1000);
		contentDownloader.pause(basicContentId, false);
		// 最初のみ処理されることをログで確認
	}


	private void dlSyncDlPauseResume() throws NetworkDisconnectedException, ABVException {
		contentDownloader.download(basicContentId);
		contentDownloader.pause(basicContentId, false);
		contentDownloader.resume(basicContentId);
		contentDownloader.resume(basicContentId);
		contentDownloader.resume(basicContentId);
		contentDownloader.resume(basicContentId);
		contentDownloader.resume(basicContentId);
		sleep(1000);
		contentDownloader.resume(basicContentId);
		sleep(1000);
		contentDownloader.resume(basicContentId);
		sleep(1000);
		contentDownloader.resume(basicContentId);
		sleep(1000);
		contentDownloader.resume(basicContentId);
		sleep(1000);
		contentDownloader.resume(basicContentId);
		sleep(1000);
		contentDownloader.resume(basicContentId);
		// 最初のみ処理されることをログで確認
	}


	private void dlSyncDlResume() throws NetworkDisconnectedException, ABVException {
		contentDownloader.download(basicContentId);
		contentDownloader.resume(basicContentId);
		contentDownloader.resume(basicContentId);
		contentDownloader.resume(basicContentId);
		contentDownloader.resume(basicContentId);
		contentDownloader.resume(basicContentId);
		sleep(1000);
		contentDownloader.resume(basicContentId);
		sleep(1000);
		contentDownloader.resume(basicContentId);
		sleep(1000);
		contentDownloader.resume(basicContentId);
		sleep(1000);
		contentDownloader.resume(basicContentId);
		sleep(1000);
		contentDownloader.resume(basicContentId);
		sleep(1000);
		contentDownloader.resume(basicContentId);
		// 最初のみ処理されることをログで確認
	}


	private void dlConcurrent() throws NetworkDisconnectedException, ABVException {
		contentDownloader.setMaximumDownloadable(1);
//		contentDownloader.setMaximumDownloadable(4); // 複数並行にするとローカルのsqlite絡みでJavaのレベルで落ちることがある
		contentDownloader.reserveDownload(basicContentId, false);
		contentDownloader.reserveDownload(1430, false);
		contentDownloader.reserveDownload(1432, false);
		contentDownloader.reserveDownload(1433, false);
		contentDownloader.reserveDownload(1447, false);
		contentDownloader.reserveDownload(1448, false);
		contentDownloader.reserveDownload(1449, false);
		contentDownloader.reserveDownload(1450, true);
	}

	private void autoDownload() throws NetworkDisconnectedException, ABVException {
		deleteAllContent();
		ContentDao contentDao = AbstractDao.getDao(ContentDao.class);
		for (ContentDto dto : contentDao.getAllContents()) {
			dto.disableAutoDl = false;
			contentDao.updateContent(dto, false);
		}
		contentDownloader.setAutoDownload(true);
		contentDownloader.addAutoDownload(basicContentId);
		contentDownloader.addAutoDownload(1432);
		contentDownloader.addAutoDownload(1430);
		contentDownloader.addAutoDownload(1433);
		contentDownloader.autoDownload();
		
		sleep(5000);
	}

	private void downloadContentInfo() throws NetworkDisconnectedException, ABVException, Exception {
		contentDownloader.downloadContentInfo(basicContentId);
		sleep(3000);
	}

	private void downloadContentDetail() throws NetworkDisconnectedException {
		contentDownloader.downloadContentDetail(1432); // PDFコンテンツにしないと詳細がない
		sleep(3000);
	}

	public void test() throws NetworkDisconnectedException, Exception {
		contentDownloader.download(basicContentId);
		
		sleep(2000);
		
		contentDownloader.pauseAll();
		contentDownloader.pause(basicContentId, true);
//		contentDownloader.pause(basicContentId, false);
		sleep(3000);
		
		contentDownloader.kickTask();
//		contentDownloader.resume(basicContentId);
		sleep(1000);

//		contentDownloader.cancel(basicContentId);
	}
	
	private void sleep(int msec) {
		try {
			Thread.sleep(msec);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
}
