package com.nifty.cloud.ncs.service;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * ファイルのMIMEタイプ取得用クラスです。
 *
 */
public class Mimetypes
{
	private static final Log log = LogFactory.getLog(Mimetypes.class);

	public static final String MIMETYPE_XML = "application/xml";
	public static final String MIMETYPE_HTML = "text/html";
	public static final String MIMETYPE_OCTET_STREAM = "application/octet-stream";
	public static final String MIMETYPE_GZIP = "application/x-gzip";
	public static final String MIMETYPE_DIRECTORY = "application/x-directory";
	public static final String MIMETYPE_JSON = "application/json";

	private static Mimetypes mimetypes = null;

	private HashMap<String, String> extensionToMimetypeMap = new HashMap<String, String>();

	/**
	 *
	 */
	private Mimetypes()
	{
	}

    /**
     * Loads MIME type info from the file 'mime.types' in the classpath, if it's available.
     * このオブジェクトのインスタンスを生成します。
     */
	public static Mimetypes getInstance()
	{
		if (mimetypes != null) {
			return (mimetypes);
		}

		mimetypes = new Mimetypes();

		InputStream mimetypesFile = mimetypes.getClass().getResourceAsStream("/mime.types");
		if (mimetypesFile != null) {
			if (log.isDebugEnabled()) {
				log.debug("Loading mime types from file in the classpath: mime.types");
			}
			try {
				mimetypes.loadAndReplaceMimetypes(mimetypesFile);
			} catch (IOException e) {
				if (log.isErrorEnabled()) {
					log.error("Failed to load mime types from file in the classpath: mime.types", e);
				}
			}
		} else {
			if (log.isWarnEnabled()) {
				log.warn("Can't find 'mime.types' file in classpath.");
			}
		}
		return (mimetypes);
	}

    /**
     * ファイル拡張子に対応するMIMEタイプの設定を入力ストリームから読み込み、保持します。
     * このメソッドが実行されるとき、MIMEタイプの設定が既に存在する場合は、MIMEタイプの値は
     * 新しいものに置き換えられます。
     *
     * @param is
     *
     * @throws IOException
     */
	public void loadAndReplaceMimetypes(InputStream is) throws IOException
	{
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		String line = null;

		while ((line = br.readLine()) != null) {
			line = line.trim();

			if (line.startsWith("#") || line.length() == 0) {
				// Ignore comments and empty lines.
			} else {
				StringTokenizer st = new StringTokenizer(line, " \t");
				if (st.countTokens() > 1) {
					String mimetype = st.nextToken();
					while (st.hasMoreTokens()) {
						String extension = st.nextToken();
						extensionToMimetypeMap.put(extension, mimetype);
						if (log.isDebugEnabled()) {
							log.debug(String.format("Setting mime type for extension '%s' to '%s'", extension, mimetype));
						}
					}
				} else {
					if (log.isDebugEnabled()) {
						log.debug(String.format("Ignoring mimetype with no associated file extensions: '%s'", line));
					}
				}
			}
		}
	}

    /**
     * ファイル名を指定し、指定されたファイルの拡張子に応じたMIMEタイプを取得します。
     *
     * @param fileName
     *            MIMEタイプを取得したいファイル名
     *
     * @return ファイルの拡張子に応じたMIMEタイプ
     */
	public String getMimetype(String fileName)
	{
		int lastPeriodIndex = fileName.lastIndexOf(".");
		if (lastPeriodIndex > 0 && lastPeriodIndex + 1 < fileName.length()) {
			String ext = fileName.substring(lastPeriodIndex + 1);
			if (extensionToMimetypeMap.keySet().contains(ext)) {
				String mimetype = (String)extensionToMimetypeMap.get(ext);
				if (log.isDebugEnabled()) {
					log.debug(String.format("Recognized extension '%s', mimetype is: '%s'", ext, mimetype));
				}
				return (mimetype);
			} else {
				if (log.isDebugEnabled()) {
					log.debug(String.format("Extension '%s' is unrecognized in mime type listing, using default mime type: '%s'.", ext, MIMETYPE_OCTET_STREAM));
				}
			}
		} else {
			if (log.isDebugEnabled()) {
				log.debug(String.format("File name has no extension, mime type can not be recognized for: %s", fileName));
			}
		}

		return (MIMETYPE_OCTET_STREAM);
	}

    /**
     * ファイルを指定し、拡張子に応じたMIMEタイプを取得します。
     *
     * @param file
     *  MIMEタイプを取得したいファイル
     *
     * @return ファイルの拡張子に応じたMIMEタイプ
     */
	public String getMimetype(File file)
	{
		return (getMimetype(file.getName()));
	}
}
