/*
 * blanco Framework
 * Copyright (C) 2004-2009 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.jsf;

import java.io.File;
import java.util.List;

import blanco.cg.BlancoCgObjectFactory;
import blanco.cg.transformer.BlancoCgTransformerFactory;
import blanco.cg.valueobject.BlancoCgClass;
import blanco.cg.valueobject.BlancoCgField;
import blanco.cg.valueobject.BlancoCgMethod;
import blanco.cg.valueobject.BlancoCgSourceFile;
import blanco.commons.util.BlancoNameAdjuster;
import blanco.commons.util.BlancoNameUtil;
import blanco.commons.util.BlancoStringUtil;
import blanco.jsf.valueobject.BlancoJsfBeanItemStructure;
import blanco.jsf.valueobject.BlancoJsfStructure;

/**
 * 処理(blancoJsf)のためのプロキシ・クラス。
 */
class BlancoJsfExpandLogicProxy {
    /**
     * 内部的に利用するblancoCg用ファクトリ。
     */
    private BlancoCgObjectFactory fCgFactory = null;

    /**
     * 内部的に利用するblancoCg用ソースファイル情報。
     */
    private BlancoCgSourceFile fCgSourceFile = null;

    /**
     * 内部的に利用するblancoCg用クラス情報。
     */
    private BlancoCgClass fCgClass = null;

    /**
     * 自動生成するソースファイルの文字エンコーディング。
     */
    private String fEncoding = null;

    public void setEncoding(final String argEncoding) {
        fEncoding = argEncoding;
    }

    /**
     * 収集された情報を元に、ソースコードを自動生成します。
     * 
     * @param argProcessStructure
     *            メタファイルから収集できた処理構造データ。
     * @param argRuntimePackage
     *            ランタイムパッケージ。nullおよび長さ0の文字列の場合は定義書ごとにランタイムクラスを生成。
     * @param argTargetLang
     *            出力対象プログラミング言語。
     * @param argDirectoryTarget
     *            ソースコードの出力先フォルダ。
     */
    public void expandSourceFile(final BlancoJsfStructure argProcessStructure,
            final List<String> sqlClassNameList, final File argDirectoryTarget) {

        // 従来と互換性を持たせるため、/mainサブフォルダに出力します。
        final File fileBlancoMain = new File(argDirectoryTarget
                .getAbsolutePath()
                + "/main");

        fCgFactory = BlancoCgObjectFactory.getInstance();
        fCgSourceFile = fCgFactory.createSourceFile(argProcessStructure
                .getPackage(), null);
        fCgSourceFile.setEncoding(fEncoding);
        fCgClass = fCgFactory.createClass(
                getProcessInterfaceName(argProcessStructure), BlancoStringUtil
                        .null2Blank(argProcessStructure.getDescription()));
        fCgSourceFile.getClassList().add(fCgClass);

        fCgClass.setDescription("処理 ["
                + getProcessInterfaceName(argProcessStructure) + "]インタフェース。");
        fCgClass.getLangDoc().getDescriptionList().add(
                "このインタフェースを継承して [" + argProcessStructure.getPackage()
                        + "]パッケージに["
                        + getProcessInterfaceName(argProcessStructure)
                        + "]クラスを作成して実際のバッチ処理を実装してください。<br>");
        fCgClass.getLangDoc().getDescriptionList().add("");

        fCgSourceFile.getImportList().add("javax.el.ELContext");
        fCgSourceFile.getImportList().add("javax.el.ELResolver");
        fCgSourceFile.getImportList().add("javax.faces.context.FacesContext");

        expandMethodExecute(argProcessStructure, sqlClassNameList);

        if (argProcessStructure.getForward()) {
            final BlancoCgField field = fCgFactory.createField("fOutcome",
                    "java.lang.String", "処理結果。");
            field.setDefault("\"error\"");
            fCgClass.getFieldList().add(field);

            expandMethodOutcome(argProcessStructure);
        }

        BlancoCgTransformerFactory.getJavaSourceTransformer().transform(
                fCgSourceFile, fileBlancoMain);
    }

