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

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import jp.agentec.abook.abv.bl.acms.client.AcmsClient;
import jp.agentec.abook.abv.bl.acms.client.json.DownloadedContentInfoJSON;
import jp.agentec.abook.abv.bl.acms.client.json.content.ContentJSON;
import jp.agentec.abook.abv.bl.acms.client.parameters.AcmsContentCheckParameters;
import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.exception.AcmsException;
import jp.agentec.abook.abv.bl.common.exception.NetworkDisconnectedException;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.common.util.ContentFileUtil;
import jp.agentec.abook.abv.bl.data.dao.AbstractDao;
import jp.agentec.abook.abv.bl.data.dao.ContentCategoryDao;
import jp.agentec.abook.abv.bl.data.dao.ContentDao;
import jp.agentec.abook.abv.bl.data.dao.ContentGroupDao;
import jp.agentec.abook.abv.bl.data.dao.ContentMarkingDao;
import jp.agentec.abook.abv.bl.data.dao.ContentPageDao;
import jp.agentec.abook.abv.bl.data.dao.ContentResourceDao;
import jp.agentec.abook.abv.bl.data.dao.ContentTagDao;
import jp.agentec.abook.abv.bl.dto.ContentDto;
import jp.agentec.abook.abv.bl.dto.ContentPageDto;
import jp.agentec.abook.abv.bl.dto.ContentTagDto;
import jp.agentec.abook.abv.bl.dto.comparator.ContentPageDtoComparator;
import jp.agentec.adf.util.FileUtil;
import jp.agentec.adf.util.StringUtil;

/**
 * @author Taejin Hong
 * @version 1.1.3
 */
public class ContentLogic extends AbstractLogic {
	private static final String TAG = "ContentLogic";
	/**
	 * コンテンツ情報入力用のDAO<br>
	 * SQLiteがマルチスレッド環境下でlockされることがある。今のところSQLiteの問題がロジックの問題なのかは判明してないが、<br>
	 * WEBの情報によると、同じ問題を訴える声が結構見当たる。<br>
	 * 問題の判明と解決には時間がかなり掛かりそうなので、スレッドを同期化させるために、DAOをstaticで持つことにした。
	 * @since 1.0.0
	 */
	private ContentDao contentDao = AbstractDao.getDao(ContentDao.class);
	private ContentCategoryDao contentCategoryDao = AbstractDao.getDao(ContentCategoryDao.class);
	private ContentTagDao contentTagDao = AbstractDao.getDao(ContentTagDao.class);
	private ContentPageDao contentPageDao = AbstractDao.getDao(ContentPageDao.class);
	private ContentResourceDao contentResourceDao = AbstractDao.getDao(ContentResourceDao.class);
	
	/**
	 * {@link ContentLogic} クラスのインスタンスを初期化します。
	 * context　Androidとの互換性の為のプロパティです。Androidの android.content.Context のインスタンスを指定します。<br>
	 * UIがAndroidでない場合、何かのオブジェクトを指定しても、nullと見なします。
	 * @see AbstractLogic
	 * @since 1.0.0
	 */
	/*package*/ ContentLogic() {
	}

	/**
	 * お気に入り情報の存在を返します。
	 * @return　お気に入り情報の存在を返します。
	 * @param contentId コンテンツIDです。
	 * @throws Exception その他、例外です。
	 * @since 1.0.0
	 */
	public boolean isExsitContentFavorite(long contentId) {
		return contentDao.getFavorite(contentId);
	}

