/*
 * Copyright (c) 2005- Shinji Kashihara.
 * All rights reserved. This program are made available under
 * the terms of the Eclipse Public License v1.0 which accompanies
 * this distribution, and is available at epl-v10.html.
 */
package jp.sourceforge.mergedoc.pleiades.runtime;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import jp.sourceforge.mergedoc.org.apache.commons.io.FileUtils;
import jp.sourceforge.mergedoc.org.apache.commons.lang.CharEncoding;
import jp.sourceforge.mergedoc.pleiades.log.Logger;
import jp.sourceforge.mergedoc.pleiades.resource.Files;
import jp.sourceforge.mergedoc.pleiades.resource.PropertySet;

/**
 * metadata を設定するためのクラスです。
 * ワークスペース作成時のデフォルトを自動設定します。
 * <p>
 * @author cypher256
 */
public class Metadata {

	/** ロガー */
	private static Logger log = Logger.getLogger(Metadata.class);

	/** 除外リスト */
	@SuppressWarnings("serial")
	private static final List<String> EXCLUDES = Collections.unmodifiableList(new ArrayList<String>() {

		{
			// Ant デフォルト除外
			add(".*~");
			add("#.*#");
			add(".#.*");
			add("%.*%");
			add("\\._.*");
			add("CVS");
			add("\\.cvsignore");
			add("SCCS");
			add("vssver\\.scc");
			add("\\.svn");
			add("\\.DS_Store");

			// その他 [#13834]
			add("\\.bzr");			// Bazaar
			add("\\.cdv");			// Codeville
			add("\\.git");			// Git
			add("\\.hg");			// Mercurial
			add("\\.pc");			// quilt
			add("RCS");				// RCS
			add("_darcs");			// darcs
			add("_sgbak");			// Vault/Fortress

			// その他 [#13834] (miau)
			add("vssver2\\.scc");	// VSS2005
			add("_svn");			// SVN オプション
		}

		@Override
		public boolean contains(Object name) {
			for (String pattern : this) {
				if (((String) name).matches(pattern)) {
					return true;
				}
			}
			return false;
		}
	});

	/** シングルトン・インスタンス */
	private static final Metadata singleton = new Metadata();

	/**
	 * このクラスのインスタンスを取得します。
	 * @return このクラスのインスタンス
	 */
	public static Metadata getInstance() {
		return singleton;
	}

	/**
	 * 外部から生成できません。
	 */
	private Metadata() {
	}

	//-------------------------------------------------------------------------

	/** Eclipse ホーム親ディレクトリー */
	private String eclipseHomeParent;

	/** metadata コピー元ディレクトリー */
	private File metadataSrcDir;

	/** metadata コピー先ディレクトリー */
	private File metadataDstDir;

	/** 新規ワークスペースの場合は true */
	private boolean isNewWorkspace;

	/**
	 * デフォルト自動設定が存在するか判定します。
	 * @return 存在する場合は true
	 */
	private boolean isExistsMetadataDefault() {

		if (metadataSrcDir == null) {
			File eclipseHomeParentDir = Applications.getHome().getParentFile();

			if (eclipseHomeParentDir != null) {
				eclipseHomeParent = eclipseHomeParentDir.getPath().replace("\\", "/");
				File metadataDir = new File(eclipseHomeParent, ".metadata.default");

				// Mac の場合は署名でエラーにならない場所に配置される
				if (!metadataDir.exists()) {
					metadataDir = new File(eclipseHomeParent, "Eclipse/.metadata.default");
				}
				metadataSrcDir = new File(metadataDir, ".plugins");
			}
		}
		return metadataSrcDir != null && metadataSrcDir.exists();
	}

	/**
	 * 新規ワークスペースのデフォルト自動設定エンコーディングを取得します。
	 * @return 新規ワークスペースでないか、デフォルト自動設定がない場合は null
	 */
	public String getDefaultWorkspaceEncoding() {

		if (!isNewWorkspace) {
			return null;
		}
		if (!isExistsMetadataDefault()) {
			return null;
		}

		// デフォルト自動設定のエンコーディング設定が格納されたファイル
		File encodingPrefs = new File(metadataSrcDir,
			"org.eclipse.core.runtime/.settings/org.eclipse.core.resources.prefs");
		if (!encodingPrefs.exists()) {
			return null;
		}

		PropertySet p = new PropertySet(encodingPrefs);
		String encoding = p.get("encoding");
		log.debug("デフォルト自動設定のワークスペースエンコーディング: %s", encoding);
		isNewWorkspace = false;
		return encoding;
	}

	/**
	 * 新規ワークスペースの metadata を作成します。
	 * @param workspace ワークスペース
	 */
	public void createNewWorkspaceMetadata(final URL workspace) throws URISyntaxException, IOException {

		if (!isExistsMetadataDefault()) {
			return;
		}
		if (workspace == null) {
			log.error("ワークスペースが null です。");
			return;
		}
		File workspaceFolder = new File(workspace.getFile());
		metadataDstDir = new File(workspaceFolder, ".metadata/.plugins");

		// metadata がコピー済みか判断するファイル
		File copiedFile = new File(metadataDstDir.getParentFile(), ".copied.metadata.default");
		if (copiedFile.exists()) {
			return;
		}
		int copyCount = copyMetadata(metadataSrcDir);

		FileUtils.writeStringToFile(copiedFile, "", CharEncoding.UTF_8);
		log.info(".metadata.default から " + copyCount +
			" 個のファイルをワークスペースにコピーしました。" + metadataDstDir.getParentFile());
		isNewWorkspace = true;
	}

	/**
	 * metadata を再帰的にコピーします。存在する場合は上書きしません。
	 * @param srcDir コピー元ディレクトリー
	 * @return コピーファイル数
	 */
	private int copyMetadata(File srcDir) throws IOException {

		final String HOME_STRING = "%ECLIPSE_HOME_PARENT%";
		int copyCount = 0;

		for (File srcFile : srcDir.listFiles()) {

			if (EXCLUDES.contains(srcFile.getName())) {
				continue;
			}
			if (srcFile.isDirectory()) {

				// 再帰
				copyCount += copyMetadata(srcFile);

			} else {
				File dstFile = new File(metadataDstDir, Files.relativePath(metadataSrcDir, srcFile));
				if (dstFile.exists()) {
					continue;
				}
				String s = FileUtils.readFileToString(srcFile, CharEncoding.UTF_8);
				if (s.contains(HOME_STRING)) {
					s = s.replace(HOME_STRING, eclipseHomeParent);
					FileUtils.writeStringToFile(dstFile, s, CharEncoding.UTF_8);
				} else {
					FileUtils.copyFile(srcFile, dstFile);
				}
				copyCount++;
			}
		}
		return copyCount;
	}
}
