/*
 * blancoIg
 * Copyright (C) 2004-2005 Yasuo Nakanishi
 * 
 * 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.struts.expander;

import blanco.core.datastruct.ReadAccessor;
import blanco.ig.expander.Type;
import blanco.ig.expander.Value;
import blanco.ig.expander.implementor.Array;
import blanco.ig.expander.implementor.Call;
import blanco.ig.expander.implementor.Cast;
import blanco.ig.expander.implementor.ImplementData;
import blanco.ig.expander.implementor.Implementor;
import blanco.ig.expander.implementor.Receiver;
import blanco.ig.expander.implementor.Statement;
import blanco.ig.expander.implementor.StringLiteral;
import blanco.struts.definition.form.FieldValidation;
import blanco.struts.definition.form.FormField;
import blanco.struts.definition.form.SizeValidationType;
import blanco.struts.runtime.validator.FormatValidation;

/**
 * @author Yasuo Nakanishi
 */
public class StrutsImplementor extends Implementor {

    public StrutsImplementor(ImplementData data) {
        super(data);
    }

    public void checkRequire(Value result, Value fieldValue, FormField field,
            Type keyType) {

        addLine("if (!Validator.validateRequired(" + getFormField(fieldValue)
                + ")) {");
        addLine("String messageKey = ErrorMessageKey.REQUIRED.getKey();");
        addLine("Object[] arguments = new Object[]{"
                + getFieldObject(field, fieldValue) + ", \"" + field.getDescription()
                + "\"};");
        addLine("errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(messageKey, arguments));");
        addLine("}");
    }

    public void checkTableRequire(Value tableItem, FormField field,
            Value result, Type keyType) {

        addLine("if (!Validator.validateRequired("
                + getTableField(tableItem, field.getValue()) + ")) {");
        addLine("String messageKey = ErrorMessageKey.REQUIRED.getKey();");
        addLine("Object[] arguments = new Object[]{"
                + getTableFieldObject(tableItem, field.getValue()) + ", \""
                + field.getDescription() + "\"};");
        addLine("errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(messageKey, arguments));");
        addLine("}");
    }

    private Statement getFormField(Value value) {
        Statement result = null;
        Type type = value.getType();
        if (type.isPrimitive() && type.equals(int.class)) {
            Call c = new Call(Integer.class, "toString");
            c.addArgument(value);
            result = c;
        } else if (type.isPrimitive() && type.equals(boolean.class)) {
            Call c = new Call(Boolean.class, "toString");
            c.addArgument(value);
            result = c;
        } else {
            result = new Statement(value);
        }
        return result;
    }

    public Statement getCheckEventStatement(Value event, String eventName) {
        Receiver r = new Receiver(event);
        Call c = r.call("equals");
        c.addArgument(new StringLiteral(eventName));
        return r;
    }

    public void checkFormat(FormField field, Value fieldValue, Value result,
            Type keyType) {

        String format = field.getValidation().getFormat();
        addLine("if (!Validator." + FormatValidation.nameToMethodName(format)
                + "(" + getFormField(fieldValue) + ")) {");
        addLine("String messageKey = ErrorMessageKey."
                + FormatValidation.nameToFieldName(format) + ".getKey();");
        addLine("Object[] arguments = new Object[]{"
                + getFieldObject(field, fieldValue) + ", \"" + format
                + "\", \"" + field.getDescription() + "\"};");
        addLine("errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(messageKey, arguments));");
        addLine("}");
    }

    public void checkRegex(FormField field, Value fieldValue, Value result,
            Type keyType) {

        addLine("if (!Validator.validateRegex(" + getFormField(fieldValue)
                + ", " + new StringLiteral(field.getValidation().getRegex())
                + ", "
                + new StringLiteral(field.getValidation().getRegexMessageKey())
                + ")) {");
        addLine("String messageKey = ErrorMessageKey.REGEX.getKey();");
        addLine("Object[] arguments = new Object[]{"
                + getFieldObject(field, fieldValue) + ", "
                + new StringLiteral(field.getValidation().getRegex()) + ", \""
                + field.getDescription() + "\"};");
        addLine("errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(messageKey, arguments));");
        addLine("}");
    }

    public void checkSize(FormField field, Value fieldValue, Value result,
            Type keyType) {

        if (field.getValidation().getSizeValidationType().equals(
                SizeValidationType.BYTES)) {
            addLine("if (!Validator.validateStringSizeByBytes("
                    + getFormField(fieldValue) + ", "
                    + new StringLiteral(field.getValidation().getMinSize())
                    + ", "
                    + new StringLiteral(field.getValidation().getMaxSize())
                    + ")) {");
        } else {
            addLine("if (!Validator.validateStringSizeByLength("
                    + getFormField(fieldValue) + ", "
                    + new StringLiteral(field.getValidation().getMinSize())
                    + ", "
                    + new StringLiteral(field.getValidation().getMaxSize())
                    + ")) {");
        }

        addLine("String messageKey = ErrorMessageKey."
                + getSizeErrorMessageField(field) + ".getKey();");
        addLine("Object[] arguments = new Object[]{"
                + getFieldObject(field, fieldValue) + ", \""
                + field.getValidation().getMinSize() + "\", \""
                + field.getValidation().getMaxSize() + "\", \""
                + field.getDescription() + "\"};");
        addLine("errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(messageKey, arguments));");
        addLine("}");
    }

