package net.argius.filetool;

import java.io.*;

/**
 * ファイルアーカイブ処理。
 */
public final class FileArchiver {

    private Archiver archiver;

    /**
     * FileArchiverの生成。
     * @param dst 出力先ファイル
     * @throws IOException 入出力エラーが発生した場合 
     */
    public FileArchiver(File dst) throws IOException {
        this(dst, ArchiveType.getType(dst));
    }

    /**
     * FileArchiverの生成。
     * @param dst 出力先ファイル
     * @param type アーカイブタイプ
     * @throws IOException 入出力エラーが発生した場合 
     */
    public FileArchiver(File dst, ArchiveType type) throws IOException {
        OutputStream os = new FileOutputStream(dst);
        if (type == ArchiveType.TAR) {
            archiver = new TarArchiver(os);
        } else if (type == ArchiveType.TGZ) {
            archiver = new TarArchiver(new GzipCompressor(os));
        } else if (type == ArchiveType.TBZ) {
            archiver = new TarArchiver(new Bzip2Compressor(os));
        } else if (type == ArchiveType.ZIP) {
            archiver = new ZipArchiver(os);
        } else if (type == ArchiveType.LZH) {
            archiver = new LhaArchiver(os);
        } else {
            throw new IllegalStateException("ArchiverType: " + type);
        }
    }

    /**
     * 終了処理。
     * @throws IOException 入出力エラーが発生した場合
     */
    public void close() throws IOException {
        archiver.close();
    }

    /**
     * アーカイブする。
     * @param src 対象ファイル
     * @throws IOException 入出力エラーが発生した場合 
     */
    public void archive(File src) throws IOException {
        archive(src, getRootPath(src));
    }

    /**
     * アーカイブする。
     * @param src 対象ファイル
     * @param rootPath 対象のルートパス
     * @throws IOException 入出力エラーが発生した場合
     */
    private void archive(File src, String rootPath) throws IOException {
        boolean isDirectory = src.isDirectory();
        String entryName = getEntryName(src, rootPath, isDirectory);
        ArchiveEntry entry = new ArchiveEntry(entryName);
        entry.setLastModified(src.lastModified());
        if (isDirectory) {
            archiver.addEntry(entry, null, 0);
            File[] files = src.listFiles();
            for (int i = 0, n = files.length; i < n; i++) {
                archive(files[i], rootPath);
            }
        } else {
            InputStream is = new FileInputStream(src);
            try {
                archiver.addEntry(entry, is, src.length());
            } finally {
                is.close();
            }
        }
    }

    /**
     * RootPathの取得。
     * @param file
     * @return RootPath
     */
    private static String getRootPath(File file) {
        String parent = normalizePath(file.getAbsoluteFile().getParent());
        if (!parent.endsWith(String.valueOf('/'))) {
            parent += '/';
        }
        return parent;
    }

    /**
     * エントリ名の取得。
     * @param file ファイル
     * @param rootPath ルートパス
     * @param isDirectory ディレクトリの場合は <code>true</code> 
     * @return エントリ名
     */
    private static String getEntryName(File file, String rootPath, boolean isDirectory) {
        String entryName;
        String absolutePath = normalizePath(file.getAbsolutePath());
        if (absolutePath.startsWith(rootPath)) {
            entryName = absolutePath.substring(rootPath.length());
            if (file.isDirectory() && !entryName.endsWith(String.valueOf('/'))) {
                entryName += '/';
            }
        } else {
            entryName = file.getName();
        }
        return entryName;
    }

    /**
     * パスセパレータを正規化する。
     * @param path パス文字列
     * @return 正規化されたパス文字列
     */
    private static String normalizePath(String path) {
        return path.replace('\\', '/');
    }

    /**
     * 使い方の表示。
     */
    private static void printUsage() {
        PrintStream err = System.err;
        err.println("FileArchiver (argius-filetool)");
        err.println("usage : (command) dst src...");
        err.println("options: none");
    }

    /**
     * FileArchiverの実行。
     * @param args パラメータ(see 'usage')
     */
    public static void main(String[] args) {
        if (args.length < 2) {
            printUsage();
            return;
        }
        for (int i = 0; i < args.length; i++) {
            String arg = args[i];
            if (arg.equals("-h") || arg.equals("--help")) {
                printUsage();
                return;
            }
        }
        final String commandNameInfo = "FileArchiver: ";
        int status;
        try {
            File dst = new File(args[0]);
            FileArchiver archiver = new FileArchiver(dst);
            try {
                for (int i = 1; i < args.length; i++) {
                    archiver.archive(new File(args[i]));
                }
            } finally {
                archiver.close();
            }
            status = 0;
        } catch (IOException ex) {
            System.out.println(commandNameInfo + ex.getMessage());
            status = 1;
        } catch (Exception ex) {
            String message = ex + Utilities.getFirstStackTrace(ex, DiskUsage.class);
            System.out.println(commandNameInfo + message);
            status = 255;
        }
        if (status > 0) {
            System.exit(status);
        }
    }

}