	/**
	 * コンテンツ内検索結果情報を返します。
	 * @return　検索結果情報のリストを返します。
	 * @param contentId コンテンツIDです。
	 * @param searchKeyword 検索文字列です。
	 * @throws Exception その他、例外です。
	 * @since 1.0.0
	 */
	public List<ContentPageDto> getContentPage(long contentId, String searchKeyword) {
		List<ContentPageDto> list = contentPageDao.getContentPages(contentId, searchKeyword);
		if (list != null && list .size() > 0) {
			int count = 0; // 同一ページは一つしか結果を返していないのでこれは不要だった。とりあえず将来的改修を踏まえて残す。
			int prevPage = -1;
			for (ContentPageDto contentPageDto : list) {
				if (contentPageDto.pageNum != prevPage) {
					prevPage = contentPageDto.pageNum;
					count = 0;
				}
				count++;
				
				int start = 0;
				for (int i = 0; i < count; i++) {
					start = contentPageDto.pageText.toLowerCase(Locale.US).indexOf(searchKeyword.toLowerCase(Locale.US), start); // 同一ページに複数ある場合、何度もこのロジックが回るが。
					if (start == -1) {
						break;
					}
				}
				
				if (start != -1) {
					start -= 10;
					if (start < 0) {
						start = 0;
					}
					int maxByte = 40; // 最大全角20文字、半角40文字
					StringBuffer sb = new StringBuffer();
					while (start < contentPageDto.pageText.length() && maxByte > 0) {
						String s = contentPageDto.pageText.substring(start, start + 1);
						maxByte -= (s.getBytes().length == 1? 1: 2);
						sb.append(s);
						start++;
					}
					contentPageDto.searchResult = sb.toString().replace("\n", " ");
				}
				
				contentPageDto.pageThumbnailPath = ContentFileUtil.getPdfThumbnailPath(contentPageDto.contentId, contentPageDto.pageNum, ContentFileUtil.SIZE_S);
			}
		}
		return list;
	}

	public void saveContentInfo(String savePath) throws Exception {
		saveContentInfo(savePath, null, null, false);
	}

	/**
	 * 
	 * ContentInfoを保存する
	 * 
	 * @param savePath
	 * @param contentId Readerのときにセットする
     * @param downloadedFlg
	 * @param noUpdateFlg
     *
	 * @throws FileNotFoundException
	 * @throws IOException
	 * @throws AcmsException 
	 */
	public void saveContentInfo(String savePath, Long contentId, Boolean downloadedFlg, boolean noUpdateFlg) throws IOException, AcmsException {
		String contentInfoFilePath = savePath + ABVEnvironment.ContentVersionInfoJsonName;
			
		DownloadedContentInfoJSON json = ContentFileUtil.getDownloadedContentInfoJSON(contentInfoFilePath);
		Logger.d(TAG, "[saveContentInfo]:DownloadedContentInfoJSON=" + json.toString());
		synchronized(contentDao) {
			try {
				boolean update = false;
				ContentDto dto = contentDao.getContent(contentId==null? json.contentId: contentId); // Reader
				
				if (dto != null) {
					dto.allPageNum = json.allPageNum;
					dto.orientation = json.orientation;
					dto.categoryId = json.categoryId;
					dto.contentDetail = json.contentDetail;
					dto.contentName = json.contentName;
					dto.contentNameKana = json.contentNameKana;
					dto.deliveryEndDate = json.deliveryEndDate;
					dto.deliveryStartDate = json.deliveryStartDate;
					if (contentId == null) {
						dto.setGroupIds(json.groupIds);
					}
					dto.metaVersion = json.metaVersion;
					if(dto.resourceVersion != json.resourceVersion) {
						dto.updatedFlg = true;
					}
					dto.resourceVersion = json.resourceVersion;
					dto.tags = json.tags;
					dto.lastDeliveryDate = json.lastDeliveryDate;
					dto.contentType = json.contentType;
					dto.contractContentId = json.contractContentId;
					dto.contentSize = json.contentSize;
					dto.alertMessageLevel = json.alertMessageLevel;
					dto.alertMessage = json.alertMessage;
					dto.productId = json.productId;
					dto.deliveryType = json.deliveryType;
					dto.disableSwipe = json.disableSwipe;
					dto.browserType = json.browserType;
					dto.commonContentFlg = json.commonContentFlg;

					if (downloadedFlg != null) {
						dto.downloadedFlg = downloadedFlg;
					}
					
					//2012/06/11　昔
					dto.newFlg = true;

					update = true;
				} else {
					dto = json.convertToDto();
					if (contentId != null) {
						dto.contentId = contentId;
					}
				}
				dto.thumbnailNormalPath = savePath + json.thumbnailName;
				String thumbnailNormalPath2x = dto.thumbnailNormalPath.replace("_THM", "_THM@2x");
				if (FileUtil.exists(thumbnailNormalPath2x)) { // retinaがあれば一律そっちに置き換える
					dto.thumbnailNormalPath = thumbnailNormalPath2x;
				}
				
				dto.thumbnailBigPath = savePath + json.thumbnailBigName;
				
				dto.contentProtectedFlg = ABVEnvironment.getInstance().isContentProtected;
				dto.newFlg = true;
				if (noUpdateFlg) {
					dto.updatedFlg = false;
				}
				
				contentDao.beginTransaction();
				
				if (update) {

					//2012/06/11　昔
					contentPageDao.deleteContentPage(dto.contentId);
					Logger.d(TAG, "[saveContentInfo]:update content : %s", dto.contentId);

					contentDao.updateContent(dto, true);
				} else {
                    Logger.d(TAG, "[saveContentInfo]:insert content : %s", dto.contentId);
					contentDao.insertContent(dto);
				}

				if (contentId == null) { // Reader
					AbstractDao.getDao(ContentGroupDao.class).deleteContentGroup(dto.contentId);
					for (int groupId : dto.groupIds) {
						AbstractDao.getDao(ContentGroupDao.class).insertContentGroup(groupId, dto.contentId);
					}
				}
				for (String tag : dto.tags) {
					if (!StringUtil.isNullOrWhiteSpace(tag)) {
						ContentTagDto contentTagDto = new ContentTagDto();
						contentTagDto.contentId = dto.contentId;
						contentTagDto.tagName = tag;

						contentTagDao.insertContentTag(contentTagDto);
					}
				}
				
				if (contentId == null) { // Reader
					contentCategoryDao.deleteContentCategory(dto.contentId);
					contentCategoryDao.insertContentCategory(dto.categoryId, dto.contentId);
				}
				
				contentDao.commit();
				Logger.d(TAG, "[saveContentInfo]:downloaded content info, id : %s", dto.contentId);
			} catch (Exception e) {
				contentDao.rollback();
				throw new RuntimeException(e);
			} finally {
			}
		}
		//20120612昔
		//FileUtil.delete(contentInfoFilePath);
	}
	
