package charactermanaj.model.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.sql.Timestamp;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import charactermanaj.model.CharacterData;
import charactermanaj.util.DirectoryConfig;

/**
 * 最後に使用したデータの使用状況を保存・復元するためのクラス
 *
 * @author seraphy
 */
public final class RecentDataPersistent {

	/**
	 * キャラクターデータの親フォルダごとに保存する、最後に使用したキャラクターデータを保存するファイル名
	 */
	private static final String RECENT_CAHARCTER_INFO_XML = "recent-character.xml";

	/**
	 * ロガー
	 */
	private static final Logger logger = Logger.getLogger(RecentDataPersistent.class.getName());

	/**
	 * シングルトン
	 */
	private static final RecentDataPersistent inst = new RecentDataPersistent();

	/**
	 * プライベートコンストラクタ
	 */
	private RecentDataPersistent() {
		super();
	}

	/**
	 * インスタンスを取得する
	 *
	 * @return インスタンス
	 */
	public static RecentDataPersistent getInstance() {
		return inst;
	}

	/**
	 * キャラクターデータの親フォルダごとに保存する、最後に使用したキャラクターデータを保存するファイル
	 *
	 * @return
	 */
	private File getRecentCharacterXML() {
		File currentCharactersDir = DirectoryConfig.getInstance()
				.getCharactersDir();
		File recentCharacterXML = new File(currentCharactersDir,
				RECENT_CAHARCTER_INFO_XML);
		return recentCharacterXML;
	}

	/**
	 * 最後に使用したキャラクターデータのフォルダ名を親ディレクトリからの相対パスとして保存する.<br>
	 * ただし、書き込み禁止である場合は何もしない.<br>
	 *
	 * @param characterData
	 *            キャラクターデータ
	 * @throws IOException
	 *             書き込みに失敗した場合
	 */
	public boolean saveRecent(CharacterData characterData) throws IOException {
		URI docBase = null;
		if (characterData != null) {
			docBase = characterData.getDocBase();
		}

		String characterDataName = null;
		if (docBase != null) {
			File currentCharactersDir = DirectoryConfig.getInstance()
					.getCharactersDir();
			File characterXml = new File(docBase);
			File characterDir = characterXml.getParentFile();
			// "Foo/Bar/character.xml"の、「Foo」の部分を取得する.
			File baseDir = characterDir.getParentFile();
			if (currentCharactersDir.equals(baseDir)) {
				// "Foo/Bar/character.xml"の「Bar」の部分を取得する.
				// ※ キャラクターデータの親フォルダ上のフォルダ名だけを保存する.(相対パス)
				characterDataName = characterDir.getName();
			}
		}
		Properties props = new Properties();
		props.setProperty("lastUseCharacterData", characterDataName == null
				? ""
				: characterDataName);

		File recentCharacterXML = getRecentCharacterXML();
		if (recentCharacterXML.exists() && !recentCharacterXML.canWrite()) {
			// ファイルが書き込み禁止の場合は何もしない.
			logger.log(Level.FINE, "recent-character.xml is readonly.");
			return false;
		}

		// ファイルがまだ実在しないか、書き込み可能である場合のみ保存する.
		OutputStream os = new BufferedOutputStream(new FileOutputStream(
				recentCharacterXML));
		try {
			String comment = "recent-character: lastModified="
					+ (new Timestamp(System.currentTimeMillis()).toString());
			props.storeToXML(os, comment);
		} finally {
			os.close();
		}
		return true;
	}

	/**
	 * 親ディレクトリからの相対パスとして記録されている、最後に使用したキャラクターデータのフォルダ名から、
	 * 最後に使用したキャラクターデータをロードして返す.<br>
	 * 該当するキャラクターデータが存在しないか、読み込みに失敗した場合は「履歴なし」としてnullを返す.<br>
	 *
	 * @return キャラクターデータ、もしくはnull
	 */
	public CharacterData loadRecent() {
		File recentCharacterXML = getRecentCharacterXML();
		if (recentCharacterXML.exists()) {
			try {
				Properties props = new Properties();
				InputStream is = new BufferedInputStream(new FileInputStream(
						recentCharacterXML));
				try {
					props.loadFromXML(is);
				} finally {
					is.close();
				}

				String characterDataName = props
						.getProperty("lastUseCharacterData");
				if (characterDataName != null
						&& characterDataName.trim().length() > 0) {
					// ※ キャラクターデータの親フォルダ上のフォルダ名だけを保存されているので
					// 相対パスから、character.xmlの絶対パスを求める
					File currentCharactersDir = DirectoryConfig.getInstance()
							.getCharactersDir();
					File characterDir = new File(currentCharactersDir,
							characterDataName);
					File characterXml = new File(characterDir,
							CharacterDataPersistent.CONFIG_FILE);
					if (characterXml.exists()) {
						// character.xmlが存在すれば復元を試行する.
						CharacterDataPersistent persist = CharacterDataPersistent
								.getInstance();
						return persist.loadProfile(characterXml.toURI());
					}
				}

			} catch (Exception ex) {
				// 失敗した場合は最後に使用したデータが存在しないものとみなす.
				logger.log(Level.WARNING, "recent data loading failed. "
						+ recentCharacterXML, ex);
			}
		}

		// 履歴がない場合、もしくは読み取れなかった場合はnullを返す.
		return null;
	}
}
