package charactermanaj.model.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import charactermanaj.model.CharacterData;

/**
 * ディレクトリをアーカイブと見立てる
 * @author seraphy
 */
public class CharacterDataDirectoryFile extends AbstractCharacterDataArchiveFile {
	
	/**
	 * ロガー
	 */
	private static final Logger logger = Logger.getLogger(CharacterDataDirectoryFile.class.getName());

	/**
	 * 対象ディレクトリ
	 */
	protected File baseDir;
	
	/**
	 * ディレクトリ上のファイルコンテンツ
	 * 
	 * @author seraphy
	 */
	protected static class DirFileContent implements FileContent {

		/**
		 * ディレクトリ名 + ファイル名からなるエントリ名.<br>
		 * エントリ名の区切り文字は常に「/」とする.<br>
		 */
		private String entryName;

		/**
		 * 実際のファイルへのパス
		 */
		private File entry;
		
		protected DirFileContent(File entry, String entryName) {
			this.entry = entry;
			this.entryName = entryName;
		}
		
		public String getEntryName() {
			return entryName;
		}
		
		public long lastModified() {
			return entry.lastModified();
		}

		public InputStream openStream() throws IOException {
			return new FileInputStream(entry);
		}
	}
	
	/**
	 * アーカイブファイルをベースとしたURLを返す.<br>
	 * @param name コンテンツ名
	 * @return URL
	 * @throws IOException URLを生成できない場合
	 */
	protected URL getContentURL(String name) throws IOException {
		return new File(baseDir, name).toURL();
	}
	
	/**
	 * エクスポートプロパティを見立てる.<br>
	 * ディレクトリインポートの場合は、エクスポートプロパティは存在しないので、
	 * サブセット = false, プリセット = true, パーツ = true というエクスポート情報があるものと見なす.<br>
	 */
	@Override
	public Properties readExportProp() throws IOException {
		Properties exportInfoProp = new Properties();
		exportInfoProp.setProperty(ExportInfoKeys.EXPORT_SUBSET, "false");
		exportInfoProp.setProperty(ExportInfoKeys.EXPORT_PRESETS, "true");
		exportInfoProp.setProperty(ExportInfoKeys.EXPORT_PARTS_IMAGES, "true");
		return exportInfoProp;
	}
	
	@Override
	public Collection<PartsImageContent> getPartsImageContents(
			CharacterData characterData) {
		if (isOverlapped(characterData)) {
			return Collections.emptyList();
		}
		return super.getPartsImageContents(characterData);
	}
	
	@Override
	public Collection<PartsImageContent> getPartsImageContentsLazy(
			CharacterData characterData) {
		if (isOverlapped(characterData)) {
			return Collections.emptyList();
		}
		return super.getPartsImageContentsLazy(characterData);
	}

	/**
	 * このディレクトリに対してターゲットプロファイルのディレクトリがかぶっているか?
	 * つまり、ターゲット自身のディレクトリをソースとしていないか?
	 * @param characterData ソースプロファイル
	 * @return 被っている場合はtrue、被っていない場合はfalse
	 */
	protected boolean isOverlapped(CharacterData characterData) {
		if (characterData == null) {
			return false;
		}
		URL docBase = characterData.getDocBase();
		if (docBase == null || !"file".equals(docBase.getProtocol())) {
			return false;
		}

		String folderPlace = File.separator;
		String basePath = new File(docBase.getPath()).getParent() + folderPlace;
		String sourcePath = baseDir.getPath() + folderPlace;
		
		// インポートもとディレクトリがインポート先のキャラクター定義のディレクトリを含むか?
		boolean result = basePath.contains(sourcePath);
		logger.log(Level.FINE, "checkOverlapped: " + basePath + " * " + sourcePath + " :" + result);
		
		return result;
	}

	public void close() throws IOException {
		// ディレクトリなのでクローズ処理は必要ない.
	}
	
	public CharacterDataDirectoryFile(File file) throws IOException {
		super(file);
		baseDir = file;
		load(baseDir, "");
		searchRootPrefix();
	}
	
	private void load(File dir, String prefix) {
		if (!dir.exists() || !dir.isDirectory()) {
			// ディレクトリでなければ何もせず戻る
			return;
		}
		
		for (File file : dir.listFiles()) {
			String name = file.getName();
			String entryName = prefix + name;
			if (file.isDirectory()) {
				// エントリ名の区切り文字は常に「/」とする. (ZIP/JARのentry互換のため)
				load(file, entryName + "/");
			} else {
				addEntry(new DirFileContent(file, entryName));
			}
		}
	}

}