	public void saveContentDetail(long contentId, String contentDetailPath) throws IOException {
		File dir = new File(contentDetailPath);
		
		//	大サムネール（nnnn...nnn__THUMBIG.jpg）のみ取得
		File[] bigThumbnail = ContentFileUtil.getBigThumbnails(dir);
			
		//	ページテキストファイル（pageText_n.txt）のみ取得
		File[] pageTexts = ContentFileUtil.getPageTexts(dir);
		
		//	ページのサムネール(page_n.jpg)のみ取得
		File[] pageThumbnails = ContentFileUtil.getPageThumbnails(dir);
		
		String contentInfoPath = FileUtil.getParentPath(contentDetailPath);
		
		if (bigThumbnail != null) {
			for (File file : bigThumbnail) {
				FileUtil.move(file, new File(contentInfoPath, file.getName()), true);
			}
		}
		
		int pageTextsLength = pageTexts != null ? pageTexts.length : 0;
		int pageThumbnailLength = pageThumbnails != null ? pageThumbnails.length : 0;
		
		List<ContentPageDto> contentPages = new ArrayList<ContentPageDto>();
		
		for (int i = 0; i < pageTextsLength; i++) {
			ContentPageDto dto = new ContentPageDto();
			dto.contentId = contentId;
			dto.pageNum = ContentFileUtil.getPageNumFromFileName(FileUtil.getFilenameWithoutExt(pageTexts[i].getName()));
			dto.pageText = ContentFileUtil.getStringFromPageTextFile(pageTexts[i]);
			
			contentPages.add(dto);
		}
		
		//	テキストとサムネールのページを合わせて同じページの　dtoに格納する。
		for (int i = 0; i < pageThumbnailLength; i++) {
            int pageNum = 0;
            if (pageTexts != null) {
                pageNum = ContentFileUtil.getPageNumFromFileName(FileUtil.getFilenameWithoutExt(pageTexts[i].getName()));
            }
            boolean exist = false;
			
			for (ContentPageDto contentPageDto : contentPages) {
				if (contentPageDto.pageNum == pageNum) {
					contentPageDto.pageThumbnailName = pageThumbnails[i].getName();
					exist = true;
					break;
				}
			}
			
			if (!exist) {
				ContentPageDto dto = new ContentPageDto();
				dto.contentId = contentId;
				dto.pageNum = pageNum;
				dto.pageThumbnailName = pageThumbnails[i].getName();
				
				contentPages.add(dto);
			}
		}
		
		try {
			contentPageDao.beginTransaction();
			
			//ページ番号でソート
			Collections.sort(contentPages, new ContentPageDtoComparator());
			
			//	ページ別サムネール保存場所を保存する。
			contentDao.updateContent(contentId, contentPages.size(), contentDetailPath);
			
			List<ContentPageDto> oldList = contentPageDao.getContentPages(contentId);
			
			if (oldList != null) {
				Iterator<ContentPageDto> newPageIterator = contentPages.iterator();
				Iterator<ContentPageDto> oldPageIterator = oldList.iterator();
				
				while (newPageIterator.hasNext()) {
					ContentPageDto newDto = newPageIterator.next();
					
					while (oldPageIterator.hasNext()) {
						ContentPageDto oldDto = oldPageIterator.next();
						if (newDto.equals(oldDto)) {
							contentPageDao.deleteContentPage(contentId, newDto.pageNum);
							contentPageDao.insertContentPages(newDto);
							newPageIterator.remove();
							oldPageIterator.remove();
							break;
						}
					}
				}
			}

			if (contentPages.size() > 0) {
				for (ContentPageDto contentPageDto : contentPages) {
					contentPageDao.insertContentPages(contentPageDto);
				}
			}
			
			contentPageDao.commit();
			
			if (pageTexts != null) {
				for (File f : pageTexts) {
					try {
						f.delete();
					} catch (Exception e) {// ignore
					}
				}
			}
			
			Logger.d(TAG, "downloaded content detail, id : %s", contentId);
		} catch (Exception e) {
			contentPageDao.rollback();
			throw new RuntimeException(e);
		} finally {
		}
	}
	
	
	public void deleteContent(ContentDto dto, boolean deletePhysical) {
//		ContentMarkingService contentMarkingService = new ContentMarkingService();
        Logger.i(TAG, "[deleteContent]: dto=" + dto.contentId);
        if (deletePhysical) {
			FileUtil.delete(dto.pagePath);
			FileUtil.delete(dto.thumbnailBigPath);
			FileUtil.delete(dto.thumbnailNormalPath);
		}
		// 小サムネイルの削除
		ContentFileUtil.deleteContentThumbnailFile(dto.contentId);
		
		String path = String.format(ABVEnvironment.ContentMarkingDirectoryFormat, ABVEnvironment.getInstance().rootDirectory, dto.contentId);
        Logger.d(TAG, "[deleteContent]:child path=" + path);
        FileUtil.deleteChilds(new File(path));
//		contentMarkingService.deleteMarkingFile(env.getAndroidContext() , dto.getContentId());
        Logger.d(TAG, "[deleteContent]:resource path=" + dto.resourcePath);
		FileUtil.delete(dto.resourcePath);
		
		//	キャッシュの削除
		FileUtil.delete(ABVEnvironment.getInstance().getContentCacheDirectory(dto.contentId));

		contentDao.deleteContent(dto.contentId, deletePhysical);
	}
	
