/*
 * 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.commons.util.BlancoNameAdjuster;
import blanco.csv.resourcebundle.BlancoCsvDotNetResourceBundle;
import blanco.csv.resourcebundle.BlancoCsvReaderDotNetResourceBundle;
import blanco.csv.valueobject.BlancoCsvFieldStructureDotNet;
import blanco.csv.valueobject.BlancoCsvStructureDotNet;
import blanco.ig.expander.ClassExpander;
import blanco.ig.expander.IgType;
import blanco.ig.expander.IgValue;
import blanco.ig.expander.field.FieldExpander;
import blanco.ig.expander.implementor.IgStatement;
import blanco.ig.expander.method.IgConstructor;
import blanco.ig.expander.method.MethodExpander;

public class BlancoCsvExpandReaderDotNet {
    final BlancoCsvDotNetResourceBundle bundle = new BlancoCsvDotNetResourceBundle();

    final BlancoCsvReaderDotNetResourceBundle readerBundle = new BlancoCsvReaderDotNetResourceBundle();

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

        final String className = BlancoNameAdjuster
                .toClassName(processStructure.getFileDefinitionId())
                + readerBundle.getClassNameSuffix();
        final ClassExpander classExpander = new ClassExpander(new IgType(
                processStructure.getPackageName()
                        + readerBundle.getPackagePrefix(), className)) {
            protected void expandClassStruct() {
                addImport(new IgType(processStructure.getRuntimePackage()
                        + readerBundle.getAddusingtype01Suffix()));
                // 常にSystem名前空間はusingします。
                addImport(new IgType("System.String"));
                addFileComment(readerBundle.getFilecomment());

                getJavaDoc()
                        .addLine(
                                readerBundle
                                        .getClasscomment01(processStructure
                                                .getFileDefinitionId()
                                                + (processStructure
                                                        .getFileDescription() == null ? ""
                                                        : "/"
                                                                + processStructure
                                                                        .getFileDescription())));
                getJavaDoc().addLine(readerBundle.getClasscomment02());
                if (processStructure.getEncoding() == null) {
                    getJavaDoc()
                            .addLine(readerBundle.getClasscomment03Case01());
                } else {
                    getJavaDoc().addLine(
                            readerBundle
                                    .getClasscomment03Case02(processStructure
                                            .getEncoding()));
                }
                getJavaDoc().addLine(readerBundle.getClasscomment04());

                final FieldExpander field1 = new FieldExpander(new IgType(
                        readerBundle.getFieldReaderType()), readerBundle
                        .getFieldReaderName());
                field1.getJavaDoc().addLine(
                        readerBundle.getFieldReaderJavadoc01());
                field1.getJavaDoc().addLine(
                        readerBundle.getFieldReaderJavadoc02());
                addField(field1);

                final FieldExpander field2 = new FieldExpander(new IgType(
                        readerBundle.getFieldLineCounterType()), readerBundle
                        .getFieldLineCounterName());
                field2.getJavaDoc().addLine(
                        readerBundle.getFieldLineCounterJavadoc01());
                field2.getJavaDoc().addLine(
                        readerBundle.getFieldLineCounterJavadoc02());
                field2.setDefaultStatement(new IgStatement(readerBundle
                        .getFieldLineCounterDefault()));
                addField(field2);

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

                    if (field.getType().equals("System.DateTime")) {
                        final FieldExpander field3 = new FieldExpander(
                                new IgType(readerBundle
                                        .getFieldSimpleDateFormatType()),
                                readerBundle
                                        .getFieldSimpleDateFormatNamePrefix()
                                        + BlancoNameAdjuster.toClassName(field
                                                .getName()));
                        field3
                                .getJavaDoc()
                                .addLine(
                                        readerBundle
                                                .getFieldSimpleDateFormatJavadoc01(field
                                                        .getName()));
                        field3.getJavaDoc().addLine(
                                readerBundle
                                        .getFieldSimpleDateFormatJavadoc02());
                        addField(field3);
                    }
                }

                addMethod(new IgConstructor(className) {
                    // メソッドのシグニチャ指定
                    public void setupSignature() {
                        getJavaDoc().addLine(
                                readerBundle.getConstructor01Javadoc01());
                        getJavaDoc().addLine(
                                readerBundle.getConstructor01Javadoc02());
                        getJavaDoc().addParameter("arg",
                                readerBundle.getConstructor01Arg01Javadoc01());
                        addArgument(new IgValue(new IgType(readerBundle
                                .getConstructor01Arg01Type()), "arg"));
                    }

                    // メソッドの実装
                    public void implement() {
                        getData().addLine("fReader = arg;");

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

                            if (field.getType().equals("System.DateTime")) {
                                getData()
                                        .addLine(
                                                readerBundle
                                                        .getFieldSimpleDateFormatNamePrefix()
                                                        + BlancoNameAdjuster
                                                                .toClassName(field
                                                                        .getName())
                                                        + " = "
                                                        + (field.getFormat() == null ? "\"yyyy/MM/dd\""
                                                                : "\""
                                                                        + field
                                                                                .getFormat()
                                                                        + "\"")
                                                        + ";");
                            }
                        }

                    }
                });

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

                addMethod(new MethodExpander("ReadRecord") {
                    // メソッドのシグニチャ指定
                    public void setupSignature() {
                        getJavaDoc().addLine(
                                readerBundle.getReadrecordJavadoc());
                        getJavaDoc().addReturn(
                                readerBundle.getReadrecordReturnJavadoc());
                        setReturnType(new IgType(recordClassName));
                    }

                    // メソッドの実装
                    public void implement() {
                        getData().addLine("string line = fReader.ReadLine();");
                        getData().addLine("if (line == null) {");
                        getData().addLine(readerBundle.getReadrecordLine03());
                        getData().addLine("return null;");
                        getData().addLine("}");
                        getData().addLine("fLineCounter++;");
                        getData()
                                .addLine(
                                        readerBundle
                                                .getReadrecordLine07(BlancoNameAdjuster
                                                        .toClassName(processStructure
                                                                .getFileDefinitionId())));
                        addUsingType(new IgType("System.IO.StringReader"));
                        getData()
                                .addLine(
                                        "StringReader reader = new StringReader(line);");
                        getData().addLine("string tokenString = null;");

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

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

                            // 型の利用を追加します。
                            addUsingType(new IgType(processStructure
                                    .getRuntimePackage()
                                    + ".util.BlancoCsvRuntimeUtil"));

                            if (field.getType().endsWith("(quote)")) {
                                getData().addLine(
                                        readerBundle.getReadrecordLine21());
                                getData()
                                        .addLine(
                                                readerBundle
                                                        .getReadrecordLine22Case01(processStructure
                                                                .getDelimiter()));
                            } else {
                                getData()
                                        .addLine(
                                                readerBundle
                                                        .getReadrecordLine22Case02(
                                                                processStructure
                                                                        .getDelimiter(),
                                                                (indexField == processStructure
                                                                        .getListField()
                                                                        .size() - 1 ? "true"
                                                                        : "false")));
                            }
                            getData().addLine("if (tokenString == null) {");
                            getData()
                                    .addLine(
                                            "throw new BlancoCsvIOException(\""
                                                    + bundle
                                                            .getCsvReaderErr001(getLocationString(
                                                                    indexField,
                                                                    field
                                                                            .getNo(),
                                                                    field
                                                                            .getName(),
                                                                    field
                                                                            .getDescription()))
                                                    + "\");");
                            getData().addLine("}");

                            if (field.getRequired()) {
                                getData().addLine("// 必須項目。");
                                getData().addLine(
                                        "if (tokenString.Length == 0) {");
                                getData()
                                        .addLine(
                                                "throw new BlancoCsvIOException(\""
                                                        + bundle
                                                                .getCsvReaderErr008(getLocationString(
                                                                        indexField,
                                                                        field
                                                                                .getNo(),
                                                                        field
                                                                                .getName(),
                                                                        field
                                                                                .getDescription()))
                                                        + "\");");
                                getData().addLine("}");
                            } else {
                                getData().addLine("// 任意項目。");
                                getData().addLine(
                                        "if (tokenString.Length == 0) {");
                                if (field.getType().startsWith("string")) {
                                    getData()
                                            .addLine(
                                                    "// 必須項目ではない文字列項目に長さ0の値が読み込まれた場合には、読み込み後の値としても長さ0の文字列とします。");
                                    getData().addLine(
                                            "record.Set"
                                                    + BlancoNameAdjuster
                                                            .toClassName(field
                                                                    .getName())
                                                    + "(tokenString);");
                                } else if (field.getType().equals("int")
                                        || field.getType().equals("long")
                                        || field.getType().equals("decimal")
                                        || field.getType().equals(
                                                "System.DateTime")) {
                                    getData()
                                            .addLine(
                                                    "// 必須項目ではない項目に長さ0の値が読み込まれた場合には、値としての代入は行いません。");
                                    getData().addLine(
                                            "record.Set"
                                                    + BlancoNameAdjuster
                                                            .toClassName(field
                                                                    .getName())
                                                    + "(" + field.getType()
                                                    + ".MinValue);");
                                } else {
                                    getData().addLine(
                                            "// サポートしない型(" + field.getType()
                                                    + ")が与えられました。");
                                }
                                getData().addLine("} else {");
                            }

                            if (field.getMinLength() != null) {
                                // 型の利用を追加します。
                                addUsingType(new IgType(processStructure
                                        .getRuntimePackage()
                                        + ".util.BlancoCsvRuntimeUtil"));

                                getData().addLine(
                                        "if (BlancoCsvRuntimeUtil.GetBytes(tokenString"
                                                + ").Length < "
                                                + field.getMinLength() + ") {");
                                getData()
                                        .addLine(
                                                "throw new BlancoCsvIOException(\""
                                                        + bundle
                                                                .getCsvReaderErr002(
                                                                        getLocationString(
                                                                                indexField,
                                                                                field
                                                                                        .getNo(),
                                                                                field
                                                                                        .getName(),
                                                                                field
                                                                                        .getDescription()),
                                                                        field
                                                                                .getMinLength())
                                                        + "\");");
                                getData().addLine("}");
                            }
                            if (field.getMaxLength() != null) {
                                // 型の利用を追加します。
                                addUsingType(new IgType(processStructure
                                        .getRuntimePackage()
                                        + ".util.BlancoCsvRuntimeUtil"));

                                getData().addLine(
                                        "if (BlancoCsvRuntimeUtil.GetBytes(tokenString"
                                                + ").Length > "
                                                + field.getMaxLength() + ") {");
                                getData()
                                        .addLine(
                                                "throw new BlancoCsvIOException(\""
                                                        + bundle
                                                                .getCsvReaderErr003(
                                                                        getLocationString(
                                                                                indexField,
                                                                                field
                                                                                        .getNo(),
                                                                                field
                                                                                        .getName(),
                                                                                field
                                                                                        .getDescription()),
                                                                        field
                                                                                .getMaxLength())
                                                        + "\");");
                                getData().addLine("}");
                            }

                            if (field.getType().equals("string")
                                    || field.getType().equals("string(quote)")) {
                                getData().addLine(
                                        "record.Set"
                                                + BlancoNameAdjuster
                                                        .toClassName(field
                                                                .getName())
                                                + "(tokenString);");
                            } else if (field.getType().equals("int")) {
                                getData().addLine("try {");
                                getData()
                                        .addLine(
                                                "record.Set"
                                                        + BlancoNameAdjuster
                                                                .toClassName(field
                                                                        .getName())
                                                        + "(Int32.Parse(tokenString));");
                                getData().addLine(
                                        "} catch (FormatException ex) {");
                                getData()
                                        .addLine(
                                                "throw new BlancoCsvIOException(\""
                                                        + bundle
                                                                .getCsvReaderErr004(getLocationString(
                                                                        indexField,
                                                                        field
                                                                                .getNo(),
                                                                        field
                                                                                .getName(),
                                                                        field
                                                                                .getDescription()))
                                                        + "\" + ex.ToString(), ex);");
                                getData().addLine("}");
                            } else if (field.getType().equals("long")) {
                                getData().addLine("try {");
                                getData()
                                        .addLine(
                                                "record.Set"
                                                        + BlancoNameAdjuster
                                                                .toClassName(field
                                                                        .getName())
                                                        + "(Int64.Parse(tokenString));");
                                getData().addLine(
                                        "} catch (FormatException ex) {");
                                getData()
                                        .addLine(
                                                "throw new BlancoCsvIOException(\""
                                                        + bundle
                                                                .getCsvReaderErr005(getLocationString(
                                                                        indexField,
                                                                        field
                                                                                .getNo(),
                                                                        field
                                                                                .getName(),
                                                                        field
                                                                                .getDescription()))
                                                        + "\" + ex.ToString(), ex);");
                                getData().addLine("}");
                            } else if (field.getType().equals("decimal")) {
                                getData().addLine("try {");
                                getData()
                                        .addLine(
                                                "record.Set"
                                                        + BlancoNameAdjuster
                                                                .toClassName(field
                                                                        .getName())
                                                        + "(Convert.ToDecimal(tokenString));");
                                getData().addLine(
                                        "} catch (FormatException ex) {");
                                getData()
                                        .addLine(
                                                "throw new BlancoCsvIOException(\""
                                                        + bundle
                                                                .getCsvReaderErr007(getLocationString(
                                                                        indexField,
                                                                        field
                                                                                .getNo(),
                                                                        field
                                                                                .getName(),
                                                                        field
                                                                                .getDescription()))
                                                        + "\" + ex.ToString(), ex);");
                                getData().addLine("}");
                            } else if (field.getType()
                                    .equals("System.DateTime")) {
                                addUsingType(new IgType(
                                        "System.FormatException"));
                                getData().addLine("try {");
                                getData()
                                        .addLine(
                                                "record.Set"
                                                        + BlancoNameAdjuster
                                                                .toClassName(field
                                                                        .getName())
                                                        + "(DateTime.ParseExact(tokenString, fSimpleDateFormat"
                                                        + BlancoNameAdjuster
                                                                .toClassName(field
                                                                        .getName())
                                                        + ", null));");
                                getData().addLine(
                                        "} catch (FormatException ex) {");
                                getData()
                                        .addLine(
                                                "throw new BlancoCsvIOException(\""
                                                        + bundle
                                                                .getCsvReaderErr006(
                                                                        getLocationString(
                                                                                indexField,
                                                                                field
                                                                                        .getNo(),
                                                                                field
                                                                                        .getName(),
                                                                                field
                                                                                        .getDescription()),
                                                                        BlancoNameAdjuster
                                                                                .toClassName(field
                                                                                        .getName()))
                                                        + "\" + ex.ToString(), ex);");
                                getData().addLine("}");
                            } else {
                                getData().addLine(
                                        bundle.getNotSupportedType(field
                                                .getType()));
                            }

                            if (field.getRequired()) {
                                // 必須項目。
                            } else {
                                getData().addLine("}");
                            }
                        }

                        getData().addLine("return record;");
                    }

                });

                addMethod(new MethodExpander("GetLineCounter") {
                    // メソッドのシグニチャ指定
                    public void setupSignature() {
                        getJavaDoc().addLine("現在処理中の行カウンタを返します。");
                        getJavaDoc().addReturn("現在処理中の行カウンタ。");
                        setReturnType(new IgType("long"));
                    }

                    // メソッドの実装
                    public void implement() {
                        getData().addLine("return fLineCounter;");
                    }
                });

                addMethod(new MethodExpander("Close") {
                    // メソッドのシグニチャ指定
                    public void setupSignature() {
                        getJavaDoc().addLine(readerBundle.getCloseJavadoc01());
                        getJavaDoc().addLine(readerBundle.getCloseJavadoc02());
                    }

                    // メソッドの実装
                    public void implement() {
                        getData().addLine("fReader.Close();");
                    }
                });
            }
        };

        ClassExpander.generateCsSource(classExpander, directoryTarget);
    }

    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));
    }
}
