package charactermanaj.util;

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.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 * ユーザーデータの保存先を生成するファクトリ
 * @author seraphy
 */
public class UserDataFactory {

	/**
	 * シングルトン
	 */
	private static UserDataFactory inst = new UserDataFactory();
	
	/**
	 * インスタンスを取得する.
	 * @return インスタンス
	 */
	public static UserDataFactory getInstance() {
		return inst;
	}
	
	/**
	 * プライベートコンストラクタ
	 */
	private UserDataFactory() {
		super();
	}

	/**
	 * 指定した名前のユーザーデータ保存先を作成する.
	 * @param name ファイル名
	 * @return 保存先
	 */
	public UserData getUserData(String name) {
		if (name == null || name.trim().length() == 0) {
			throw new IllegalArgumentException();
		}
		File userDataDir = ConfigurationDirUtilities.getUserDataDir(); 
		return new FileUserData(new File(userDataDir, name));
	}

	/**
	 * docBaseごとにのハッシュ値を文字列表現化したプレフィックスをもつユーザーデータ保存先を作成する.<br>
	 * docBaseのURLの圧縮を目的としており、等しいdocBaseは等しいプレフィックスによるようにしている.(暗号化が目的ではない).<br>
	 * ハッシュ値はmd5の5バイトで生成されるため、nameを工夫して衝突の確率を軽減するか衝突しても問題ないように考慮することが望ましい.<Br>
	 * @param docBase URL、null可
	 * @param name ファイル名
	 * @return 保存先
	 */
	public UserData getMangledNamedUserData(URL docBase, String name) {
		String docBaseStr;
		if (docBase == null) {
			docBaseStr = "";
		} else {
			docBaseStr = docBase.toString();
		}
		String prefix = UUID.nameUUIDFromBytes(docBaseStr.getBytes()).toString();
		return getUserData(prefix + "-" + name);
	}
	
}

/**
 * ファイルベースのユーザーデータの保存先の実装
 * @author seraphy
 */
class FileUserData implements UserData {

	/**
	 * ロガー
	 */
	private static final Logger logger = ApplicationLogger.getLogger();
	
	/**
	 * 保存先ファイル
	 */
	private File file;
	
	public FileUserData(File file) {
		if (file == null) {
			throw new IllegalArgumentException();
		}
		this.file = file;
	}
	
	public boolean exists() {
		return file.exists() && file.isFile();
	}
	
	public long lastModified() {
		return file.lastModified();
	}
	
	public InputStream openStream() throws IOException {
		return new BufferedInputStream(new FileInputStream(file));
	}
	
	public OutputStream getOutputStream() throws IOException {
		return new BufferedOutputStream(new FileOutputStream(file));
	}
	
	public boolean delete() {
		try {
			return file.delete();

		} catch (Exception ex) {
			// セキュリティ例外ぐらい.
			logger.log(Level.WARNING, "file removing failed." + file, ex);
			return false;
		}
	}
	
	public void save(Object userData) throws IOException {
		ObjectOutputStream oos = new ObjectOutputStream(getOutputStream());
		try {
			oos.writeObject(userData);
			oos.close();
		} finally {
			oos.close();
		}
	}

	public Object load() throws IOException {
		ObjectInputStream ois = new ObjectInputStream(openStream());
		try {
			try {
				return ois.readObject();

			} catch (ClassNotFoundException ex) {
				// 復元先クラスがみつからないということは、このアプリケーションの保存した形式としておかしい
				IOException ex2 = new IOException("invalid format.");
				ex2.initCause(ex2);
				throw ex2;
			}
		} finally {
			ois.close();
		}
	}
	
	@Override
	public String toString() {
		return "FileUserData{file:" + file + "}";
	}
	
}

