/*
 * blanco Framework
 * Copyright (C) 2008 NTT DATA BUSINESS BRAINS CORPORATION
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */
package blanco.sed;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import blanco.commons.util.BlancoNameUtil;

/**
 * sed のごく一部機能の実現を目指したクラス。
 * 
 * @author IGA Tosiki
 */
public class BlancoSed {
    /**
     * sed のスクリプト相当の機能を提供します。
     * 
     * 注意: 本来の sed がスクリプトファイル内の複数にマッチした際の挙動を未調査。
     * 
     * @throws IOException
     */
    public static void process(final BufferedReader reader,
            final BufferedWriter writer, final BufferedReader readerScriptFile)
            throws IOException {
        final List<String> scriptList = new ArrayList<String>(1024);
        for (;;) {
            final String line = readerScriptFile.readLine();
            if (line == null) {
                break;
            }
            scriptList.add(line);
        }

        for (;;) {
            String line = reader.readLine();
            if (line == null) {
                break;
            }

            for (int indexScript = 0; indexScript < scriptList.size(); indexScript++) {
                line = process(line, scriptList.get(indexScript));
            }
            writer.write(line);
            writer.newLine();
        }
    }

    /**
     * sed のスクリプト相当の機能を提供します。
     * 
     * @param source
     *            入力文字列。
     * @param script
     *            sedスクリプト。s/regexp/replacement/g 相当のもの。
     * @return 置換後の文字列。
     */
    public static String process(final String source, final String script) {
        boolean isReplaceAll = false;
        final String[] tokens = BlancoNameUtil.splitString(script, '/');

        if (tokens.length < 1) {
            throw new IllegalArgumentException("sed: コマンドがありません。");
        }
        if (tokens.length < 2) {
            throw new IllegalArgumentException("sed: 正規表現がありません。");
        }
        if (tokens.length < 3) {
            throw new IllegalArgumentException("sed: 置換文字列がありません。");
        }

        final String cmd = tokens[0];
        if ("s".equals(cmd) == false) {
            throw new IllegalArgumentException("sed: s以外のコマンド[" + cmd
                    + "]は受け付けません。");
        }

        final String regexp = tokens[1];

        final String replacement = tokens[2];

        if (tokens.length >= 4) {
            final String replaceAll = tokens[3];
            if ("g".equals(replaceAll)) {
                isReplaceAll = true;
            }
        }
        return replace(source, regexp, replacement, isReplaceAll);
    }

    /**
     * 与えられた文字列に regexp のマッチを試み、マッチした場合には置換文字列で置換します。
     * 
     * @param source
     *            入力文字列。
     * @param regexp
     *            マッチさせる際の正規表現。
     * @param replacement
     *            置換文字列。
     * @param isReplaceAll
     *            全てを置換するかどうか。
     * @return 置換後の文字列。マッチした場合にはマッチした部分が replacement に置換されます。
     */
    public static String replace(final String source, final String regexp,
            final String replacement, final boolean isReplaceAll) {
        try {
            final Pattern pattern = Pattern.compile(regexp);
            final Matcher matcher = pattern.matcher(source);
            if (isReplaceAll) {
                return matcher.replaceAll(replacement);
            } else {
                return matcher.replaceFirst(replacement);
            }
        } catch (PatternSyntaxException ex) {
            throw new IllegalArgumentException("与えられた正規表現パターン[" + regexp
                    + "]は無効です", ex);
        }
    }
}