    private String getSizeErrorMessageField(FormField field) {
        String result = null;
        SizeValidationType type = field.getValidation().getSizeValidationType();
        if (type.equals(SizeValidationType.BYTES)) {
            result = "BYTE_SIZE";
        } else if (type.equals(SizeValidationType.LENGTH)) {
            result = "LENGTH_SIZE";
        }
        return result;
    }

    public void checkIntRange(FormField field, Value fieldValue, Value result,
            Type keyType, Type validatorType) {

        Call c = new Call(validatorType, "validateIntRange");
		
        c.addArgument(fieldValue);
        c.addArgument(new StringLiteral(field.getValidation().getMinRange()));
        c.addArgument(new StringLiteral(field.getValidation().getMaxRange()));
        c.setNagation(true);
        openIf(c);

        Value messageKey = new Value(String.class, "messageKey");
        declare(messageKey, getErrorMessageKey(keyType, "RANGE"));

        Value arguments = new Value(Object[].class, "arguments");
        declare(arguments, getIntRangeErrorArgument(field, getFieldObject(
                field, fieldValue)));

        Receiver r = new Receiver(result);
        c = r.call("add");
        c.addArgument(new Value(new Type(
                "org.apache.struts.action.ActionErrors"),
                "ActionErrors.GLOBAL_ERROR"));
        c.addArgument(newActionError(messageKey, arguments));
        addStatement(r);

        closeIf();
    }

    public void checkTableFormat(Value tableItem, FormField field,
            Value result, Type keyType, Type validatorType) {

        String format = field.getValidation().getFormat();
        String methodName = FormatValidation.nameToMethodName(format);

        Call c = new Call(validatorType, methodName);
		
        c.addArgument(getTableField(tableItem, field.getValue()));
        c.setNagation(true);
        openIf(c);

        Value messageKey = new Value(String.class, "messageKey");
        declare(messageKey, getErrorMessageKey(keyType, FormatValidation
                .nameToFieldName(format)));

        Value arguments = new Value(Object[].class, "arguments");
        declare(arguments, getFormatErrorArgument(field, getTableFieldObject(
                tableItem, field.getValue())));

        Receiver r = new Receiver(result);
        c = r.call("add");
        c.addArgument(new Value(new Type(
                "org.apache.struts.action.ActionErrors"),
                "ActionErrors.GLOBAL_ERROR"));
        c.addArgument(newActionError(messageKey, arguments));
        addStatement(r);

        closeIf();
    }

    public void checkTableRegex(Value tableItem, FormField field, Value result,
            Type keyType, Type validatorType) {

        Call c = new Call(validatorType, "validateRegex");

        c.addArgument(getTableField(tableItem, field.getValue()));
        c.addArgument(new StringLiteral(field.getValidation().getRegex()));
        c.addArgument(new StringLiteral(field.getValidation()
                .getRegexMessageKey()));
        c.setNagation(true);
        openIf(c);

        Value messageKey = new Value(String.class, "messageKey");
        declare(messageKey, getErrorMessageKey(keyType, "REGEX"));

        Value arguments = new Value(Object[].class, "arguments");
        declare(arguments, getRegexErrorArgument(field, getTableFieldObject(
                tableItem, field.getValue())));

        Receiver r = new Receiver(result);
        c = r.call("add");
        c.addArgument(new Value(new Type(
                "org.apache.struts.action.ActionErrors"),
                "ActionErrors.GLOBAL_ERROR"));
        c.addArgument(newActionError(messageKey, arguments));
        addStatement(r);

        closeIf();
    }

    private Statement getTableField(Value item, Value value) {
        Statement result = null;
        Type type = value.getType();

        Receiver r = new Receiver(item, ReadAccessor.createName(value));

        if (type.isPrimitive() && type.equals(int.class)) {
            Call c = new Call(Integer.class, "toString");
            c.addArgument(r);
            result = c;
        } else if (type.isPrimitive() && type.equals(boolean.class)) {
            Call c = new Call(Boolean.class, "toString");
            c.addArgument(r);
            result = c;
        } else {
            result = r;
        }
        return result;
    }

    public void checkTableSize(Value tableItem, FormField field, Value result,
            Type keyType) {

        if (field.getValidation().getSizeValidationType().equals(
                SizeValidationType.BYTES)) {
            addLine("if (!Validator.validateStringSizeByBytes("
                    + getFormField(tableItem) + ", "
                    + new StringLiteral(field.getValidation().getMinSize())
                    + ", "
                    + new StringLiteral(field.getValidation().getMaxSize())
                    + ")) {");
        } else {
            addLine("if (!Validator.validateStringSizeByLength("
                    + getFormField(tableItem) + ", "
                    + new StringLiteral(field.getValidation().getMinSize())
                    + ", "
                    + new StringLiteral(field.getValidation().getMaxSize())
                    + ")) {");
        }

        addLine("String messageKey = ErrorMessageKey."
                + getSizeErrorMessageField(field) + ".getKey();");
        addLine("Object[] arguments = new Object[]{"
                + getFieldObject(field, tableItem) + ", \"" + field.getDescription()
                + "\"};");
        addLine("errors.add(ActionErrors.GLOBAL_ERROR, new ActionError(messageKey, arguments));");
        addLine("}");
    }

