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

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

import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import blanco.commons.util.BlancoXmlUtil;
import blanco.fixedlength.expand.BlancoFixedLengthExpandIOException;
import blanco.fixedlength.expand.BlancoFixedLengthExpandReader;
import blanco.fixedlength.expand.BlancoFixedLengthExpandRecord;
import blanco.fixedlength.expand.BlancoFixedLengthExpandRuntimeUtil;
import blanco.fixedlength.expand.BlancoFixedLengthExpandWriter;
import blanco.fixedlength.resourcebundle.BlancoFixedLengthResourceBundle;
import blanco.fixedlength.valueobject.BlancoFixedLengthFieldStructure;
import blanco.fixedlength.valueobject.BlancoFixedLengthStructure;

public class BlancoFixedLengthXml2JavaClass {
    private final BlancoFixedLengthResourceBundle fBundle = new BlancoFixedLengthResourceBundle();

    private String fRuntimePackage = null;

    /**
     * 与えられた中間ファイルを、与えられた中間ディレクトリを用いながら、ターゲットディレクトリに生成します。
     * 
     * @param fileXmlSource
     *            中間ファイル
     * @param runtimePackage
     *            ランタイムパッケージ。無指定の場合には定義書の値を利用。
     * @param directoryValueObjectWork
     *            VOのためのワークディレクトリ。
     * @param directoryBlancoTarget
     *            ソースコード出力先の blanco フォルダ。
     * @throws TransformerException
     *             XML変換例外が発生した場合。
     */
    public void process(final File fileXmlSource, final String runtimePackage,
            final File directoryValueObjectWork,
            final File directoryBlancoTarget, final boolean isGenerateJUnit)
            throws TransformerException {
        fRuntimePackage = runtimePackage;

        final DOMResult result = BlancoXmlUtil.transformFile2Dom(fileXmlSource);

        try {
            final Node rootNode = result.getNode();
            if (rootNode instanceof Document) {
                // これが正常系。ドキュメントルートを取得
                final Document rootDocument = (Document) rootNode;
                final NodeList listSheet = rootDocument
                        .getElementsByTagName("sheet");
                final int sizeListSheet = listSheet.getLength();
                for (int index = 0; index < sizeListSheet; index++) {
                    final Element elementSheet = (Element) listSheet
                            .item(index);

                    final NodeList listCommon = elementSheet
                            .getElementsByTagName(fBundle
                                    .getMeta2xmlElementCommon());
                    if (listCommon.getLength() == 0) {
                        // commonが無い場合にはスキップします。
                        continue;
                    }

                    final Element elementCommon = (Element) listCommon.item(0);

                    final BlancoFixedLengthStructure processStructure = new BlancoFixedLengthStructure();

                    processStructure.setFileDefinitionId(BlancoXmlUtil
                            .getTextContent(elementCommon, "fileDefinitionId"));
                    if (processStructure.getFileDefinitionId() == null) {
                        // ファイル定義IDが未定義のものは処理スキップします。
                        continue;
                    }

                    processStructure.setPackageName(BlancoXmlUtil
                            .getTextContent(elementCommon, "packageName"));
                    if (processStructure.getPackageName() == null) {
                        throw new IllegalArgumentException(fBundle
                                .getXml2javaclassErr001(processStructure
                                        .getFileDefinitionId()));
                    }

                    processStructure.setFileDescription(BlancoXmlUtil
                            .getTextContent(elementCommon, "fileDescription"));

                    // ランタイムパッケージの確定。
                    if (fRuntimePackage != null
                            && fRuntimePackage.trim().length() > 0) {
                        // ランタイムパッケージの指定がある場合にはそれを利用します。
                        processStructure.setRuntimePackage(fRuntimePackage);
                    } else {
                        // ランタイムパッケージの指定がない場合には、定義書上のパッケージ名を利用します。
                        processStructure.setRuntimePackage(processStructure
                                .getPackageName());
                    }

                    processStructure.setEncoding(BlancoXmlUtil.getTextContent(
                            elementCommon, "encoding"));

                    final NodeList listRow = elementSheet
                            .getElementsByTagName("field");
                    final int sizeListRow = listRow.getLength();
                    for (int indexField = 0; indexField < sizeListRow; indexField++) {
                        final Element elementField = (Element) listRow
                                .item(indexField);

                        final BlancoFixedLengthFieldStructure field = new BlancoFixedLengthFieldStructure();

                        field.setName(BlancoXmlUtil.getTextContent(
                                elementField, "name"));
                        if (field.getName() == null) {
                            continue;
                        }

                        field.setType(BlancoXmlUtil.getTextContent(
                                elementField, "type"));
                        field.setNo(BlancoXmlUtil.getTextContent(elementField,
                                "no"));
                        field.setDescription(BlancoXmlUtil.getTextContent(
                                elementField, "description"));
                        field.setLength(BlancoXmlUtil.getTextContent(
                                elementField, "length"));
                        field.setDefault(BlancoXmlUtil.getTextContent(
                                elementField, "default"));
                        field.setFormat(BlancoXmlUtil.getTextContent(
                                elementField, "format"));

                        if (field.getType() == null) {
                            throw new IllegalArgumentException(fBundle
                                    .getXml2javaclassErr004(processStructure
                                            .getFileDefinitionId(), field
                                            .getName()));
                        }
                        if (field.getType().equals("java.util.Date")) {
                            if (field.getFormat() == null) {
                                throw new IllegalArgumentException(fBundle
                                        .getXml2javaclassErr005(
                                                processStructure
                                                        .getFileDefinitionId(),
                                                field.getName()));
                            }
                        } else {
                            if (field.getFormat() != null) {
                                throw new IllegalArgumentException(fBundle
                                        .getXml2javaclassErr006(
                                                processStructure
                                                        .getFileDefinitionId(),
                                                field.getName()));
                            }
                        }

                        if (field.getLength() == null) {
                            throw new IllegalArgumentException("ファイル定義ID["
                                    + processStructure.getFileDefinitionId()
                                    + "] 項目[" + field.getName() + "]の長さが未指定です。");
                        }

                        if (field.getLength() != null) {
                            try {
                                if (Integer.parseInt(field.getLength()) < 0) {
                                    // 0以上の整数のみが指定可能
                                    throw new IllegalArgumentException(
                                            fBundle
                                                    .getXml2javaclassErr007(
                                                            processStructure
                                                                    .getFileDefinitionId(),
                                                            field.getName(),
                                                            field.getLength()));
                                }
                            } catch (NumberFormatException ex) {
                                throw new IllegalArgumentException(fBundle
                                        .getXml2javaclassErr008(
                                                processStructure
                                                        .getFileDefinitionId(),
                                                field.getName(), field
                                                        .getLength()));
                            }
                        }

                        processStructure.getListField().add(field);
                    }

                    expandSheet(processStructure, elementSheet, elementCommon,
                            directoryValueObjectWork, directoryBlancoTarget,
                            isGenerateJUnit);
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * シートを展開します。
     * 
     * @param elementSheet
     * @param elementCommon
     * @param directoryValueObjectWork
     * @param directoryTarget
     * @param isGenerateJUnit
     * @throws IOException
     * @throws TransformerException
     */
    private static void expandSheet(
            final BlancoFixedLengthStructure processStructure,
            final Element elementSheet, final Element elementCommon,
            final File directoryValueObjectWork, final File directoryTarget,
            final boolean isGenerateJUnit) throws IOException,
            TransformerException {
        // 最初にレコードから展開することとします。
        // レコードの処理の中に妥当性チェックなどを含めることとします。
        BlancoFixedLengthExpandRecord.expand(processStructure,
                directoryValueObjectWork, directoryTarget, isGenerateJUnit);
        new BlancoFixedLengthExpandReader().expand(processStructure,
                directoryTarget);
        new BlancoFixedLengthExpandWriter().expand(processStructure,
                directoryTarget);

        new BlancoFixedLengthExpandIOException().expand(processStructure,
                directoryTarget);
        new BlancoFixedLengthExpandRuntimeUtil().expand(processStructure,
                directoryTarget);
    }
}
