/*******************************************************************************
 * Copyright (c) 2009 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) 2009 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.pdf.report.task;

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

import blanco.cg.BlancoCgObjectFactory;
import blanco.cg.transformer.java.BlancoCgJavaSourceTransformer;
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.BlancoStringUtil;
import blanco.pdf.BlancoPdfConstants;
import blanco.pdf.page.BlancoPdfPageUtil;
import blanco.pdf.page.valueobject.BlancoPdfBlockDef;
import blanco.pdf.page.valueobject.BlancoPdfItemDef;
import blanco.pdf.page.valueobject.BlancoPdfPageDef;
import blanco.pdf.report.task.valueobject.BlancoPdfReportProcessInput;
import blanco.pdf.valueobject.BlancoPdfSetting;

/**
 * XML ファイルからソースコードを自動生成。
 * 
 * @author Tosiki IGA
 */
public class BlancoPdfReportProcessImpl implements BlancoPdfReportProcess {
    protected BlancoPdfReportProcessInput fInput = null;

    /**
     * {@inheritDoc}
     */
    public int execute(final BlancoPdfReportProcessInput input)
            throws IOException, IllegalArgumentException {
        fInput = input;

        final File pageDir = new File(input.getConfdir() + "/page");

        final File[] pageFiles = pageDir.listFiles();
        if (pageFiles == null) {
            return -1;
        }

        for (File look : pageFiles) {
            if (look == null) {
                continue;
            }
            if (look.isDirectory()) {
                continue;
            }

            if (look.getName().endsWith(BlancoPdfConstants.FILEEXT_PDFPAGEDEF) == false) {
                continue;
            }

            final String pageName = look.getName().substring(
                    0,
                    look.getName().length()
                            - BlancoPdfConstants.FILEEXT_PDFPAGEDEF.length());
            if (input.getVerbose()) {
                progress("pageName: " + pageName);
            }

            BlancoPdfPageDef pageDef = null;
            {
                // TODO マルチフォーム対応。
                final BlancoPdfSetting setting = new BlancoPdfSetting();
                setting.setConfPath(input.getConfdir());
                pageDef = BlancoPdfPageUtil.read(setting, pageName);
            }

            new File("./tmp");

            new BlancoPdfReportGenerateValueObject(fInput)
                    .generateItemBean(pageDef);
            generatePdfReport(pageDef);
        }

        return 0;
    }

