package jp.sf.amateras.rdiffbackup.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jp.sf.amateras.rdiffbackup.dto.ChangeDto;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.seasar.struts.util.RequestUtil;

/**
 * rdiff-backupコマンドの実行結果を取得するためのユーティリティクラスです。
 *
 * @author Naoki Takezoe
 */
public class RDiffBackup {

	/**
	 * rdiff-backupのコマンド
	 * <p>
	 * TODO app.diconで設定できるようにする？
	 */
	private static final String COMMAND = "rdiff-backup";

	/**
	 * rdiff-backup -l の実行結果から履歴のタイムスタンプを抽出するための正規表現
	 */
	private static final Pattern PATTERN = Pattern.compile(
			"\\.([0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\+09:00)\\.");

	/**
	 * 引数で指定したファイルもしくはディレクトリのバックアップ日付のリストを返却します。
	 *
	 * @param path ファイルまたはディレクトリ
	 * @return バックアップ日付のリスト
	 */
	public static List<String> list(String dir){
		List<String> result = new ArrayList<String>();
		ProcessBuilder builder = new ProcessBuilder(COMMAND, "-l", dir);
		InputStream in = null;

		try {
			Process process = builder.start();
			in = process.getInputStream();
			String text = IOUtils.toString(in, "UTF-8");

			Matcher matcher = PATTERN.matcher(text);
			while(matcher.find()){
				result.add(matcher.group(1));
			}

		} catch(IOException ex){
			throw new RuntimeException(ex);
		} finally {
			IOUtils.closeQuietly(in);
		}

		return result;
	}

	/**
	 * 引数で指定したファイルもしくはディレクトリのうち、指定日時以降に変更されているもののリストを返却します。
	 *
	 * @param path ファイルまたはディレクトリ
	 * @param timestamp タイムスタンプ
	 * @return 指定日時以降に変更されたファイルまたはディレクトリのリスト
	 */
	public static List<ChangeDto> changes(String path, String timestamp){
		List<ChangeDto> result = new ArrayList<ChangeDto>();
		ProcessBuilder builder = new ProcessBuilder(COMMAND, "--list-changed-since", timestamp, path);
		InputStream in = null;

		try {
			Process process = builder.start();
			in = process.getInputStream();
			String text = IOUtils.toString(in, "UTF-8");

			String[] lines = text.split("\n");
			for(String line: lines){
				int index = line.indexOf(' ');
				if(index >= 0){
					String type = line.substring(0, index);
					String file = line.substring(index + 1).trim();

					ChangeDto changeDto = new ChangeDto();
					changeDto.type = type;
					changeDto.path = file;
					result.add(changeDto);
				}
			}

		} catch(IOException ex){
			throw new RuntimeException(ex);
		} finally {
			IOUtils.closeQuietly(in);
		}

		return result;
	}

	/**
	 * 引数で指定した日時のファイルもしくはディレクトリを一時ディレクトリにリストアします。
	 * 一時ディレクトリはアクションでの処理の終了後に{@link #removeTemporaryDirectory()}で削除してください。
	 *
	 * @param path ファイルまたはディレクトリ
	 * @param timestamp リストアする履歴のタイムスタンプ
	 * @return 一時ディレクトリ
	 */
	public static File restore(String path, String timestamp){
		File tmpDir = new File(System.getProperty("java.io.tmpdir"),
				RequestUtil.getRequest().getSession().getId());
		tmpDir.mkdir();

		ProcessBuilder builder = new ProcessBuilder(COMMAND, "-r", timestamp, path,
				new File(tmpDir, PathUtil.getFileName(path)).getAbsolutePath());
		InputStream in = null;

		try {
			Process process = builder.start();
			in = process.getInputStream();
			IOUtils.toString(in, "UTF-8");

		} catch(IOException ex){
			throw new RuntimeException(ex);
		} finally {
			IOUtils.closeQuietly(in);
		}

		return tmpDir;
	}

	/**
	 * {@link #restore(String, String)}で作成された一時ディレクトリを削除します。
	 * <p>
	 * 一時ディレクトリが存在しない場合は何も行いません。
	 * また、一時ディレクトリの削除に失敗した場合も例外はスローしません。
	 */
	public static void removeTemporaryDirectory(){
		File tmpDir = new File(System.getProperty("java.io.tmpdir"),
				RequestUtil.getRequest().getSession().getId());

		if(tmpDir.exists() && tmpDir.isDirectory()){
			try {
				FileUtils.deleteDirectory(tmpDir);
			} catch(IOException ex){
				;
			}
		}
	}

}
