/*******************************************************************************
 * Copyright (c) 2008 IGA Tosiki, NTT DATA BUSINESS BRAINS Corp.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    IGA Tosiki (NTT DATA BUSINESS BRAINS Corp.) - initial API and implementation
 *******************************************************************************/
/*
 * 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.nlpack.generator.task;

import java.io.File;
import java.io.IOException;

import blanco.commons.util.BlancoStringUtil;
import blanco.nlpack.generator.BlancoNLpackGeneratorConstants;
import blanco.nlpack.generator.dictionary.BlancoNLpackGeneratorPleiadesDictionary;
import blanco.nlpack.generator.message.BlancoNLpackGeneratorMessage;
import blanco.nlpack.generator.task.valueobject.BlancoNLpackGeneratorTranslateXliffByPleiadesProcessInput;
import blanco.properties.BlancoPropertiesUtil;
import blanco.xliff.BlancoXliffUtil;
import blanco.xliff.stringgroup.BlancoXliffStateStringGroup;
import blanco.xliff.valueobject.BlancoXliff;
import blanco.xliff.valueobject.BlancoXliffFile;
import blanco.xliff.valueobject.BlancoXliffNote;
import blanco.xliff.valueobject.BlancoXliffTarget;
import blanco.xliff.valueobject.BlancoXliffTool;
import blanco.xliff.valueobject.BlancoXliffTransUnit;

public class BlancoNLpackGeneratorTranslateXliffByPleiadesProcessImpl implements
        BlancoNLpackGeneratorTranslateXliffByPleiadesProcess {
    private BlancoNLpackGeneratorTranslateXliffByPleiadesProcessInput fInput = null;

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

    /**
     * Pleiades辞書のインスタンス。
     */
    private BlancoNLpackGeneratorPleiadesDictionary fPleiadesDictionary = new BlancoNLpackGeneratorPleiadesDictionary();

    private int fTransCount = 0;

    private int fAltTransCount = 0;

    private String fPrevBundleSymbolicName = null;

    private BlancoXliff fXliff = null;

    /**
     * クラスをインスタンス化して処理を実行する際のエントリポイントです。
     * 
     * @param input
     *            処理の入力パラメータ。
     * @return 処理の実行結果。
     * @throws IOException
     *             入出力例外が発生した場合。
     * @throws IllegalArgumentException
     *             入力値に不正が見つかった場合。
     */
    public int execute(
            final BlancoNLpackGeneratorTranslateXliffByPleiadesProcessInput input)
            throws IOException, IllegalArgumentException {
        fInput = input;

        if (fInput.getSettarget() == false && fInput.getSettargetoverwrite()) {
            throw new IllegalArgumentException(
                    "settargetoverwrite を利用する場合には settarget を true に設定してください。");
        }

        if (fInput.getSettarget() == false && fInput.getSetalttrans() == false) {
            throw new IllegalArgumentException(
                    "settarget または setalttrans のいずれか一方は true に設定してください。");
        }

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

        final File fileXliffdir = new File(input.getXliffdir());

        if (fileXliffdir.exists() == false) {
            throw new IllegalArgumentException("指定された xliffdir["
                    + fileXliffdir.getAbsolutePath() + "]が見つかりません。");
        }
        if (fileXliffdir.isDirectory() == false) {
            throw new IllegalArgumentException("指定された xliffdir["
                    + fileXliffdir.getAbsolutePath() + "]はディレクトリではありません。");
        }

        final File[] files = fileXliffdir.listFiles();
        if (files == null) {
            return 0;
        }
        for (int index = 0; index < files.length; index++) {
            if (files[index].isFile() == false) {
                continue;
            }
            if (files[index].getName().endsWith(".xlf")) {
                process(fileXliffdir, files[index]);
            }
        }

        return 0;
    }

    private void process(final File fileXliffdir, final File fileTarget)
            throws IOException {
        fTransCount = 0;
        fAltTransCount = 0;

        final String bundleSymbolicName = fileTarget.getName().substring(0,
                fileTarget.getName().length() - ".xlf".length());

        if (bundleSymbolicName.equals(fPrevBundleSymbolicName)) {
            // キャッシュがヒットしました。リロードしません。
        } else {
            fXliff = BlancoXliffUtil.getXliffInstance(fileXliffdir,
                    bundleSymbolicName);
            fPrevBundleSymbolicName = bundleSymbolicName;
        }

        for (int indexFile = 0; indexFile < fXliff.getFileList().size(); indexFile++) {
            final BlancoXliffFile file = (BlancoXliffFile) fXliff.getFileList()
                    .get(indexFile);
            processFile(file);
        }

        switch (BlancoXliffUtil.saveXliffInstance(fXliff, fileXliffdir,
                bundleSymbolicName)) {
        case 1:
            // 基本的に、こちらには入らない見込み。
            if (fInput.getVerbose()) {
                System.out.println("xliff: trans: pleiades: create: "
                        + bundleSymbolicName);
            }
            break;
        case 2:
            if (fInput.getVerbose()) {
                System.out.println("xliff: trans: pleiades: "
                        + bundleSymbolicName + ": t=" + fTransCount + ", a="
                        + fAltTransCount);
            }
            break;
        }
    }

    private void processFile(final BlancoXliffFile file) {
        boolean isToolModified = false;
        for (int index = 0; index < file.getBody().getTransUnitList().size(); index++) {
            final BlancoXliffTransUnit transUnit = (BlancoXliffTransUnit) file
                    .getBody().getTransUnitList().get(index);

            if (transUnit.getTranslate() == false) {
                // 除外項目。
                continue;
            }

            final String outputValue = fPleiadesDictionary
                    .lookup(BlancoStringUtil.null2Blank(transUnit.getSource()));
            if (outputValue == null) {
                throw new IllegalArgumentException(fMsg.getMbebc003(transUnit
                        .getId(), "キー(?)"));
            }

            if (fPleiadesDictionary.getFoundStatus() == BlancoNLpackGeneratorPleiadesDictionary.STATUS_NOT_FOUND) {
                // 辞書に見当たらなかった場合。

                // 何もしません。
                continue;
            }

            if (fPleiadesDictionary.getFoundStatus() == BlancoNLpackGeneratorPleiadesDictionary.STATUS_MNEMONIC_CHANGED) {
                // ニモーニック変換のみ実施された場合。

                // 出力値をセット！
                if (transUnit.getTarget() == null) {
                    // targetが未設定の場合にのみ、その内容をセットします。
                    fTransCount++;

                    transUnit.setTarget(new BlancoXliffTarget());
                    transUnit.getTarget().setTarget(outputValue);
                    // ニモーニック変換のみの場合には、「新規」とセットします。
                    transUnit.getTarget().setState("new");
                }
                // ニモーニック処理のみの場合には alt-trans はセットしないこととします。
                continue;
            }

            if (fPleiadesDictionary.getFoundStatus() == BlancoNLpackGeneratorPleiadesDictionary.STATUS_FOUND) {
                // 辞書からの引き当てに成功した場合。

                if (fInput.getSettarget()) {
                    // 出力値をセット！

                    // TM対象外のものは settargetoverwrite の判断の際に特別扱いします。
                    final BlancoXliffNote noteOmit = getNoteOmitTm(transUnit);
                    // プリファランス・キーワードも特別扱いします。
                    final boolean isPreferenceKeywords = BlancoPropertiesUtil
                            .isPreferenceKeywords(transUnit.getId());

                    boolean isOverwriteTargetTarget = false;
                    if (transUnit.getTarget() != null
                            && BlancoStringUtil.null2Blank(
                                    transUnit.getTarget().getState()).length() > 0) {
                        switch (new BlancoXliffStateStringGroup()
                                .convertToInt(BlancoStringUtil
                                        .null2Blank(transUnit.getTarget()
                                                .getState()))) {
                        case BlancoXliffStateStringGroup.NEW:
                        case BlancoXliffStateStringGroup.NEEDS_TRANSLATION:
                            isOverwriteTargetTarget = true;
                            break;
                        }
                    }

                    if (false) {
                        // あいまいの範囲に許容できる変更が Pleiades
                        // で発生した場合に、寛容に上書きしてしまいたい場合にのみここを利用します。
                        // これは、通常は利用しません。Pleiades で大規模な変更があった場合にのみ利用します。
                        String oldTarget = BlancoStringUtil
                                .null2Blank(transUnit.getTarget().getTarget());
                        String newTarget = BlancoStringUtil
                                .null2Blank(outputValue);
                        if (oldTarget.equals(newTarget)) {
                            // そもそも Pleiades 側に変化が無いので、ここでは何もしません。
                        } else {
                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    " ", "", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    " ", "", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "　", "", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "　", "", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    ".", "", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    ".", "", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "!", "", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "!", "", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "。", "", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "。", "", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "、", "", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "、", "", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "｡", "", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "｡", "", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    ":", "", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    ":", "", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    ";", "", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    ";", "", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "-", "", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "-", "", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "・", "", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "・", "", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "無効です", "有効ではありません", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "無効です", "有効ではありません", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "有効ではない", "無効", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "有効ではない", "無効", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "無効な値です", "有効な値ではありません", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "無効な値です", "有効な値ではありません", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "無効な式です", "有効な式ではありません", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "無効な式です", "有効な式ではありません", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "空欄", "空", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "空欄", "空", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "タイプ", "型", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "タイプ", "型", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "記述", "説明", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "記述", "説明", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "中", "", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "中", "", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "Getter", "getter", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "Getter", "getter", true);

                            oldTarget = BlancoStringUtil.replace(oldTarget,
                                    "Setter", "setter", true);
                            newTarget = BlancoStringUtil.replace(newTarget,
                                    "Setter", "setter", true);

                            if (oldTarget.equals(newTarget)) {
                                // 置換によって一致する文字列になりました。
                                isOverwriteTargetTarget = true;
                            }
                        }
                    }

                    if ((fInput.getSettargetoverwrite() && noteOmit == null && isPreferenceKeywords == false)
                            || transUnit.getTarget() == null
                            || isOverwriteTargetTarget) {
                        // 上書き または targetが未設定の場合にのみ、その内容をセットします。
                        fTransCount++;

                        if (transUnit.getTarget() == null) {
                            transUnit.setTarget(new BlancoXliffTarget());
                        }

                        if (BlancoStringUtil.null2Blank(outputValue).equals(
                                transUnit.getTarget().getTarget()) == false) {
                            // 値が違うのでセットします。
                            transUnit.getTarget().setTarget(outputValue);

                            if (isOverwriteTargetTarget) {
                                // Pleiades
                                // から翻訳発見で戻ってきた場合で、既に state
                                // が new, needs-translation
                                // がセットされている場合は空白に変更します。
                                transUnit.getTarget().setState(null);
                            }
                        }
                    }
                }

                if (fInput.getSetalttrans()) {
                    if (BlancoNLpackGeneratorTranslateXliffByNLpackProcessImpl
                            .setAltTransTarget("pleiades", transUnit, transUnit
                                    .getSource(), outputValue)) {
                        fAltTransCount++;
                        isToolModified = true;
                    }
                }

                continue;
            }

            throw new IllegalArgumentException("想定されない箇所に入ってきました。");
        }

        if (isToolModified) {
            final BlancoXliffTool tool = new BlancoXliffTool();
            tool.setToolId("pleiades");
            tool.setToolName("Pleiades");
            BlancoXliffUtil.setTool(file.getHeader(), tool);

        }
    }

    /**
     * noteを検索して取得します。
     * 
     * @param transUnit
     * @param from
     * @param textString
     * @return
     */
    private BlancoXliffNote getNoteOmitTm(final BlancoXliffTransUnit transUnit) {
        for (int index = 0; index < transUnit.getNoteList().size(); index++) {
            final BlancoXliffNote note = (BlancoXliffNote) transUnit
                    .getNoteList().get(index);
            if (BlancoStringUtil.null2Blank(note.getFrom()).equals(
                    BlancoNLpackGeneratorConstants.PRODUCT_NAME_LOWER
                            + ".omit.tm") == false) {
                // 他のfromのものはスキップします。
                continue;
            }

            // from が一致しました。
            return note;
        }

        return null;
    }
}
