/*
 * blancoCsv Copyright (C) 2005 Tosiki Iga
 * 
 * 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.csv.expand;

import java.io.File;

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.BlancoStringUtil;
import blanco.csv.resourcebundle.BlancoCsvReaderResourceBundle;
import blanco.csv.resourcebundle.BlancoCsvResourceBundle;
import blanco.csv.valueobject.BlancoCsvFieldStructure;
import blanco.csv.valueobject.BlancoCsvStructure;

public class BlancoCsvExpandReader {
    /**
     * 内部的に利用するblancoCg用ファクトリ。
     */
    private BlancoCgObjectFactory fCgFactory = null;

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

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

    final BlancoCsvResourceBundle bundle = new BlancoCsvResourceBundle();

    final BlancoCsvReaderResourceBundle readerBundle = new BlancoCsvReaderResourceBundle();

    public void expand(final BlancoCsvStructure processStructure,
            final File directoryTarget) {

        final String className = BlancoNameAdjuster
                .toClassName(processStructure.getFileDefinitionId())
                + readerBundle.getClassNameSuffix();
        final String classNameblancoCsvIOException = processStructure
                .getRuntimePackage()
                + readerBundle.getAddusingtype01Suffix();

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

        fCgFactory = BlancoCgObjectFactory.getInstance();
        fCgSourceFile = fCgFactory.createSourceFile(processStructure
                .getPackageName()
                + readerBundle.getPackagePrefix(),
                "このソースコードは blanco Frameworkによって自動生成されています。");
        fCgClass = fCgFactory
                .createClass(
                        className,
                        BlancoStringUtil
                                .null2Blank(readerBundle
                                        .getClasscomment01(processStructure
                                                .getFileDefinitionId()
                                                + (processStructure
                                                        .getFileDescription() == null ? ""
                                                        : "/"
                                                                + processStructure
                                                                        .getFileDescription()))));
        fCgSourceFile.getClassList().add(fCgClass);

        fCgSourceFile.getImportList().add(classNameblancoCsvIOException);

        fCgClass.getLangDoc().getDescriptionList().add(
                readerBundle.getClasscomment02());
        if (processStructure.getEncoding() == null) {
            fCgClass.getLangDoc().getDescriptionList().add(
                    readerBundle.getClasscomment03Case01());
        } else {
            fCgClass.getLangDoc().getDescriptionList().add(
                    readerBundle.getClasscomment03Case02(processStructure
                            .getEncoding()));
        }
        fCgClass.getLangDoc().getDescriptionList().add(
                readerBundle.getClasscomment04());

        {
            final BlancoCgField field = fCgFactory.createField(readerBundle
                    .getFieldReaderName(), readerBundle.getFieldReaderType(),
                    readerBundle.getFieldReaderJavadoc01());
            fCgClass.getFieldList().add(field);
            field.getLangDoc().getDescriptionList().add(
                    readerBundle.getFieldReaderJavadoc02());
        }

        {
            final BlancoCgField field = fCgFactory.createField(readerBundle
                    .getFieldLineCounterName(), readerBundle
                    .getFieldLineCounterType(), readerBundle
                    .getFieldLineCounterJavadoc01());
            fCgClass.getFieldList().add(field);
            field.getLangDoc().getDescriptionList().add(
                    readerBundle.getFieldLineCounterJavadoc02());
            field.setDefault(readerBundle.getFieldLineCounterDefault());
        }

        // 「書式」対応
        for (int indexField = 0; indexField < processStructure.getFieldList()
                .size(); indexField++) {
            final BlancoCsvFieldStructure fieldStructure = (BlancoCsvFieldStructure) processStructure
                    .getFieldList().get(indexField);

            if (fieldStructure.getType().equals("java.util.Date")) {
                final BlancoCgField field = fCgFactory.createField(readerBundle
                        .getFieldSimpleDateFormatNamePrefix()
                        + BlancoNameAdjuster.toClassName(fieldStructure
                                .getName()), readerBundle
                        .getFieldSimpleDateFormatType(), readerBundle
                        .getFieldSimpleDateFormatJavadoc01(fieldStructure
                                .getName()));
                fCgClass.getFieldList().add(field);
                field.getLangDoc().getDescriptionList().add(
                        readerBundle.getFieldSimpleDateFormatJavadoc02());
            }
        }

        {
            final BlancoCgMethod method = fCgFactory.createMethod(className,
                    readerBundle.getConstructor01Javadoc01());
            fCgClass.getMethodList().add(method);
            method.setConstructor(true);
            method.getLangDoc().getDescriptionList().add(
                    readerBundle.getConstructor01Javadoc02());
            method.getParameterList().add(
                    fCgFactory.createParameter("arg", readerBundle
                            .getConstructor01Arg01Type(), readerBundle
                            .getConstructor01Arg01Javadoc01()));

            // メソッドの実装
            method.getLineList().add("fReader = arg;");

            // 「書式」対応
            for (int indexField = 0; indexField < processStructure
                    .getFieldList().size(); indexField++) {
                final BlancoCsvFieldStructure field = (BlancoCsvFieldStructure) processStructure
                        .getFieldList().get(indexField);

                if (field.getType().equals("java.util.Date")) {
                    method
                            .getLineList()
                            .add(
                                    readerBundle
                                            .getFieldSimpleDateFormatNamePrefix()
                                            + BlancoNameAdjuster
                                                    .toClassName(field
                                                            .getName())
                                            + " = new SimpleDateFormat("
                                            + (field.getFormat() == null ? "\"yyyy/MM/dd\""
                                                    : "\"" + field.getFormat()
                                                            + "\"") + ");");
                    method.getLineList().add("// 日付/時刻解析を厳密におこないます。");
                    method
                            .getLineList()
                            .add(
                                    readerBundle
                                            .getFieldSimpleDateFormatNamePrefix()
                                            + BlancoNameAdjuster
                                                    .toClassName(field
                                                            .getName())
                                            + ".setLenient(false);");
                }
            }
        }

        final String recordClassName = processStructure.getPackageName()
                + ".record."
                + BlancoNameAdjuster.toClassName(processStructure
                        .getFileDefinitionId()) + "CsvRecord";

        // メソッドのシグニチャ指定
        {
            final BlancoCgMethod method = fCgFactory.createMethod("readRecord",
                    readerBundle.getReadrecordJavadoc());
            fCgClass.getMethodList().add(method);
            method.setReturn(fCgFactory.createReturn(recordClassName,
                    readerBundle.getReadrecordReturnJavadoc()));
            method.getThrowList().add(
                    fCgFactory.createException(classNameblancoCsvIOException,
                            readerBundle.getReadrecordThrows01Javadoc()));
            method.getThrowList().add(
                    fCgFactory.createException(readerBundle
                            .getReadrecordThrows02Type(), readerBundle
                            .getReadrecordThrows02Javadoc()));

            // メソッドの実装
            method.getLineList().add("final String line = fReader.readLine();");
            method.getLineList().add("if (line == null) {");
            method.getLineList().add(readerBundle.getReadrecordLine03());
            method.getLineList().add("return null;");
            method.getLineList().add("}");
            method.getLineList().add("fLineCounter++;");
            method.getLineList().add(
                    readerBundle
                            .getReadrecordLine07(BlancoNameAdjuster
                                    .toClassName(processStructure
                                            .getFileDefinitionId())));
            fCgSourceFile.getImportList().add("java.io.StringReader");
            method.getLineList().add(
                    "final StringReader reader = new StringReader(line);");
            method.getLineList().add("String tokenString = null;");

            for (int indexField = 0; indexField < processStructure
                    .getFieldList().size(); indexField++) {
                final BlancoCsvFieldStructure field = (BlancoCsvFieldStructure) processStructure
                        .getFieldList().get(indexField);

                method.getLineList().add("");
                method
                        .getLineList()
                        .add(
                                (readerBundle
                                        .getReadrecordLine12(
                                                (field.getNo() == null ? ""
                                                        : bundle
                                                                .getFieldNo(field
                                                                        .getNo())),
                                                field.getName()
                                                        + (field
                                                                .getDescription() == null ? ""
                                                                : "/"
                                                                        + field
                                                                                .getDescription()))));

                fCgSourceFile.getImportList().add(
                        processStructure.getRuntimePackage()
                                + ".util.BlancoCsvRuntimeUtil");
                if (field.getType().endsWith("(quote)")) {
                    // 文字列のクオート付。
                    method.getLineList()
                            .add(readerBundle.getReadrecordLine21());
                    method.getLineList().add(
                            readerBundle.getReadrecordLine22Case01(
                                    processStructure.getDelimiter(),
                                    (indexField == processStructure
                                            .getFieldList().size() - 1 ? "true"
                                            : "false")));
                } else {
                    method.getLineList().add(
                            readerBundle.getReadrecordLine22Case02(
                                    processStructure.getDelimiter(),
                                    (indexField == processStructure
                                            .getFieldList().size() - 1 ? "true"
                                            : "false")));
                }
                method.getLineList().add("if (tokenString == null) {");
                method.getLineList().add(
                        "throw new BlancoCsvIOException(\""
                                + bundle.getCsvReaderErr001(getLocationString(
                                        indexField, field.getNo(), field
                                                .getName(), field
                                                .getDescription())) + "\");");
                method.getLineList().add("}");

                final String encodingString = (processStructure.getEncoding() == null ? ""
                        : "\"" + processStructure.getEncoding() + "\"");
                if (field.getRequired()) {
                    method.getLineList().add("// 必須項目。");
                    method.getLineList()
                            .add("if (tokenString.length() == 0) {");
                    method
                            .getLineList()
                            .add(
                                    "throw new BlancoCsvIOException(\""
                                            + bundle
                                                    .getCsvReaderErr008(getLocationString(
                                                            indexField,
                                                            field.getNo(),
                                                            field.getName(),
                                                            field
                                                                    .getDescription()))
                                            + "\");");
                    method.getLineList().add("}");
                } else {
                    method.getLineList().add("// 任意項目。");
                    method.getLineList()
                            .add("if (tokenString.length() == 0) {");
                    if (field.getType().startsWith("java.lang.String")) {
                        method
                                .getLineList()
                                .add(
                                        "// 必須項目ではない文字列項目に長さ0の値が読み込まれた場合には、読み込み後の値としても長さ0の文字列とします。");
                        method.getLineList().add(
                                "record.set"
                                        + BlancoNameAdjuster.toClassName(field
                                                .getName()) + "(tokenString);");
                    } else {
                        method
                                .getLineList()
                                .add(
                                        "// 必須項目ではない項目に長さ0の値が読み込まれた場合には、値としてnullを代入します。");
                        method.getLineList().add(
                                "record.set"
                                        + BlancoNameAdjuster.toClassName(field
                                                .getName()) + "(null);");
                    }
                    method.getLineList().add("} else {");
                }
                if (field.getMinLength() != null) {
                    method.getLineList().add(
                            "if (tokenString.getBytes(" + encodingString
                                    + ").length < " + field.getMinLength()
                                    + ") {");
                    method.getLineList().add(
                            "throw new BlancoCsvIOException(\""
                                    + bundle.getCsvReaderErr002(
                                            getLocationString(indexField, field
                                                    .getNo(), field.getName(),
                                                    field.getDescription()),
                                            field.getMinLength()) + "\");");
                    method.getLineList().add("}");
                }
                if (field.getMaxLength() != null) {
                    method.getLineList().add(
                            "if (tokenString.getBytes(" + encodingString
                                    + ").length > " + field.getMaxLength()
                                    + ") {");
                    method.getLineList().add(
                            "throw new BlancoCsvIOException(\""
                                    + bundle.getCsvReaderErr003(
                                            getLocationString(indexField, field
                                                    .getNo(), field.getName(),
                                                    field.getDescription()),
                                            field.getMaxLength()) + "\");");
                    method.getLineList().add("}");
                }

                if (field.getType().equals("java.lang.String")
                        || field.getType().equals("java.lang.String(quote)")) {
                    method.getLineList().add(
                            "record.set"
                                    + BlancoNameAdjuster.toClassName(field
                                            .getName()) + "(tokenString);");
                } else if (field.getType().equals("int")) {
                    method.getLineList().add("try {");
                    if (field.getRequired()) {
                        // 必須の場合
                        method.getLineList().add(
                                "record.set"
                                        + BlancoNameAdjuster.toClassName(field
                                                .getName())
                                        + "(Integer.parseInt(tokenString));");
                    } else {
                        method.getLineList().add(
                                "record.set"
                                        + BlancoNameAdjuster.toClassName(field
                                                .getName())
                                        + "(new Integer(tokenString));");
                    }
                    method.getLineList().add(
                            "} catch (NumberFormatException ex) {");
                    method
                            .getLineList()
                            .add(
                                    "throw new BlancoCsvIOException(\""
                                            + bundle
                                                    .getCsvReaderErr004(getLocationString(
                                                            indexField,
                                                            field.getNo(),
                                                            field.getName(),
                                                            field
                                                                    .getDescription()))
                                            + "\" + ex.toString(), ex);");
                    method.getLineList().add("}");
                } else if (field.getType().equals("long")) {
                    method.getLineList().add("try {");
                    if (field.getRequired()) {
                        // 必須の場合
                        method.getLineList().add(
                                "record.set"
                                        + BlancoNameAdjuster.toClassName(field
                                                .getName())
                                        + "(Long.parseLong(tokenString));");
                    } else {
                        method.getLineList().add(
                                "record.set"
                                        + BlancoNameAdjuster.toClassName(field
                                                .getName())
                                        + "(new Long(tokenString));");
                    }
                    method.getLineList().add(
                            "} catch (NumberFormatException ex) {");
                    method
                            .getLineList()
                            .add(
                                    "throw new BlancoCsvIOException(\""
                                            + bundle
                                                    .getCsvReaderErr005(getLocationString(
                                                            indexField,
                                                            field.getNo(),
                                                            field.getName(),
                                                            field
                                                                    .getDescription()))
                                            + "\" + ex.toString(), ex);");
                    method.getLineList().add("}");
                } else if (field.getType().equals("java.util.Date")) {
                    fCgSourceFile.getImportList().add(
                            "java.text.ParseException");
                    method.getLineList().add("try {");
                    method.getLineList().add(
                            "record.set"
                                    + BlancoNameAdjuster.toClassName(field
                                            .getName())
                                    + "(fSimpleDateFormat"
                                    + BlancoNameAdjuster.toClassName(field
                                            .getName())
                                    + ".parse(tokenString));");
                    method.getLineList().add("} catch (ParseException ex) {");
                    method.getLineList().add(
                            "throw new BlancoCsvIOException(\""
                                    + bundle.getCsvReaderErr006(
                                            getLocationString(indexField, field
                                                    .getNo(), field.getName(),
                                                    field.getDescription()),
                                            BlancoNameAdjuster
                                                    .toClassName(field
                                                            .getName()))
                                    + "\" + ex.toString(), ex);");
                    method.getLineList().add("}");
                } else if (field.getType().equals("java.math.BigDecimal")) {
                    fCgSourceFile.getImportList().add("java.math.BigDecimal");
                    method.getLineList().add("try {");
                    method.getLineList().add(
                            "record.set"
                                    + BlancoNameAdjuster.toClassName(field
                                            .getName())
                                    + "(new BigDecimal(tokenString));");
                    method.getLineList().add(
                            "} catch (NumberFormatException ex) {");
                    method
                            .getLineList()
                            .add(
                                    "throw new BlancoCsvIOException(\""
                                            + bundle
                                                    .getCsvReaderErr007(getLocationString(
                                                            indexField,
                                                            field.getNo(),
                                                            field.getName(),
                                                            field
                                                                    .getDescription()))
                                            + "\" + ex.toString(), ex);");
                    method.getLineList().add("}");
                } else {
                    method.getLineList().add(
                            bundle.getNotSupportedType(field.getType()));
                }

                if (field.getRequired()) {
                    // 必須項目。
                } else {
                    method.getLineList().add("}");
                }
            }

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

        // メソッドのシグニチャ指定
        {
            final BlancoCgMethod method = fCgFactory.createMethod(
                    "getLineCounter", "現在処理中の行カウンタを返します。");
            fCgClass.getMethodList().add(method);
            method.setReturn(fCgFactory.createReturn("long", "現在処理中の行カウンタ。"));

            // メソッドの実装
            method.getLineList().add("return fLineCounter;");
        }

        // メソッドのシグニチャ指定
        {
            final BlancoCgMethod method = fCgFactory.createMethod("close",
                    readerBundle.getCloseJavadoc01());
            fCgClass.getMethodList().add(method);
            method.getLangDoc().getDescriptionList().add(
                    readerBundle.getCloseJavadoc02());
            method.getThrowList().add(
                    fCgFactory.createException(readerBundle
                            .getCloseThrows01Type(), readerBundle
                            .getCloseThrows01Javadoc()));

            // メソッドの実装
            method.getLineList().add("fReader.close();");
        }

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

    private String getLocationString(int indexField, String fieldNo,
            String fieldName, String fieldDescription) {
        return readerBundle.getGetlocationstringLine01("" + (indexField + 1),
                (fieldNo == null ? "" : bundle.getFieldNo(fieldNo) + " "),
                fieldName
                        + (fieldDescription == null ? "" : "/"
                                + fieldDescription));
    }
}