    void generatePdfReport(final BlancoPdfPageDef pageDef) {
        final BlancoCgObjectFactory cgFactory = BlancoCgObjectFactory
                .getInstance();
        final BlancoCgSourceFile cgSourceFile = cgFactory.createSourceFile(
                pageDef.getPackage(), null);
        final BlancoCgClass cgClass = cgFactory.createClass(BlancoNameAdjuster
                .toClassName(pageDef.getName())
                + "PdfReport", "PDF レポート。");
        cgSourceFile.getClassList().add(cgClass);

        {
            final BlancoCgField field = cgFactory.createField("fPageDef",
                    "blanco.pdf.page.valueobject.BlancoPdfPageDef",
                    "ページ定義オブジェクト。");
            cgClass.getFieldList().add(field);
        }

        {
            final BlancoCgField field = cgFactory.createField("fPageWriter",
                    "blanco.pdf.page.BlancoPdfPageWriter", "ページ・ライター・オブジェクト。");
            cgClass.getFieldList().add(field);
        }

        {
            final BlancoCgMethod method = cgFactory.createMethod(
                    BlancoNameAdjuster.toClassName(pageDef.getName())
                            + "PdfReport", "1 ページ分のデータをセットします。");
            cgClass.getMethodList().add(method);
            method.setConstructor(true);
            method.getParameterList().add(
                    cgFactory.createParameter("pdfSetting",
                            "blanco.pdf.valueobject.BlancoPdfSetting",
                            "PDF 項目群。"));
            method.getParameterList().add(
                    cgFactory.createParameter("outStream",
                            "java.io.OutputStream", "出力ストリーム。"));
            cgSourceFile.getImportList().add(
                    "blanco.pdf.page.BlancoPdfPageUtil");
            method.getLineList().add(
                    "fPageDef = BlancoPdfPageUtil.read(pdfSetting, \""
                            + pageDef.getName() + "\");");
            method
                    .getLineList()
                    .add(
                            "fPageWriter = new BlancoPdfPageWriter(pdfSetting.getConfPath(), fPageDef, outStream);");
        }

        {
            final BlancoCgMethod method = cgFactory.createMethod("writeItems",
                    "1 ページ分のデータを PDF に書き出します。");
            cgClass.getMethodList().add(method);
            method.getParameterList().add(
                    cgFactory.createParameter("pdfItems", pageDef.getPackage()
                            + ".items."
                            + BlancoNameAdjuster.toClassName(pageDef.getName())
                            + "PdfReportItems", "PDF 項目群。"));

            // ブロックを処理。
            for (BlancoPdfBlockDef blockDef : pageDef.getBlockList()) {
                method.getLineList().add(
                        "for (int occur = 0; occur < pdfItems.get"
                                + BlancoNameAdjuster.toClassName(blockDef
                                        .getName())
                                + "List().size(); occur++) {");
                cgSourceFile.getImportList().add(
                        pageDef.getPackage()
                                + ".items."
                                + BlancoNameAdjuster.toClassName(pageDef
                                        .getName())
                                + "PdfReport"
                                + BlancoNameAdjuster.toClassName(blockDef
                                        .getName()) + "Items");
                method.getLineList().add(
                        "final "
                                + BlancoNameAdjuster.toClassName(pageDef
                                        .getName())
                                + "PdfReport"
                                + BlancoNameAdjuster.toClassName(blockDef
                                        .getName())
                                + "Items blockItems = pdfItems.get"
                                + BlancoNameAdjuster.toClassName(blockDef
                                        .getName()) + "List().get(occur);");
                for (BlancoPdfItemDef itemDef : blockDef.getItemList()) {
                    processItem(itemDef, true, blockDef, cgSourceFile, method);
                }
                method.getLineList().add("}");
            }

            for (BlancoPdfItemDef itemDef : pageDef.getItemList()) {
                processItem(itemDef, false, null, cgSourceFile, method);
            }
        }

        {
            final BlancoCgMethod method = cgFactory.createMethod(
                    "writeItemsList", "ページをすべて出力します。");
            cgClass.getMethodList().add(method);
            method.getParameterList().add(
                    cgFactory.createParameter("itemsList", "java.util.List<"
                            + BlancoNameAdjuster.toClassName(pageDef.getName())
                            + "PdfReportItems>", "出力したい項目群。"));
            method.getLineList().add("boolean isFirst = true;");
            method.getLineList().add(
                    "for (" + BlancoNameAdjuster.toClassName(pageDef.getName())
                            + "PdfReportItems items : itemsList) {");
            method.getLineList().add("if (isFirst) {");
            method.getLineList().add("isFirst = false;");
            method.getLineList().add("} else {");
            method.getLineList().add("nextPage();");
            method.getLineList().add("}");
            method.getLineList().add("writeItems(items);");
            method.getLineList().add("}");
        }

        {
            final BlancoCgMethod method = cgFactory.createMethod("nextPage",
                    "改ページします。");
            cgClass.getMethodList().add(method);
            method.getLineList().add("fPageWriter.nextPage();");
        }

        {
            final BlancoCgMethod method = cgFactory.createMethod("normalize",
                    "正規化の実施。ページブレークなどの処理をおこないます。");
            cgClass.getMethodList().add(method);
            method.getParameterList().add(
                    cgFactory.createParameter("itemsList", "java.util.List<"
                            + BlancoNameAdjuster.toClassName(pageDef.getName())
                            + "PdfReportItems>", "正規化したい項目群。"));
            method.getLineList().add("consolidatePage(itemsList);");
            method.getLineList().add("breakPageByRowCount(itemsList);");
            method.getLineList().add("processPageContinuity(itemsList);");
            method.getLineList().add("omitDitto(itemsList);");
            method.getLineList().add("");
            method.getLineList().add("// ページ関連情報の埋め込み。");
            method.getLineList().add("applyPageNo(itemsList);");
        }

        new BlancoPdfReportGeneratePageOperation().consolidatePage(pageDef,
                cgFactory, cgSourceFile, cgClass);
        new BlancoPdfReportGeneratePageOperation().canConsolidatePage(pageDef,
                cgFactory, cgSourceFile, cgClass);
        new BlancoPdfReportGeneratePageOperation().breakPageByRowCount(pageDef,
                cgFactory, cgSourceFile, cgClass);
        new BlancoPdfReportGeneratePageOperation().processPageContinuity(
                pageDef, cgFactory, cgSourceFile, cgClass);
        new BlancoPdfReportGeneratePageOperation().applyPageNo(pageDef,
                cgFactory, cgSourceFile, cgClass);

        new BlancoPdfReportGenerateOmitDitto().omitDitto(pageDef, cgFactory,
                cgSourceFile, cgClass);

        {
            final BlancoCgMethod method = cgFactory.createMethod(
                    "drawGridSheet", "グリッド・シートを描画します。");
            cgClass.getMethodList().add(method);
            method.getLineList().add("fPageWriter.drawGridSheet();");
        }

        {
            final BlancoCgMethod method = cgFactory.createMethod("close",
                    "クローズします。");
            cgClass.getMethodList().add(method);
            method.getLineList().add("fPageWriter.close();");
        }

        {
            final BlancoCgMethod method = cgFactory.createMethod(
                    "getPdfWriter", "PDF ライターを取得します。");
            cgClass.getMethodList().add(method);
            method.getAnnotationList().add("Deprecated");
            method.setReturn(cgFactory.createReturn(
                    "com.lowagie.text.pdf.PdfWriter", "内部的に利用しているライター。"));
            method.getLineList().add("return fPageWriter.getWriter();");
        }

        cgSourceFile.setEncoding(fInput.getEncoding());
        new BlancoCgJavaSourceTransformer().transform(cgSourceFile, new File(
                fInput.getTargetdir() + "/main"));
    }