    /**
     * execute メソッドを展開します。
     * 
     * @param argProcessStructure
     *            メタファイルから収集できた処理構造データ。
     */
    private void expandMethodExecute(
            final BlancoJsfStructure argProcessStructure,
            final List<String> sqlClassNameList) {
        final BlancoCgMethod method = fCgFactory.createMethod("execute",
                "クラスをインスタンス化して処理を実行する際のエントリポイントです。");
        fCgClass.getMethodList().add(method);
        method.setFinal(true);

        method.getParameterList().add(
                fCgFactory.createParameter("event",
                        "javax.faces.event.ActionEvent", "イベント。"));

        method.getLineList().add(
                "final FacesContext ctx = FacesContext.getCurrentInstance();");
        method.getLineList().add("final ELContext elc = ctx.getELContext();");
        method
                .getLineList()
                .add(
                        "final ELResolver rslvr = ctx.getApplication().getELResolver();");

        method.getLineList().add("");

        for (BlancoJsfBeanItemStructure bean : argProcessStructure
                .getBeanList()) {
            final String classname = BlancoNameUtil.trimJavaPackage(bean
                    .getName());
            method.getLineList().add(
                    "// Managed Bean ["
                            + BlancoNameAdjuster.toLowerCaseTitle(classname)
                            + "] を取得します。");
            fCgSourceFile.getImportList().add(bean.getName());
            method.getLineList().add(
                    BlancoNameUtil.trimJavaPackage(bean.getName()) + " arg"
                            + classname + " = ("
                            + BlancoNameUtil.trimJavaPackage(bean.getName())
                            + ") rslvr.getValue(elc, null, \""
                            + BlancoNameAdjuster.toLowerCaseTitle(classname)
                            + "\");");
        }
        method.getLineList().add("");

        if (argProcessStructure.getSqlList().size() > 0) {
            addUtilImport(argProcessStructure);
            fCgSourceFile.getImportList().add("java.sql.Connection");
            method
                    .getLineList()
                    .add(
                            "final Connection conn = BlancoJsfProxyUtil.getConnection();");
            method.getLineList().add("boolean isLogicSuccess = false;");
        }

        for (String sqlClassName : sqlClassNameList) {
            fCgSourceFile.getImportList().add(sqlClassName);
            method.getLineList().add(
                    BlancoNameUtil.trimJavaPackage(sqlClassName) + " arg"
                            + BlancoNameUtil.trimJavaPackage(sqlClassName)
                            + " = new "
                            + BlancoNameUtil.trimJavaPackage(sqlClassName)
                            + "(conn);");
            method.getLineList().add("");
        }

        method.getLineList().add(
                "// 以下のコードでコンパイル・エラーが発生する場合には、クラス ["
                        + argProcessStructure.getPackage()
                        + "."
                        + BlancoNameAdjuster.toClassName(argProcessStructure
                                .getName()) + argProcessStructure.getSuffix()
                        + "Impl] を新規に作成してください。");
        method.getLineList().add(
                "final "
                        + BlancoNameAdjuster.toClassName(argProcessStructure
                                .getName())
                        + argProcessStructure.getSuffix()
                        + " logic = new "
                        + BlancoNameAdjuster.toClassName(argProcessStructure
                                .getName()) + argProcessStructure.getSuffix()
                        + "Impl();");

        {
            final StringBuffer buf = new StringBuffer();
            boolean isFirst = true;
            for (BlancoJsfBeanItemStructure bean : argProcessStructure
                    .getBeanList()) {
                final String classname = BlancoNameUtil.trimJavaPackage(bean
                        .getName());
                if (isFirst == false) {
                    buf.append(", ");
                } else {
                    isFirst = false;
                }
                buf.append("arg" + classname);
            }
            for (String sql : sqlClassNameList) {
                final String classname = BlancoNameUtil.trimJavaPackage(sql);
                if (isFirst == false) {
                    buf.append(", ");
                } else {
                    isFirst = false;
                }
                buf.append("arg" + classname);
            }

            method.getLineList().add("");
            method.getLineList().add("try {");
            {
                String line = "logic.execute(" + buf.toString() + ");";
                if (argProcessStructure.getForward()) {
                    line = "fOutcome = " + line;
                }
                method.getLineList().add(line);

                if (argProcessStructure.getSqlList().size() > 0) {
                    method.getLineList().add("conn.commit();");
                    method.getLineList().add("isLogicSuccess = true;");
                }
            }
            method.getLineList().add("} catch (Exception ex) {");
            method.getLineList().add("ex.printStackTrace();");
            method.getLineList().add("}");

            if (argProcessStructure.getSqlList().size() > 0) {
                method.getLineList().add("");
                method.getLineList().add("if (isLogicSuccess == false) {");
                method.getLineList().add("try {");
                method.getLineList().add("conn.rollback();");
                method.getLineList().add("} catch (Exception ex) {");
                method.getLineList().add("ex.printStackTrace();");
                method.getLineList().add("}");
                method.getLineList().add("}");
                method.getLineList().add("// コネクションの開放。");
                method.getLineList().add(
                        "BlancoJsfProxyUtil.releaseConnection(conn);");
            }
        }
    }

    private void expandMethodOutcome(
            final BlancoJsfStructure argProcessStructure) {
        final BlancoCgMethod method = fCgFactory.createMethod("outcome",
                "実行結果を取得。");
        fCgClass.getMethodList().add(method);
        method.setFinal(true);
        method.setReturn(fCgFactory.createReturn("java.lang.String", "次画面情報。"));
        method.getLineList().add("return fOutcome;");
    }

    private String getProcessInterfaceName(final BlancoJsfStructure argStructure) {
        return BlancoNameAdjuster.toClassName(argStructure.getName())
                + argStructure.getSuffix() + "Proxy";
    }

    /**
     * DB の getConnection が必要な場合に利用。
     * 
     * @param argProcessStructure
     */
    private void addUtilImport(final BlancoJsfStructure argProcessStructure) {
        fCgSourceFile.getImportList().add(
                argProcessStructure.getPackage() + ".BlancoJsfProxyUtil");
    }
}
