/*
 * blanco Framework
 * Copyright (C) 2004-2005 IGA Tosiki
 * 
 * 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.plugin.resourcebundle.editors;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;

import javax.xml.transform.dom.DOMResult;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.IFileEditorInput;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import blanco.commons.util.BlancoXmlUtil;
import blanco.plugin.resourcebundle.BlancoResourceBundlePlugin;
import blanco.plugin.resourcebundle.BlancoResourceBundlePluginConstants;
import blanco.plugin.resourcebundle.actions.BlancoResourceBundleGenerateAction;
import blanco.resourcebundle.BlancoResourceBundleConstants;
import blanco.resourcebundle.resourcebundle.BlancoResourceBundleResourceBundle;

/**
 * リソースバンドル定義書からプロパティファイルおよびアクセサJavaソースコードを生成する Eclipseエディタです。
 * 
 * このソースコードはblancoResourceBundleの一部です。<br>
 * 
 * @author IGA Tosiki
 */
public class BlancoResourceBundleEditor extends
        AbstractBlancoResourceBundleEditor {
    /**
     * リソースバンドルアクセサのインスタンス。
     */
    private static final BlancoResourceBundleResourceBundle fBundle = new BlancoResourceBundleResourceBundle();

    /**
     * トップオブジェクト。
     */
    private Composite top = null;

    // ビジュアル領域
    private Label labelBlancoResourceBundleTitle = null;

    private Label labelBlancoResourceBundleSettingId = null;

    private Text textBlancoResourceBundleSettingId = null;

    private Label labelMetadir = null;

    private Combo comboMetadir = null;

    private Label labelMetadirComment = null;

    private Label labelSettingComment = null;

    private Button checkBoxFailOnMessageFormatError = null;

    private Button checkBoxIsLog = null;

    private Button buttonExecute = null;

    private Button buttonCreateMetafile = null;

    /**
     * Partコントロールを作成します。
     * 
     * @param parent
     *            親コントロール。
     */
    public void createPartControl(Composite parent) {
        top = parent;
        top.setSize(new org.eclipse.swt.graphics.Point(1000, 400));

        GridLayout layout = new GridLayout();
        layout.numColumns = 2;
        top.setLayout(layout);

        // スペーサー
        new Label(top, SWT.NONE).setText("");

        labelBlancoResourceBundleTitle = new Label(top, SWT.NONE);
        labelBlancoResourceBundleTitle
                .setText(BlancoResourceBundleConstants.PRODUCT_NAME
                        + "設定ファイル エディタ ("
                        + BlancoResourceBundleConstants.VERSION + ")");

        // スペーサー
        new Label(top, SWT.NONE).setText("");

        // スペーサー
        new Label(top, SWT.NONE).setText("");

        labelBlancoResourceBundleSettingId = new Label(top, SWT.NONE);
        labelBlancoResourceBundleSettingId.setText("設定ID");
        textBlancoResourceBundleSettingId = new Text(top, SWT.BORDER);
        textBlancoResourceBundleSettingId
                .setText(BlancoResourceBundleConstants.PRODUCT_NAME);
        textBlancoResourceBundleSettingId.setEditable(false);

        labelMetadir = new Label(top, SWT.NONE);
        labelMetadir.setText("メタディレクトリ");
        createComboMetadir();

        // スペーサー
        new Label(top, SWT.NONE).setText("");

        labelMetadirComment = new Label(top, SWT.NONE);
        labelMetadirComment.setText("※メタディレクトリは通常変更せずに利用します。");
        labelMetadirComment.setSize(new org.eclipse.swt.graphics.Point(500,
                30 + 20));

        // スペーサー
        new Label(top, SWT.NONE).setText("");

        buttonCreateMetafile = new Button(top, SWT.NONE);
        buttonCreateMetafile
                .setText(fBundle.getMetafileDisplayname() + " 新規作成");
        buttonCreateMetafile
                .addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() {
                    public void widgetSelected(
                            org.eclipse.swt.events.SelectionEvent e) {
                        // メタファイル作成の際には、他の項目のチェックは行いません。
                        saveToTextEditor();

                        if (comboMetadir.getText().length() == 0) {
                            MessageDialog
                                    .openWarning(
                                            top.getShell(),
                                            fBundle.getMetafileDisplayname()
                                                    + " 新規作成",
                                            "メタディレクトリ名を指定してください。\n"
                                                    + "※メタディレクトリ名の一般的な設定値は 'meta/resourcebundle' です。");
                            return;
                        }

                        final IFileEditorInput input = (IFileEditorInput) getEditorInput();

                        try {
                            if (BlancoResourceBundlePluginUtil.findFolder(
                                    input.getFile(), getProgressMonitor(),
                                    comboMetadir.getText()).exists() == false) {
                                BlancoResourceBundlePluginUtil.createFolder(
                                        input.getFile(), getProgressMonitor(),
                                        comboMetadir.getText());

                                MessageDialog.openInformation(top.getShell(),
                                        "メタディレクトリの作成", "メタディレクトリ ["
                                                + comboMetadir.getText()
                                                + "] を新規に作成しました。\n"
                                                + "※初回にのみ必要な動作です。");
                            }
                        } catch (CoreException e3) {
                            MessageDialog.openWarning(top.getShell(), fBundle
                                    .getMetafileDisplayname()
                                    + " 新規作成", "メタディレクトリ作成に失敗しました。処理中断します。:"
                                    + e3.toString());
                            e3.printStackTrace();
                            return;
                        }

                        try {
                            URL localURL = BlancoResourceBundlePlugin
                                    .getPluginRoot();

                            InputDialog inputDialog = new InputDialog(top
                                    .getShell(), fBundle
                                    .getMetafileDisplayname()
                                    + " 新規作成", fBundle.getMetafileDisplayname()
                                    + "(メタファイル)をメタディレクトリ["
                                    + comboMetadir.getText() + "]に作成します。"
                                    + fBundle.getMetafileDisplayname()
                                    + "の新規ファイル名を入力してください。",
                                    BlancoResourceBundleConstants.PRODUCT_NAME
                                            + "Meta.xls",
                                    new IInputValidator() {
                                        public String isValid(String newText) {
                                            if (newText.endsWith(".xls") == false) {
                                                return "ファイル名は .xlsで終了するように指定してください。";
                                            }
                                            if (newText.indexOf('/') >= 0
                                                    || newText.indexOf('\\') >= 0
                                                    || newText.indexOf(':') >= 0
                                                    || newText.indexOf(';') >= 0
                                                    || newText.indexOf('*') >= 0
                                                    || newText.indexOf('?') >= 0
                                                    || newText.indexOf('\"') >= 0
                                                    || newText.indexOf('>') >= 0
                                                    || newText.indexOf('<') >= 0
                                                    || newText.indexOf('|') >= 0) {
                                                return "ファイル名には / \\ : ; * ? \" > < | は利用しないでください。";
                                            }
                                            // 既にそのファイルが存在しないかどうかをチェック
                                            IFile newFileMeta = input.getFile()
                                                    .getProject().getFolder(
                                                            comboMetadir
                                                                    .getText())
                                                    .getFile(newText);
                                            if (newFileMeta.exists()) {
                                                return "指定のファイルは既に存在しています。"
                                                        + "別のファイル名を指定してください。";
                                            }
                                            return null;
                                        }
                                    });
                            if (inputDialog.open() == InputDialog.CANCEL) {
                                inputDialog.close();
                                return;
                            }
                            final String newMetaFilename = inputDialog
                                    .getValue();
                            inputDialog.close();

                            // プラグインのローカルからテンプレートを読み込みます。
                            InputStream inStream = new FileInputStream(
                                    localURL.getPath()
                                            + BlancoResourceBundlePluginConstants.TEMPLATE_FILENAME_WITHOUT_EXT
                                            + ".xls");
                            IFile newFileMeta = input.getFile().getProject()
                                    .getFolder(comboMetadir.getText()).getFile(
                                            newMetaFilename);
                            newFileMeta.create(inStream, true,
                                    getProgressMonitor());
                            inStream.close();
                            MessageDialog
                                    .openInformation(
                                            top.getShell(),
                                            fBundle.getMetafileDisplayname()
                                                    + " 新規作成",
                                            fBundle.getMetafileDisplayname()
                                                    + "(メタファイル ["
                                                    + newMetaFilename
                                                    + "] をメタディレクトリ["
                                                    + comboMetadir.getText()
                                                    + "]に新規作成しました。\n"
                                                    + "このファイルを編集して、リソースバンドル関連情報を設定してください。");
                        } catch (CoreException e1) {
                            e1.printStackTrace();
                            return;
                        } catch (IOException e2) {
                            MessageDialog.openWarning(top.getShell(), "メタファイル",
                                    "メタファイルのコピーに失敗しました。:" + e2.toString());
                            e2.printStackTrace();
                        }

                    }
                });

        // スペーサー
        new Label(top, SWT.NONE).setText("");

        buttonExecute = new Button(top, SWT.NONE);
        buttonExecute.setText("ソースコード生成");
        buttonExecute
                .addSelectionListener(new org.eclipse.swt.events.SelectionAdapter() {
                    public void widgetSelected(
                            org.eclipse.swt.events.SelectionEvent e) {
                        if (checkInputField() == false) {
                            return;
                        }

                        try {
                            if (checkInputField() == false) {
                                return;
                            }

                            final IFileEditorInput input = (IFileEditorInput) getEditorInput();
                            if (input.getFile().getProject().getFolder(
                                    comboMetadir.getText()).exists() == false) {
                                MessageDialog.openWarning(top.getShell(),
                                        "ソースコード生成", "メタディレクトリ ("
                                                + comboMetadir.getText()
                                                + ") が存在しません。処理を中断します。");
                                return;
                            }

                            showBusy(true);
                            // 再入を防止するために必要です。
                            buttonExecute.setEnabled(false);

                            BlancoResourceBundleGenerateAction
                                    .processBlancoResourceBundle(input
                                            .getFile(), top.getShell(),
                                            getProgressMonitor(), comboMetadir
                                                    .getText(),
                                            checkBoxFailOnMessageFormatError
                                                    .getSelection(),
                                            checkBoxIsLog.getSelection());

                            // blancoフォルダを同期
                            BlancoResourceBundlePluginUtil.refreshFolder(input
                                    .getFile(), getProgressMonitor(), "blanco");

                            // tmpフォルダを同期
                            BlancoResourceBundlePluginUtil.refreshFolder(input
                                    .getFile(), getProgressMonitor(), "tmp");

                            // resourcebundleフォルダを同期
                            // あくまでも保険として同期を取ります。
                            BlancoResourceBundlePluginUtil.refreshFolder(input
                                    .getFile(), getProgressMonitor(),
                                    comboMetadir.getText());
                        } catch (Exception ex) {
                            ex.printStackTrace();
                            StringWriter writer = new StringWriter();
                            ex.printStackTrace(new PrintWriter(writer));
                            writer.flush();
                            MessageDialog.openError(top.getShell(), "ソースコード生成",
                                    "回復不能なエラーが発生.:" + ex.toString() + ": "
                                            + writer.toString());
                        } finally {
                            showBusy(false);
                            // 再入を防止するために必要です。
                            buttonExecute.setEnabled(true);
                        }
                    }

                });

        // スペーサー
        new Label(top, SWT.NONE).setText("");

        checkBoxFailOnMessageFormatError = new Button(top, SWT.CHECK);
        checkBoxFailOnMessageFormatError
                .setText("文字列をMessageFormatで解析した際に例外が発生したら処理中断する。");
        checkBoxFailOnMessageFormatError.setSelection(true);

        // スペーサー
        new Label(top, SWT.NONE).setText("");

        checkBoxIsLog = new Button(top, SWT.CHECK);
        checkBoxIsLog.setText("ログ出力を自動生成されるソースコードに含める。");
        checkBoxIsLog.setSelection(true);

        // スペーサー
        new Label(top, SWT.NONE).setText("");

        labelSettingComment = new Label(top, SWT.NONE);
        labelSettingComment.setText("※各々の設定値は "
                + BlancoResourceBundlePluginConstants.TARGET_FILENAME
                + "ファイルに平文で格納されます。");
        labelSettingComment
                .setSize(new org.eclipse.swt.graphics.Point(500, 20));

        loadFromTextEditor();

        setFocusLostHandler(comboMetadir);
        setFocusLostHandler(checkBoxFailOnMessageFormatError);
        setFocusLostHandler(checkBoxIsLog);
    }

    /**
     * 読込処理を行います。
     * 
     * このメソッドは勝手に命名して作成しました。 <br>
     */
    protected void loadFromTextEditorInternal(final DOMResult argResult) {
        final Node nodeRoot = argResult.getNode();
        if (nodeRoot == null) {
            return;
        }
        final Element elementBlancoResourceBundle = BlancoXmlUtil.getElement(
                nodeRoot, "blanco/target/"
                        + BlancoResourceBundleConstants.PRODUCT_NAME_LOWER);
        if (elementBlancoResourceBundle == null) {
            return;
        }

        final String metadir = elementBlancoResourceBundle
                .getAttribute("metadir");
        if (metadir != null) {
            comboMetadir.setText(metadir);
        }

        final String failonmessageformaterror = elementBlancoResourceBundle
                .getAttribute("failonmessageformaterror");
        if (failonmessageformaterror != null) {
            checkBoxFailOnMessageFormatError.setSelection("true"
                    .equals(failonmessageformaterror));
        }

        final String log = elementBlancoResourceBundle.getAttribute("log");
        if (log != null) {
            checkBoxIsLog.setSelection("true".equals(log));
        }
    }

    /**
     * テキストエディタとしての保存処理を行います。
     */
    protected Document saveToTextEditorInternal() {
        final Document document = BlancoXmlUtil.newDocument();
        final Element eleBlanco = document.createElement("blanco");
        document.appendChild(eleBlanco);

        final Element eleTarget = document.createElement("target");
        eleTarget.setAttribute("name", textBlancoResourceBundleSettingId
                .getText());
        eleBlanco.appendChild(eleTarget);

        final Element eleBlancoDb = document
                .createElement(BlancoResourceBundleConstants.PRODUCT_NAME_LOWER);
        eleTarget.appendChild(eleBlancoDb);

        eleBlancoDb.setAttribute("metadir", comboMetadir.getText());
        eleBlancoDb.setAttribute("failonmessageformaterror",
                checkBoxFailOnMessageFormatError.getSelection() ? "true"
                        : "false");
        eleBlancoDb.setAttribute("log", checkBoxIsLog.getSelection() ? "true"
                : "false");
        return document;
    }

    /**
     * 入力項目のチェックを実施します。
     * 
     * エラーダイアログはこのメソッドで表示します。
     * 
     * @return エラーがあったらfalse。エラーが無ければtrue。
     */
    private final boolean checkInputField() {
        boolean isError = false;
        if (comboMetadir.getText().length() == 0) {
            comboMetadir.setBackground(top.getDisplay().getSystemColor(
                    SWT.COLOR_YELLOW));
            isError = true;
        } else {
            comboMetadir.setBackground(top.getDisplay().getSystemColor(
                    SWT.COLOR_INFO_BACKGROUND));
        }

        if (isError) {
            MessageDialog.openWarning(top.getShell(), "必須項目への入力が足りません",
                    "必須項目への入力が足りません。\nハイライト表示されている必須項目に値を入力してください");
        }

        return !isError;
    }

    /**
     * comboMetadirのための初期化処理
     */
    private void createComboMetadir() {
        comboMetadir = new Combo(top, SWT.NONE);
        comboMetadir.add("meta/resourcebundle");
        comboMetadir.select(0);
    }
}