/*
 * 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.soap.actions;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;

import blanco.commons.io.File2StreamWrapper;
import blanco.commons.util.BlancoNameUtil;
import blanco.doclisting.BlancoDocListingXml2CombinedXmlTelegram;
import blanco.doclisting.BlancoDocListingXml2CombinedXmlTelegramProcess;
import blanco.plugin.soap.BlancoSOAPPlugin;
import blanco.plugin.soap.editors.BlancoSOAPEditor;
import blanco.plugin.soap.editors.BlancoSOAPPluginUtil;
import blanco.wsdl.BlancoWsdlMeta2Xml;
import blanco.wsdl.BlancoWsdlXml2Wsdl;
import blanco.wsdl.BlancoWsdlXml2Xsd;
import blanco.xsd.BlancoXsdXsdValidator;

/**
 * 電文処理定義書・電文定義書 (Excel) からWSDL・XSDを生成するアクション
 * 
 * @author IGA Tosiki
 */
public class BlancoSOAPGenerateAction {
    /**
     * GUIデバッグ用の低速モード。通常はfalseで運用します。
     */
    private static final boolean IS_DEBUG_SLOW = false;

    /**
     * blancoSOAPソースコード生成処理を実行します。
     * 
     * @param input
     * @return
     * @throws InvocationTargetException
     * @throws InterruptedException
     */
    public static final void processBlancoSOAP(final IFile ifile,
            final Shell shell, IProgressMonitor workbenchMonitor,
            final String metaDir, final String blancoWsdlDir,
            final boolean isGenerateXsdElementDeclaration)
            throws InvocationTargetException, InterruptedException {
        if (ifile.getProject().getFolder(metaDir).exists() == false) {
            MessageDialog.openWarning(shell, "ソースコード生成", "メタディレクトリ (" + metaDir
                    + ") が存在しません。処理を中断します。");
            return;
        }

        // Eclipse 3.3 対策: begin
        // workbenchMonitorを利用することを停止しました。
        // org.eclipse.swt.SWTException: Invalid thread access が発生してしまいます。
        // Eclipse 3.3 対策: end

        try {
            // PlatformUI.getWorkbench().getProgressService()を有益と判断。
            PlatformUI.getWorkbench().getProgressService().busyCursorWhile(
                    new IRunnableWithProgress() {
                        public void run(IProgressMonitor monitor)
                                throws InvocationTargetException,
                                InterruptedException {
                            try {
                                monitor.beginTask("blancoSOAPソースコード生成", 10);

                                if (monitor.isCanceled()) {
                                    return;
                                }

                                monitor.subTask("ディレクトリ: テンポラリフォルダの調整");

                                // テンポラリフォルダを一旦削除します。
                                BlancoSOAPPluginUtil.deleteFolder(ifile,
                                        monitor, "tmp/soap/wsdl");
                                BlancoSOAPPluginUtil.createFolder(ifile,
                                        monitor, "tmp/soap/wsdl");
                                BlancoSOAPPluginUtil.createFolder(ifile,
                                        monitor, "tmp/soap/wsdl/tmp");
                                BlancoSOAPPluginUtil.createFolder(ifile,
                                        monitor, blancoWsdlDir);

                                final IFolder folderBlancoWsdl = BlancoSOAPPluginUtil
                                        .findFolder(ifile, monitor,
                                                blancoWsdlDir);
                                folderBlancoWsdl.refreshLocal(
                                        IResource.DEPTH_INFINITE, monitor);

                                final IFolder folderDirSoapWsdl2 = BlancoSOAPPluginUtil
                                        .findFolder(ifile, monitor,
                                                "tmp/soap/wsdl");
                                final IFolder folderDirSoapWsdlTmp = BlancoSOAPPluginUtil
                                        .findFolder(ifile, monitor,
                                                "tmp/soap/wsdl/tmp");
                                folderDirSoapWsdlTmp.refreshLocal(
                                        IResource.DEPTH_INFINITE, monitor);

                                final IFolder folderDirDoclisting = BlancoSOAPPluginUtil
                                        .findFolder(ifile, monitor,
                                                "tmp/doclisting");
                                folderDirDoclisting.refreshLocal(
                                        IResource.DEPTH_INFINITE, monitor);

                                if (monitor.isCanceled()) {
                                    return;
                                }

                                monitor
                                        .subTask(BlancoSOAPEditor.BLANCO_META_FILE_TELEGRAM
                                                + " (*.xls)を処理します");
                                monitor.worked(1);

                                final IFolder folderDirMetaDir = BlancoSOAPPluginUtil
                                        .findFolder(ifile, monitor, metaDir);
                                folderDirMetaDir.refreshLocal(
                                        IResource.DEPTH_INFINITE, monitor);
                                final IResource[] resourcesMetaFiles = folderDirMetaDir
                                        .members();
                                for (int index = 0; index < resourcesMetaFiles.length; index++) {
                                    if (monitor.isCanceled()) {
                                        return;
                                    }

                                    if (resourcesMetaFiles[index] instanceof IFile) {
                                        IFile fileLookMetaFile = (IFile) resourcesMetaFiles[index];
                                        if (fileLookMetaFile.getFileExtension()
                                                .equals("xls") == false) {
                                            // xls以外の拡張子のファイルはスキップします。");
                                            continue;
                                        }

                                        final IFile fileTargetXml = folderDirSoapWsdlTmp
                                                .getFile(fileLookMetaFile
                                                        .getName()
                                                        + ".xml");
                                        monitor.subTask("メタファイル処理: ファイル["
                                                + fileLookMetaFile.getName()
                                                + "]を処理します (" + (index + 1)
                                                + "/"
                                                + resourcesMetaFiles.length
                                                + ")");

                                        final InputStream inStream = fileLookMetaFile
                                                .getContents();
                                        try {
                                            new File2StreamWrapper(null,
                                                    fileTargetXml.getLocation()
                                                            .toFile()) {
                                                protected void process(
                                                        InputStream inStreamIgnore,
                                                        OutputStream outStream)
                                                        throws Exception {
                                                    new BlancoWsdlMeta2Xml()
                                                            .process(inStream,
                                                                    outStream);
                                                    outStream.flush();
                                                }
                                            }.run();
                                        } finally {
                                            // 外で作ったストリームは外で閉じます。
                                            inStream.close();
                                        }

                                        if (IS_DEBUG_SLOW) {
                                            Thread.sleep(1000);
                                        }
                                    }
                                }

                                if (monitor.isCanceled()) {
                                    return;
                                }

                                monitor.subTask("WSDL/XSD作成");
                                monitor.worked(1);

                                // 出力は blanco.wsdlフォルダとします。(その値はデフォルト値の場合の例)
                                final File fileBlancoWsdl = folderBlancoWsdl
                                        .getLocation().toFile();

                                final BlancoWsdlXml2Wsdl xml2wsdl = new BlancoWsdlXml2Wsdl();
                                folderDirSoapWsdlTmp.refreshLocal(
                                        IResource.DEPTH_INFINITE, monitor);
                                final IResource[] resourcesXmlWsdlFiles = folderDirSoapWsdlTmp
                                        .members();
                                for (int index = 0; index < resourcesXmlWsdlFiles.length; index++) {
                                    if (monitor.isCanceled()) {
                                        return;
                                    }
                                    if (resourcesXmlWsdlFiles[index] instanceof IFile) {
                                        final IFile fileLookMetaWorkXml = (IFile) resourcesXmlWsdlFiles[index];
                                        if (fileLookMetaWorkXml
                                                .getFileExtension().equals(
                                                        "xml") == false) {
                                            // 拡張子がxml以外のファイルはスキップします。
                                            continue;
                                        }

                                        monitor.subTask("WSDL/XSD作成: ファイル["
                                                + fileLookMetaWorkXml.getName()
                                                + "]を処理します (" + (index + 1)
                                                + "/"
                                                + resourcesXmlWsdlFiles.length
                                                + ")");

                                        try {
                                            // WSDL処理よりもXSD処理を先行させる必要があります。
                                            final BlancoWsdlXml2Xsd xml2xsd = new BlancoWsdlXml2Xsd();
                                            if (isGenerateXsdElementDeclaration) {
                                                xml2xsd
                                                        .setGenerateXsdElementDeclaration(true);
                                            }
                                            xml2xsd.process(fileLookMetaWorkXml
                                                    .getLocation().toFile(),
                                                    fileBlancoWsdl);

                                            // XSD処理で生成されたXSDファイルをWSDL処理で利用しています。
                                            xml2wsdl.parse(fileLookMetaWorkXml
                                                    .getLocation().toFile(),
                                                    fileBlancoWsdl);
                                        } catch (IllegalArgumentException ex) {
                                            throw new IllegalArgumentException(

                                                    "ファイル["
                                                            + fileLookMetaWorkXml
                                                                    .getName()
                                                            + "]の処理中にエラーが発生しました。処理を中断します。\n\n"
                                                            + ex.toString());
                                        }

                                        if (IS_DEBUG_SLOW) {
                                            Thread.sleep(1000);
                                        }
                                    }
                                }

                                xml2wsdl.generate(fileBlancoWsdl);

                                folderBlancoWsdl.refreshLocal(
                                        IResource.DEPTH_INFINITE, monitor);
                                final IResource[] resourcesXmlXsdFiles = folderBlancoWsdl
                                        .members();
                                for (int index = 0; index < resourcesXmlXsdFiles.length; index++) {
                                    if (monitor.isCanceled()) {
                                        return;
                                    }
                                    if (resourcesXmlXsdFiles[index] instanceof IFile) {
                                        final IFile fileLookMetaWorkXsd = (IFile) resourcesXmlXsdFiles[index];
                                        if (fileLookMetaWorkXsd
                                                .getFileExtension().equals(
                                                        "xsd") == false) {
                                            // 拡張子がxsd以外のファイルはスキップします。
                                            continue;
                                        }

                                        monitor.subTask("XSDチェック: ファイル["
                                                + fileLookMetaWorkXsd.getName()
                                                + "]を処理します (" + (index + 1)
                                                + "/"
                                                + resourcesXmlXsdFiles.length
                                                + ")");

                                        try {
                                            // XSDファイルの妥当性検証を行います。
                                            final String fileNameFinal = BlancoNameUtil
                                                    .trimFileExtension(fileLookMetaWorkXsd
                                                            .getName());
                                            new File2StreamWrapper(
                                                    fileLookMetaWorkXsd
                                                            .getLocation()
                                                            .toFile(), null) {
                                                protected void process(
                                                        InputStream inStream,
                                                        OutputStream outStreamIgnore)
                                                        throws Exception {
                                                    new BlancoXsdXsdValidator()
                                                            .process(inStream,
                                                                    fileNameFinal);
                                                }
                                            }.run();
                                        } catch (IllegalArgumentException ex) {
                                            throw new IllegalArgumentException(

                                                    "ファイル["
                                                            + fileLookMetaWorkXsd
                                                                    .getName()
                                                            + "]の処理中にエラーが発生しました。処理を中断します。\n\n"
                                                            + ex.toString());
                                        }

                                        if (IS_DEBUG_SLOW) {
                                            Thread.sleep(1000);
                                        }
                                    }
                                }

                                if (monitor.isCanceled()) {
                                    return;
                                }

                                final File fileDoclistingInput = folderDirSoapWsdlTmp
                                        .getLocation().toFile();
                                final File fileDoclistingOutput = folderDirDoclisting
                                        .getLocation().toFile();

                                monitor
                                        .subTask("一覧更新: 電文処理定義書・電文定義書の一覧のための中間ファイルを更新。");
                                new BlancoDocListingXml2CombinedXmlTelegramProcess()
                                        .process(fileDoclistingInput,
                                                fileDoclistingOutput);
                                new BlancoDocListingXml2CombinedXmlTelegram()
                                        .process(fileDoclistingInput,
                                                fileDoclistingOutput);

                                if (monitor.isCanceled()) {
                                    return;
                                }

                                monitor.subTask("フォルダ更新: WSDLメタファイル・フォルダを更新");
                                monitor.worked(1);
                                folderBlancoWsdl.refreshLocal(
                                        IResource.DEPTH_INFINITE, monitor);

                                if (IS_DEBUG_SLOW) {
                                    Thread.sleep(1000);
                                }
                            } catch (Exception e) {
                                // 最終防衛ライン
                                e.printStackTrace();
                                throw new InvocationTargetException(e, e
                                        .toString());
                            } catch (Error e) {
                                // 最終防衛ライン
                                e.printStackTrace();
                                throw new InvocationTargetException(e, e
                                        .toString());
                            } finally {
                                // 最後の最後では done扱いとします。
                                monitor.done();
                            }
                        }
                    });
        } catch (InvocationTargetException ex) {
            BlancoSOAPPlugin.log(ex);
            MessageDialog.openWarning(shell, "ソースコード生成",
                    "例外が発生しました。処理を中断します。\n" + ex.getCause().toString());
            ex.printStackTrace();
        } catch (InterruptedException ex) {
            MessageDialog.openWarning(shell, "ソースコード生成",
                    "割り込み中断例外が発生しました。処理を中断します。\n" + ex.toString());
            ex.printStackTrace();
        } catch (Exception ex) {
            MessageDialog.openWarning(shell, "ソースコード生成",
                    "予期せぬ例外が発生しました。処理を中断します。\n" + ex.toString());
            ex.printStackTrace();
        }
    }
}