	/**
	 * コンテンツのお気に入りフラグを更新します。
	 * @param contentId コンテンツIDです。
	 * @param favorite trueにするとお気に入りコンテンツになります。
	 * @return 更新が成功するとtrueを返します。
	 * @since 1.0.0
	 */
	public boolean setFavoriteContent(long contentId, boolean favorite) {
		return contentDao.updateContentFavoriteFlg(contentId, favorite);
	}
	
	public int getContentCheckDeliverable(long contentId, int resourceVersion) throws AcmsException, NetworkDisconnectedException {
		AcmsContentCheckParameters contentCheckParams = new AcmsContentCheckParameters(cache.getMemberInfo().sid, contentId, resourceVersion);
		AcmsClient acmsClient = AcmsClient.getInstance(cache.getUrlPath(), networkAdapter);
		return acmsClient.contentCheckDeliverable(contentCheckParams);
	}

	public void deleteContentMarkingData() {
		ContentMarkingDao contentMarkingDao = AbstractDao.getDao(ContentMarkingDao.class);
		List<ContentDto> localContents = contentDao.getAllContents(); // TODO: later contentIdのリストだけでよいはず
		if (localContents != null) {
			for (ContentDto localDto : localContents) {
				String path = String.format(ABVEnvironment.ContentMarkingDirectoryFormat, ABVEnvironment.getInstance().rootDirectory, localDto.contentId);
				FileUtil.deleteChilds(new File(path));
				contentMarkingDao.deleteMarking(localDto.contentId);
			}
		}
	}
	
