package blanco.nlpack.generator.task;

import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

import blanco.commons.io.Native2AsciiWriter;
import blanco.commons.util.BlancoFileUtil;
import blanco.commons.util.BlancoStringUtil;
import blanco.nlpack.generator.BlancoNLpackGeneratorConstants;
import blanco.nlpack.generator.csv.io.BlancoCsvIOException;
import blanco.nlpack.generator.csv.io.BlancoNLpackGeneratorResultCsvWriter;
import blanco.nlpack.generator.csv.io.BlancoNLpackGeneratorResultDetailCsvWriter;
import blanco.nlpack.generator.csv.record.BlancoNLpackGeneratorResultCsvRecord;
import blanco.nlpack.generator.csv.record.BlancoNLpackGeneratorResultDetailCsvRecord;
import blanco.nlpack.generator.message.BlancoNLpackGeneratorMessage;
import blanco.nlpack.generator.resourcebundle.BlancoNLpackGeneratorResourceBundle;
import blanco.nlpack.generator.task.valueobject.BlancoNLpackGeneratorConvertXliff2BabelPropertiesProcessInput;
import blanco.properties.BlancoPropertiesDirectoryParser;
import blanco.properties.BlancoPropertiesUtil;
import blanco.properties.manifest.valueobject.BlancoPropertiesManifest;
import blanco.xliff.BlancoXliffUtil;
import blanco.xliff.valueobject.BlancoXliff;
import blanco.xliff.valueobject.BlancoXliffTransUnit;

