package blanco.plugin.csv.actions;

import java.io.File;
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.csv.BlancoCsvConstants;
import blanco.csv.BlancoCsvDotNetMeta2Xml;
import blanco.csv.BlancoCsvMeta2Xml;
import blanco.csv.BlancoCsvXml2CsClass;
import blanco.csv.BlancoCsvXml2JavaClass;
import blanco.csv.resourcebundle.BlancoCsvResourceBundle;
import blanco.doclisting.BlancoDocListingXml2CombinedXmlCsv;
import blanco.plugin.csv.BlancoCsvPlugin;
import blanco.plugin.csv.BlancoCsvPluginConstants;
import blanco.plugin.csv.editors.BlancoCsvPluginUtil;

public class BlancoCsvGenerateAction {
    private static final BlancoCsvResourceBundle fBundle = new BlancoCsvResourceBundle();

    /**
     * blancoCsv Javaソースコード生成処理を実行します。
     * 
     * @param ifile
     *            処理対象のファイル。
     * @param shell
     *            親のシェル。
     * @param workbenchMonitor
     *            ワークベンチモニタ。
     * @param runtimePackage
     *            ランタイムパッケージ。無指定はnullで表現。
     * @param metaDir
     *            メタディレクトリ。
     * @throws InvocationTargetException
     *             実行時の例外。
     * @throws InterruptedException
     *             割り込み例外。
     */
    public static final void processBlancoCsvJavaSource(final IFile ifile,
            final Shell shell, IProgressMonitor workbenchMonitor,
            final String runtimePackage, final String metaDir)
            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(final IProgressMonitor monitor)
                                throws InvocationTargetException,
                                InterruptedException {
                            try {
                                monitor.beginTask(
                                        BlancoCsvConstants.PRODUCT_NAME
                                                + "ソースコード生成", 10);

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

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

                                // テンポラリフォルダを一旦削除します。
                                BlancoCsvPluginUtil
                                        .deleteFolder(
                                                ifile,
                                                monitor,
                                                "tmp"
                                                        + BlancoCsvConstants.TARGET_SUBDIRECTORY);
                                BlancoCsvPluginUtil
                                        .createFolder(
                                                ifile,
                                                monitor,
                                                "tmp"
                                                        + BlancoCsvConstants.TARGET_SUBDIRECTORY);
                                BlancoCsvPluginUtil
                                        .createFolder(
                                                ifile,
                                                monitor,
                                                "tmp"
                                                        + BlancoCsvConstants.TARGET_SUBDIRECTORY
                                                        + BlancoCsvConstants.VALUE_OBJECT_DIRECTORY);
                                final IFolder tempFolder = BlancoCsvPluginUtil
                                        .findFolder(
                                                ifile,
                                                monitor,
                                                "tmp"
                                                        + BlancoCsvConstants.TARGET_SUBDIRECTORY);
                                final IFolder tempValueObjectFolder = BlancoCsvPluginUtil
                                        .findFolder(
                                                ifile,
                                                monitor,
                                                "tmp"
                                                        + BlancoCsvConstants.TARGET_SUBDIRECTORY
                                                        + BlancoCsvConstants.VALUE_OBJECT_DIRECTORY);

                                // ドキュメント一覧のための中間ファイル用ディレクトリ。
                                final File blancoTempDirectoryDocListing = ifile
                                        .getProject().getFolder(
                                                "tmp/doclisting").getLocation()
                                        .toFile();
                                blancoTempDirectoryDocListing.mkdirs();

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

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

                                final IFolder findFolder = BlancoCsvPluginUtil
                                        .findFolder(ifile, monitor, metaDir);
                                findFolder.refreshLocal(
                                        IResource.DEPTH_INFINITE, monitor);

                                new BlancoCsvMeta2Xml() {
                                    protected boolean progress(
                                            final int progressCurrent,
                                            final int progressTotal,
                                            final String progressItem) {
                                        if (monitor.isCanceled()) {
                                            // 処理中断します。
                                            return false;
                                        }

                                        monitor.subTask("メタファイル処理: ファイル["
                                                + progressItem + "]を処理します ("
                                                + progressCurrent + "/"
                                                + progressTotal + ")");

                                        if (BlancoCsvPluginConstants.IS_DEBUG) {
                                            try {
                                                Thread.sleep(10000);
                                            } catch (InterruptedException e) {
                                                e.printStackTrace();
                                            }
                                        }

                                        return true;
                                    }
                                }.processDirectory(findFolder.getLocation()
                                        .toFile(), tempFolder.getLocation()
                                        .toFile().getAbsolutePath());

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

                                monitor.subTask("フォルダ更新: tmpフォルダ");
                                monitor.worked(1);

                                tempFolder.refreshLocal(
                                        IResource.DEPTH_INFINITE, monitor);

                                monitor.subTask("フォルダ作成: blancoフォルダ");

                                IFolder folderTargetDirectory = ifile
                                        .getProject().getFolder("blanco");
                                if (ifile.getProject().getFolder(metaDir)
                                        .exists() == false) {
                                    folderTargetDirectory.create(true, true,
                                            monitor);
                                }

                                monitor.subTask("フォルダ作成: blanco/mainフォルダ");
                                final IFolder folderTargetDirectoryWithMain = folderTargetDirectory
                                        .getFolder("main");
                                if (ifile.getProject().getFolder(metaDir)
                                        .exists() == false) {
                                    folderTargetDirectoryWithMain.create(true,
                                            true, monitor);
                                }

                                File blancoTargetDirectory = folderTargetDirectory
                                        .getLocation().toFile();

                                monitor.subTask("Javaソースコード作成");
                                monitor.worked(1);

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

                                        monitor.subTask("Javaソースコード作成: ファイル["
                                                + fileLook.getName()
                                                + "]を処理します (" + (index + 1)
                                                + "/" + resourcesXml2.length
                                                + ")");

                                        try {
                                            new BlancoCsvXml2JavaClass()
                                                    .process(
                                                            fileLook
                                                                    .getLocation()
                                                                    .toFile(),
                                                            runtimePackage,
                                                            tempValueObjectFolder
                                                                    .getLocation()
                                                                    .toFile(),
                                                            blancoTargetDirectory,
                                                            false);
                                        } catch (IllegalArgumentException ex) {
                                            throw new IllegalArgumentException(

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

                                        if (BlancoCsvPluginConstants.IS_DEBUG) {
                                            Thread.sleep(1000);
                                        }

                                    }
                                }

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

                                monitor.subTask("ドキュメント一覧のための中間ファイルを作成。");
                                monitor.worked(1);

                                // ドキュメント一覧のための中間ファイルを作成。
                                final BlancoDocListingXml2CombinedXmlCsv doclistingCsv = new BlancoDocListingXml2CombinedXmlCsv();
                                doclistingCsv.process(tempFolder.getLocation()
                                        .toFile(),
                                        blancoTempDirectoryDocListing);

                                monitor.subTask("フォルダ更新: blancoフォルダを更新");
                                monitor.worked(1);

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

                                folderTargetDirectory.refreshLocal(
                                        IResource.DEPTH_INFINITE, monitor);

                                if (BlancoCsvPluginConstants.IS_DEBUG) {
                                    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) {
            BlancoCsvPlugin.log(ex);
            // 例外の中から本当の原因を取り出すことがポイントです。
            MessageDialog.openWarning(shell, "ソースコード生成",
                    "例外が発生しました。処理を中断します。\n" + ex.getCause().toString());
            ex.printStackTrace();
        } catch (InterruptedException ex) {
            BlancoCsvPlugin.log(ex);
            MessageDialog.openWarning(shell, "ソースコード生成",
                    "割り込み中断例外が発生しました。処理を中断します。\n" + ex.toString());
            ex.printStackTrace();
        } catch (Exception ex) {
            BlancoCsvPlugin.log(ex);
            MessageDialog.openWarning(shell, "ソースコード生成",
                    "予期せぬ例外が発生しました。処理を中断します。\n" + ex.toString());
            ex.printStackTrace();
        } catch (Error er) {
            BlancoCsvPlugin.log(er);
            MessageDialog.openWarning(shell, "ソースコード生成",
                    "予期せぬエラーが発生しました。処理を中断します。\n" + er.toString());
            er.printStackTrace();
        }
    }

    /**
     * blancoCsv C#.NETソースコード生成処理を実行します。
     * 
     * @param ifile
     *            処理対象のファイル。
     * @param shell
     *            親のシェル。
     * @param workbenchMonitor
     *            ワークベンチモニタ。
     * @param runtimePackage
     *            ランタイムパッケージ。無指定はnullで表現。
     * @param metaDir
     *            メタディレクトリ。
     * @throws InvocationTargetException
     *             実行時の例外。
     * @throws InterruptedException
     *             割り込み例外。
     */
    public static final void processBlancoCsvCsSource(final IFile ifile,
            final Shell shell, IProgressMonitor workbenchMonitor,
            final String runtimePackage, final String metaDir)
            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(final IProgressMonitor monitor)
                                throws InvocationTargetException,
                                InterruptedException {
                            try {
                                monitor.beginTask(
                                        BlancoCsvConstants.PRODUCT_NAME
                                                + "ソースコード生成", 10);

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

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

                                // テンポラリフォルダを一旦削除します。
                                BlancoCsvPluginUtil
                                        .deleteFolder(
                                                ifile,
                                                monitor,
                                                "tmp"
                                                        + BlancoCsvConstants.TARGET_SUBDIRECTORY);
                                BlancoCsvPluginUtil
                                        .createFolder(
                                                ifile,
                                                monitor,
                                                "tmp"
                                                        + BlancoCsvConstants.TARGET_SUBDIRECTORY);
                                BlancoCsvPluginUtil
                                        .createFolder(
                                                ifile,
                                                monitor,
                                                "tmp"
                                                        + BlancoCsvConstants.TARGET_SUBDIRECTORY
                                                        + BlancoCsvConstants.VALUE_OBJECT_DIRECTORY);
                                final IFolder tempFolder = BlancoCsvPluginUtil
                                        .findFolder(
                                                ifile,
                                                monitor,
                                                "tmp"
                                                        + BlancoCsvConstants.TARGET_SUBDIRECTORY);
                                final IFolder tempValueObjectFolder = BlancoCsvPluginUtil
                                        .findFolder(
                                                ifile,
                                                monitor,
                                                "tmp"
                                                        + BlancoCsvConstants.TARGET_SUBDIRECTORY
                                                        + BlancoCsvConstants.VALUE_OBJECT_DIRECTORY);

                                // ドキュメント一覧のための中間ファイル用ディレクトリ。
                                final File blancoTempDirectoryDocListing = ifile
                                        .getProject().getFolder(
                                                "tmp/doclisting").getLocation()
                                        .toFile();
                                blancoTempDirectoryDocListing.mkdirs();

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

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

                                final IFolder findFolder = BlancoCsvPluginUtil
                                        .findFolder(ifile, monitor, metaDir);
                                findFolder.refreshLocal(
                                        IResource.DEPTH_INFINITE, monitor);

                                new BlancoCsvDotNetMeta2Xml() {
                                    protected boolean progress(
                                            final int progressCurrent,
                                            final int progressTotal,
                                            final String progressItem) {
                                        if (monitor.isCanceled()) {
                                            // 処理中断します。
                                            return false;
                                        }

                                        monitor.subTask("メタファイル処理: ファイル["
                                                + progressItem + "]を処理します ("
                                                + progressCurrent + "/"
                                                + progressTotal + ")");

                                        if (BlancoCsvPluginConstants.IS_DEBUG) {
                                            try {
                                                Thread.sleep(10000);
                                            } catch (InterruptedException e) {
                                                e.printStackTrace();
                                            }
                                        }

                                        return true;
                                    }
                                }.processDirectory(findFolder.getLocation()
                                        .toFile(), tempFolder.getLocation()
                                        .toFile().getAbsolutePath());

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

                                monitor.subTask("フォルダ更新: tmpフォルダ");
                                monitor.worked(1);

                                tempFolder.refreshLocal(
                                        IResource.DEPTH_INFINITE, monitor);

                                monitor.subTask("フォルダ作成: blanco.csフォルダ");

                                IFolder folderTargetDirectory = ifile
                                        .getProject().getFolder("blanco.cs");
                                if (ifile.getProject().getFolder(metaDir)
                                        .exists() == false) {
                                    folderTargetDirectory.create(true, true,
                                            monitor);
                                }

                                monitor.subTask("フォルダ作成: blanco.cs/mainフォルダ");
                                final IFolder folderTargetDirectoryWithMain = folderTargetDirectory
                                        .getFolder("main");
                                if (ifile.getProject().getFolder(metaDir)
                                        .exists() == false) {
                                    folderTargetDirectoryWithMain.create(true,
                                            true, monitor);
                                }

                                File blancoTargetDirectory = folderTargetDirectory
                                        .getLocation().toFile();

                                monitor.subTask("C#.NETソースコード作成");
                                monitor.worked(1);

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

                                        monitor.subTask("C#.NETソースコード作成: ファイル["
                                                + fileLook.getName()
                                                + "]を処理します (" + (index + 1)
                                                + "/" + resourcesXml2.length
                                                + ")");

                                        try {
                                            new BlancoCsvXml2CsClass().process(
                                                    fileLook.getLocation()
                                                            .toFile(),
                                                    runtimePackage,
                                                    tempValueObjectFolder
                                                            .getLocation()
                                                            .toFile(),
                                                    blancoTargetDirectory);
                                        } catch (IllegalArgumentException ex) {
                                            throw new IllegalArgumentException(

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

                                        if (BlancoCsvPluginConstants.IS_DEBUG) {
                                            Thread.sleep(1000);
                                        }

                                    }
                                }

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

                                monitor.subTask("ドキュメント一覧のための中間ファイルを作成。");
                                monitor.worked(1);

                                // ドキュメント一覧のための中間ファイルを作成。
                                final BlancoDocListingXml2CombinedXmlCsv doclistingCsv = new BlancoDocListingXml2CombinedXmlCsv();
                                doclistingCsv.process(tempFolder.getLocation()
                                        .toFile(),
                                        blancoTempDirectoryDocListing);

                                monitor.subTask("フォルダ更新: blanco.csフォルダを更新");
                                monitor.worked(1);

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

                                folderTargetDirectory.refreshLocal(
                                        IResource.DEPTH_INFINITE, monitor);

                                if (BlancoCsvPluginConstants.IS_DEBUG) {
                                    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) {
            BlancoCsvPlugin.log(ex);
            // 例外の中から本当の原因を取り出すことがポイントです。
            MessageDialog.openWarning(shell, "ソースコード生成",
                    "例外が発生しました。処理を中断します。\n" + ex.getCause().toString());
            ex.printStackTrace();
        } catch (InterruptedException ex) {
            BlancoCsvPlugin.log(ex);
            MessageDialog.openWarning(shell, "ソースコード生成",
                    "割り込み中断例外が発生しました。処理を中断します。\n" + ex.toString());
            ex.printStackTrace();
        } catch (Exception ex) {
            BlancoCsvPlugin.log(ex);
            MessageDialog.openWarning(shell, "ソースコード生成",
                    "予期せぬ例外が発生しました。処理を中断します。\n" + ex.toString());
            ex.printStackTrace();
        } catch (Error er) {
            BlancoCsvPlugin.log(er);
            MessageDialog.openWarning(shell, "ソースコード生成",
                    "予期せぬエラーが発生しました。処理を中断します。\n" + er.toString());
            er.printStackTrace();
        }
    }
}