	public void deleteAllContent(boolean isAllDelete) {
		List<ContentDto> localContents;
		if (isAllDelete) {
			localContents = contentDao.getAllContents();
		} else {
			localContents = contentDao.notGetAllContent();
		}
		if (localContents != null) {
			for (ContentDto localDto : localContents) {
				deleteContent(localDto, true);
				contentDao.deleteContentLog(localDto.contentId);
			}
		}
	}

	public String getHtmleFilePath(long contentId, String fileName, boolean isFullScreen) {
		//isFullScreen : 앙케이트의 화면 전체표시유무. 기존 html패스는 true로 넘겨주면 됨.
		File path;
		String htmlPath;
		path = ABVEnvironment.getInstance().getContentCacheDirectory(contentId);
		String directoryPath = path.toString();
		
		//앙케이트 부분표시화면일때만 index_s.html 파일 존재하는지 확인
		if (!isFullScreen) {
			String htmlsPath = directoryPath+"/"+fileName+"/index_s.html";
			File file = new File(htmlsPath);
			if (file.isFile()) {
				htmlsPath = "file://" + htmlsPath;
				return htmlsPath;
			}
		}
		
		htmlPath = directoryPath+"/"+fileName+"/index.html";
		
		boolean fileCheckFlg = false; 
		File file = new File(htmlPath);
        if (file.isFile()) {
            fileCheckFlg = true;
        } else {
            htmlPath = directoryPath + "/" + fileName + "/index.htm";
            File file2 = new File(htmlPath);
            if (file2.isFile()) {
                fileCheckFlg = true;
            } else {
                htmlPath = StringUtil.Empty;
            }
        }
		if (fileCheckFlg) {
			htmlPath = "file://" + htmlPath;
		}
		return htmlPath;
	}
	
	public String getHtmlImagePath(long contentId, String fileName) {
		//다운로드 받아진 HTML파일의 패스
		String directoryPath = ABVEnvironment.getInstance().getContentCacheDirectoryPath(contentId) + "/" + fileName;
		Logger.d(TAG, "getHtmlImagePath : %s", directoryPath);
		File dirPath = new File(directoryPath);
		String imgPath = StringUtil.Empty;
		File imgFiles[] = dirPath.listFiles();
		if (imgFiles != null) {
			for (int i=0; i<imgFiles.length; i++) {
				if (StringUtil.contains(imgFiles[i].getName(),
						new String[]{"thumbnail.png","thumbnail.jpg","T.png","T.jpg","t.png","t.jpg"})) {
					imgPath = directoryPath + "/" + imgFiles[i].getName();
					break;
				}
			}
		}
		
		return imgPath;
	}
	
	
	/**
	 * 指定コンテンツIDのJSON情報を取得する
	 * @param contentId コンテンツID
	 * @return ContentJSON
	 * @throws IOException 
	 */
	public ContentJSON getContentInfoJson(String contentDir, long contentId) throws IOException {
		// 文字化けが発生するので文字コードをUTF-8で指定してファイルを読み込むように修正
		String text = FileUtil.readTextFileByUTF8(contentDir + "/" + contentId + ".json");
		if (StringUtil.isNullOrEmpty(text)) {
			return null;
		}
		return new ContentJSON(text);
	}
}