    public void checkTableIntRange(Value tableItem, FormField field,
            Value result, Type keyType, Type validatorType) {

        Call c = new Call(validatorType, "validateIntRange");
		
        Receiver r = new Receiver(tableItem, ReadAccessor.createName(field
                .getValue()));
        c.addArgument(r);
        c.addArgument(new StringLiteral(field.getValidation().getMinRange()));
        c.addArgument(new StringLiteral(field.getValidation().getMaxRange()));
        c.setNagation(true);
        openIf(c);

        Value messageKey = new Value(String.class, "messageKey");
        declare(messageKey, getErrorMessageKey(keyType, "RANGE"));

        Value arguments = new Value(Object[].class, "arguments");
        declare(arguments, getIntRangeErrorArgument(field, getTableFieldObject(
                tableItem, field.getValue())));

        r = new Receiver(result);
        c = r.call("add");
        c.addArgument(new Value(new Type(
                "org.apache.struts.action.ActionErrors"),
                "ActionErrors.GLOBAL_ERROR"));
        c.addArgument(newActionError(messageKey, arguments));
        addStatement(r);

        closeIf();
    }

    public Statement getLoopEndStatement(Value tableField, Value counter) {
        Receiver r = new Receiver(counter);
        r.join("<", new Receiver(tableField, "size"));
        return r;
    }

    public void assignTableItem(Value tableItem, Type type, Value tableField,
            Value counter) {
        Cast cast = new Cast(type);
        Receiver r = cast.value(tableField);
        Call call = r.call("get");
        call.addArgument(counter);
        assign(tableItem, cast);
    }

    private Statement getFieldObject(FormField field, Value fieldValue) {
        Statement result = null;

        Value value = field.getValue();
        Type type = value.getType();
		// TODO: 使用してないのでコメントアウト
        //String name = value.getName();
        if (type.equals(int.class)) {
            Call c = new Call(Integer.class);
            c.addArgument(fieldValue);
            result = c;
        } else if (type.equals(boolean.class)) {
            Call c = new Call(Boolean.class);
            c.addArgument(fieldValue);
            result = c;
        } else {
            result = new Statement(fieldValue);
        }
        return result;
    }

    private Statement getTableFieldObject(Value item, Value tableField) {
        Statement result = null;

        Type type = tableField.getType();
        Receiver r = new Receiver(item, ReadAccessor.createName(tableField));

        if (type.equals(int.class)) {
            Call c = new Call(Integer.class);
            c.addArgument(r);
            result = c;
        } else if (type.equals(boolean.class)) {
            Call c = new Call(Boolean.class);
            c.addArgument(r);
            result = c;
        } else {
            result = r;
        }
        return result;
    }

    private Receiver getErrorMessageKey(Type errorMessageKey, String fieldName) {
        Receiver r = new Receiver(errorMessageKey, fieldName);
        r.call("getKey");
        return r;
    }

    private Call newActionError(Value messageKey, Value messageArguments) {
        Call result = new Call(new Type("org.apache.struts.action.ActionError"));
        result.addArgument(messageKey);
        result.addArgument(messageArguments);
        return result;
    }

    private Array getSizeErrorArgument(FormField field, Statement dataStatement) {
        Array result = new Array(Object.class);
        String name = field.getDescription();
        FieldValidation validatoin = field.getValidation();
        result.addArgument(dataStatement);
        result.addArgument(new StringLiteral(validatoin.getMinSize()));
        result.addArgument(new StringLiteral(validatoin.getMaxSize()));
        result.addArgument(new StringLiteral(name));
        return result;
    }

    private Array getIntRangeErrorArgument(FormField field,
            Statement dataStatement) {
        Array result = new Array(Object.class);
        String name = field.getDescription();
        FieldValidation validatoin = field.getValidation();
        result.addArgument(dataStatement);
        result.addArgument(new StringLiteral(validatoin.getMinRange()));
        result.addArgument(new StringLiteral(validatoin.getMaxRange()));
        result.addArgument(new StringLiteral(name));
        return result;
    }

    private Array getFormatErrorArgument(FormField field,
            Statement dataStatement) {
        Array result = new Array(Object.class);
        String name = field.getDescription();
        FieldValidation validatoin = field.getValidation();
        result.addArgument(dataStatement);
        result.addArgument(new StringLiteral(validatoin.getFormat()));
        result.addArgument(new StringLiteral(name));
        return result;
    }

    private Array getRegexErrorArgument(FormField field, Statement dataStatement) {
        Array result = new Array(Object.class);
        String name = field.getDescription();
        FieldValidation validatoin = field.getValidation();
        result.addArgument(dataStatement);
        result.addArgument(new StringLiteral(validatoin.getRegex()));
        result.addArgument(new StringLiteral(name));
        return result;
    }
}