    /**
     * 項目 1 つ分を書き出し。
     * 
     * @param itemDef
     * @param isBlock
     * @param blockDef
     * @param cgSourceFile
     * @param method
     */
    void processItem(final BlancoPdfItemDef itemDef, final boolean isBlock,
            final BlancoPdfBlockDef blockDef,
            final BlancoCgSourceFile cgSourceFile, final BlancoCgMethod method) {
        if (itemDef.getHidden()) {
            // 隠し項目。
            return;
        }

        final boolean isDecimal = "decimal".equals(itemDef.getType());
        final boolean isAlignRight = "right".equals(itemDef.getAlign());
        final boolean isAlignCenter = "center".equals(itemDef.getAlign());
        if (isDecimal) {
            cgSourceFile.getImportList().add("java.text.DecimalFormat");
        }
        if (isAlignRight) {
            cgSourceFile.getImportList().add(
                    "blanco.commons.util.BlancoStringUtil");
        }

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

        cgSourceFile.getImportList().add(
                "blanco.pdf.valueobject.BlancoPdfMetrix");
        method.getLineList().add(
                "final BlancoPdfMetrix metrix = new BlancoPdfMetrix();");
        method.getLineList().add(
                "metrix.setFont(\"" + itemDef.getFont() + "\");");
        method.getLineList().add(
                "metrix.setFontSize(" + itemDef.getFontSize() + ");");
        if (isDecimal) {
            if (BlancoStringUtil.null2Blank(itemDef.getFormat()).length() > 0) {
                method.getLineList().add(
                        "final DecimalFormat format = new DecimalFormat(\""
                                + itemDef.getFormat() + "\");");
            } else {
                method.getLineList().add(
                        "final DecimalFormat format = new DecimalFormat();");
            }

            if (itemDef.getFormatMinimumFractionDigits() >= 0) {
                method.getLineList().add(
                        "format.setMinimumFractionDigits("
                                + itemDef.getFormatMinimumFractionDigits()
                                + ");");
            }
        }

        // null チェックの if 文。
        method.getLineList().add(
                "if (" + (isBlock ? "blockItems" : "pdfItems") + ".get"
                        + BlancoNameAdjuster.toClassName(itemDef.getName())
                        + "() != null) {");

        if (isAlignRight) {
            // 右寄せ
            method.getLineList()
                    .add(
                            "fPageWriter.drawText("
                                    + (itemDef.getX() + (isBlock ? blockDef
                                            .getX() : 0))
                                    + "f"
                                    + (isBlock ? " + " + blockDef.getPitchX()
                                            + "f * occur" : "")
                                    + ", "
                                    + (itemDef.getY() + (isBlock ? blockDef
                                            .getY() : 0))
                                    + "f"
                                    + (isBlock ? " + " + blockDef.getPitchY()
                                            + "f * occur" : "")
                                    + ", "
                                    + "BlancoStringUtil.padLeft("
                                    + (isDecimal ? "format.format(" : "")
                                    + (isBlock ? "blockItems" : "pdfItems")
                                    + ".get"
                                    + BlancoNameAdjuster.toClassName(itemDef
                                            .getName())
                                    + "()"
                                    + (isDecimal ? ")" : "")
                                    + (isAlignRight ? ", "
                                            + itemDef.getLength() + ", ' ')"
                                            : "") + ", metrix);");
        } else if (isAlignCenter) {
            // センタリング
            method
                    .getLineList()
                    .add(
                            "fPageWriter.drawTextCenter("
                                    + (itemDef.getX() + itemDef.getFontSize()
                                            * itemDef.getLength() / 4/*
                                                                      * フォントサイズ/2
                                                                      * で半角 1 つ。
                                                                      */+ (isBlock ? blockDef
                                            .getX()
                                            : 0))
                                    + "f"
                                    + (isBlock ? " + " + blockDef.getPitchX()
                                            + "f * occur" : "")
                                    + ", "
                                    + (itemDef.getY() + (isBlock ? blockDef
                                            .getY() : 0))
                                    + "f"
                                    + (isBlock ? " + " + blockDef.getPitchY()
                                            + "f * occur" : "")
                                    + ", "
                                    + (isDecimal ? "format.format(" : "")
                                    + (isBlock ? "blockItems" : "pdfItems")
                                    + ".get"
                                    + BlancoNameAdjuster.toClassName(itemDef
                                            .getName())
                                    + "()"
                                    + (isDecimal ? ")" : "")
                                    + (isAlignRight ? ", "
                                            + itemDef.getLength() + ", ' ')"
                                            : "") + ", metrix);");
        } else {
            // 左寄
            method.getLineList()
                    .add(
                            "fPageWriter.drawText("
                                    + (itemDef.getX() + (isBlock ? blockDef
                                            .getX() : 0))
                                    + "f"
                                    + (isBlock ? " + " + blockDef.getPitchX()
                                            + "f * occur" : "")
                                    + ", "
                                    + (itemDef.getY() + (isBlock ? blockDef
                                            .getY() : 0))
                                    + "f"
                                    + (isBlock ? " + " + blockDef.getPitchY()
                                            + "f * occur" : "")
                                    + ", "
                                    + (isDecimal ? "format.format(" : "")
                                    + (isBlock ? "blockItems" : "pdfItems")
                                    + ".get"
                                    + BlancoNameAdjuster.toClassName(itemDef
                                            .getName())
                                    + "()"
                                    + (isDecimal ? ")" : "")
                                    + (isAlignRight ? ", "
                                            + itemDef.getLength() + ", ' ')"
                                            : "") + ", metrix);");
        }

        // null チェックの if 文に対応。
        method.getLineList().add("}");

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

    /**
     * {@inheritDoc}
     */
    public boolean progress(final String argProgressMessage) {
        System.out.println(argProgressMessage);
        return false;
    }
}