public class BlancoNLpackGeneratorConvertXliff2BabelPropertiesProcessImpl
        extends BlancoPropertiesDirectoryParser implements
        BlancoNLpackGeneratorConvertXliff2BabelPropertiesProcess {
    private BlancoNLpackGeneratorConvertXliff2BabelPropertiesProcessInput fInput = null;

    /**
     * メッセージオブジェクト。
     */
    private final BlancoNLpackGeneratorMessage fMsg = new BlancoNLpackGeneratorMessage();

    private final BlancoNLpackGeneratorResourceBundle fBundle = new BlancoNLpackGeneratorResourceBundle();

    private String fPrevBundleSymbolicName = null;

    private BlancoXliff fXliff = null;

    private BlancoNLpackGeneratorResultCsvWriter fLogSummaryWriter = null;

    private BlancoNLpackGeneratorResultDetailCsvWriter fLogDetailWriter = null;

    private String fPropertiesNameWithPath = null;

    private Properties fOutputProps = null;

    /**
     * ヒットした件数。
     */
    private int fReplaceHitCount = 0;

    /**
     * 除外した件数。
     */
    private int fReplaceOmitCount = 0;

    /**
     * ミスした件数。
     */
    private int fReplaceMissCount = 0;

    public int execute(
            final BlancoNLpackGeneratorConvertXliff2BabelPropertiesProcessInput input)
            throws IOException, IllegalArgumentException {
        fInput = input;

        // キャッシュのクリア。
        fPrevBundleSymbolicName = null;

        System.out.println("babel: begin: [" + fInput.getSourcedir() + "]->["
                + fInput.getTargetdir() + "].");

        {
            // 出力先ディレクトリを作成。
            final File fileTarget = new File(fInput.getTargetdir());
            if (fileTarget.exists() == false) {
                fileTarget.mkdirs();
            }

            final File fileTmpDir = new File(fInput.getTmpdir() + "/log");
            fileTmpDir.mkdirs();
        }

        // TODO ログファイル名の外部化。
        final File fileLogSummary = new File(fInput.getTmpdir()
                + "/log/blancoNLpackGeneratorResult.csv");
        try {
            final BufferedWriter writer = new BufferedWriter(
                    new BufferedWriter(new FileWriter(fileLogSummary, true)));
            writer.write("[[" + BlancoNLpackGeneratorConstants.PRODUCT_NAME
                    + " (" + BlancoNLpackGeneratorConstants.VERSION + ")"
                    + "]]: " + fInput.getSourcedir());
            writer.newLine();

            fLogSummaryWriter = new BlancoNLpackGeneratorResultCsvWriter(writer);
            fLogSummaryWriter.writeTitle();
        } catch (IOException ex) {
            throw new IllegalArgumentException(fMsg.getMbebi004(fileLogSummary
                    .getAbsolutePath(), ex.toString()));
        }

        // TODO ログファイル名の外部化。
        final File fileLogDetail = new File(fInput.getTmpdir()
                + "/log/blancoNLpackGeneratorResultDetail.csv");
        try {
            final BufferedWriter writer = new BufferedWriter(
                    new BufferedWriter(new FileWriter(fileLogDetail, true)));
            writer.write("[[" + BlancoNLpackGeneratorConstants.PRODUCT_NAME
                    + " (" + BlancoNLpackGeneratorConstants.VERSION + ")"
                    + "]]: " + fInput.getSourcedir());
            writer.newLine();

            fLogDetailWriter = new BlancoNLpackGeneratorResultDetailCsvWriter(
                    writer);
            fLogDetailWriter.writeTitle();
        } catch (IOException ex) {
            throw new IllegalArgumentException(fMsg.getMbebi005(fileLogDetail
                    .getAbsolutePath(), ex.toString()));
        }

        try {
            if (new File(fInput.getSourcedir()).exists() == false) {
                throw new IllegalArgumentException(fMsg.getMbebi001(fInput
                        .getSourcedir()));
            }

            parse(new File(fInput.getSourcedir()));
        } finally {
            if (fLogSummaryWriter != null) {
                fLogSummaryWriter.close();
                fLogSummaryWriter = null;
            }

            if (fLogDetailWriter != null) {
                fLogDetailWriter.close();
                fLogDetailWriter = null;
            }
        }

        System.out.println("babel: end.");

        return 0;
    }

    @Override
    protected void beginProperties(final String bundleURI,
            final Properties inputProps,
            final String argPropertiesNameWithPath,
            final BufferedWriter bufPropsWriter) throws IOException {
        fReplaceHitCount = 0;
        fReplaceMissCount = 0;
        fReplaceOmitCount = 0;
        fOutputProps = new Properties();

        final String nextBundleSymbolicName = BlancoPropertiesUtil
                .getHostFromBabelURI(bundleURI);
        if (nextBundleSymbolicName.equals(fPrevBundleSymbolicName)) {
            // キャッシュがヒットしました。リロードしません。
        } else {
            fXliff = BlancoXliffUtil.getXliffInstance(new File(fInput
                    .getXliffdir()), nextBundleSymbolicName);
            fPrevBundleSymbolicName = nextBundleSymbolicName;
        }

        fPropertiesNameWithPath = argPropertiesNameWithPath;
        if (BlancoStringUtil.null2Blank(fBundle.getPropertiesHeader()).length() > 0) {
            // 人工的にヘッダーを付与します。
            bufPropsWriter.write(fBundle.getPropertiesHeader());
            bufPropsWriter.newLine();
            bufPropsWriter.newLine();
        }
    }

    @Override
    protected boolean processPropertiesKey(String bundleURI,
            Properties inputProps, String key, BufferedWriter bufPropsWriter)
            throws IOException {
        boolean isReplaced = false;
        final BlancoNLpackGeneratorResultDetailCsvRecord record = new BlancoNLpackGeneratorResultDetailCsvRecord();
        record.setBundleUri(bundleURI + "?key=" + key);
        record.setKey(key);
        record.setInput((String) inputProps.get(key));

        final BlancoXliffTransUnit transUnit = BlancoXliffUtil.getTransUnit(
                fXliff, bundleURI + "?key=" + key);

        if (transUnit == null) {
            // 見つけられず
            fReplaceMissCount++;
            record.setAction("miss.n");
        } else if (transUnit.getTranslate() == false) {
            // 翻訳対象外。
            fReplaceOmitCount++;
            record.setAction("omit");
        } else if (BlancoStringUtil.null2Blank(transUnit.getSource()).equals(
                (String) inputProps.get(key)) == false) {
            // せっかく trans-unit が見つかったのに、source が異なります。
            fReplaceMissCount++;
            record.setAction("miss.s");

            // FIXME source異なりの場合の処理について、どのような実装がベストなのか検討をおこなうこと。
            System.out
                    .println("trace: せっかく trans-unit が見つかったのに、source が異なります。: "
                            + record.getBundleUri());
        } else if (transUnit.getTarget() != null
                && transUnit.getTarget().getTarget() != null) {
            // 以前は state によって動作を替えていたが、その処理は破棄しました。
            // target があれば、常に翻訳置換を実施します。
            fReplaceHitCount++;
            record.setAction("hit");
            isReplaced = true;

            fOutputProps.put(key, transUnit.getTarget().getTarget());
        } else {
            fReplaceMissCount++;
            record.setAction("miss.t");
        }

        if (isReplaced) {
            bufPropsWriter
                    .write(Native2AsciiWriter.encodeNative2AsciiValue(transUnit
                            .getTarget().getTarget()));
            record.setOutput(transUnit.getTarget().getTarget());
        } else {
            bufPropsWriter.write(Native2AsciiWriter
                    .encodeNative2AsciiValue((String) inputProps.get(key)));
        }
        bufPropsWriter.newLine();

        try {
            fLogDetailWriter.writeRecord(record);
        } catch (BlancoCsvIOException e) {
            throw new IllegalArgumentException("CSV入出力例外", e);
        }

        return isReplaced;
    }

    @Override
    protected void endProperties(String bundleURI, Properties inputProps,
            BlancoPropertiesManifest manifest, byte[] bufPropsBytesIgnore,
            boolean isReplaced) throws IOException {
        final BlancoNLpackGeneratorResultCsvRecord record = new BlancoNLpackGeneratorResultCsvRecord();
        record.setBundleSymbolicName(BlancoPropertiesUtil
                .getHostFromBabelURI(bundleURI));
        record.setBundleUri(bundleURI);
        record.setReplaceHitCount(fReplaceHitCount);
        record.setReplaceMissCount(fReplaceMissCount);
        record.setReplaceOmitCount(fReplaceOmitCount);

        final String displayPath = BlancoPropertiesUtil
                .getHostFromBabelURI(bundleURI)
                + ": " + BlancoPropertiesUtil.getPathFromBabelURI(bundleURI);

        final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        fOutputProps.store(outStream, fBundle.getPropertiesHeader()
                + ", for Eclipse BABEL large contribution.");
        outStream.flush();

        if (isReplaced) {
            // Eclipse BABEL の場合には locale やバージョンは付与しない。
            final File fileTarget = new File(fInput.getTargetdir() + "/"
                    + BlancoPropertiesUtil.getHostFromBabelURI(bundleURI)
                    + fPropertiesNameWithPath);
            fileTarget.getParentFile().mkdirs();
            switch (BlancoFileUtil.bytes2FileIfNecessary(outStream
                    .toByteArray(), fileTarget)) {
            case 1:
                if (fInput.getVerbose()) {
                    System.out.println("xliff: x2p: create: " + displayPath);
                }
                record.setAction("create");
                break;
            case 2:
                if (fInput.getVerbose()) {
                    System.out.println("xliff: x2p: update: " + displayPath);
                }
                record.setAction("update");
                break;
            case 0:
                if (fInput.getVerbose()) {
                    System.out.println("xliff: x2p: none  : " + displayPath);
                }
                record.setAction("none");
                break;
            }
        } else {
            if (fInput.getVerbose()) {
                System.out.println("xliff: x2p: skip  : " + displayPath);
            }
            record.setAction("skip");
        }

        try {
            fLogSummaryWriter.writeRecord(record);
        } catch (BlancoCsvIOException e) {
            throw new IOException(fMsg.getMbebc001(e.toString()));
        }
    }

    /**
     * {@inheritDoc}
     */
    public boolean progress(final String argProgressMessage) {
        System.out.println(argProgressMessage);
        return false;
    }
}
