package org.apache.slide.projector.processor.form;

import java.util.Map;

import org.apache.commons.contract.Context;
import org.apache.commons.contract.Processor;
import org.apache.commons.contract.Result;
import org.apache.commons.contract.constraints.StringConstraints;
import org.apache.commons.contract.descriptor.ParameterDescriptor;
import org.apache.commons.contract.descriptor.ResultDescriptor;
import org.apache.commons.contract.descriptor.ResultEntryDescriptor;
import org.apache.commons.contract.descriptor.StateDescriptor;
import org.apache.commons.contract.i18n.ParameterMessage;
import org.apache.commons.i18n.LocalizedError;
import org.apache.commons.i18n.LocalizedMessage;
import org.apache.slide.projector.constraints.AnyConstraints;
import org.apache.slide.projector.constraints.URIConstraints;
import org.apache.slide.projector.engine.ProcessorManager;
import org.apache.slide.projector.processor.ConfigurationException;
import org.apache.slide.projector.processor.ProcessException;
import org.apache.slide.projector.processor.TemplateRenderer;
import org.apache.slide.projector.value.Streamable;
import org.apache.slide.projector.value.URI;
import org.apache.slide.projector.value.URIValue;

/**
 * @version $Revision: 1.6 $
 */

public class Control extends TemplateRenderer {
    private final static String NAME = " control";

    public final static String OPTIONAL_VALID_CONTROL = "valid optional";
    public final static String OPTIONAL_INVALID_CONTROL = "invalid optional";
    public final static String OPTIONAL_CONTROL = "optional";
    public final static String REQUIRED_CONTROL = "required";
    public final static String REQUIRED_VALID_CONTROL = "valid required";
    public final static String REQUIRED_INVALID_CONTROL = "invalid required";

    public final static String ACTION = "action";
    public final static String PARAMETER = "parameter";
    public final static String VALUE = "value";
    public final static String STATE = "state";

    protected Template optionalFragment, requiredFragment, validOptionalFragment, invalidOptionalFragment, validRequiredFragment, invalidRequiredFragment;

    private ParameterDescriptor[] parameterDescriptors;
    private final static ResultDescriptor[] resultDescriptors = new ResultDescriptor[] {
            new ResultDescriptor(
            StateDescriptor.OK_DESCRIPTOR,
            new ResultEntryDescriptor[]{
                new ResultEntryDescriptor(OUTPUT, new LocalizedMessage("control/output"), AnyConstraints.UNCONSTRAINED)
            })
    };

    public Control() {
        setRequiredFragments(new String[] { OPTIONAL_CONTROL+getName(), REQUIRED_CONTROL+getName() });
        setOptionalFragments(new String[] { OPTIONAL_VALID_CONTROL+getName(), OPTIONAL_INVALID_CONTROL+getName(),
                                            REQUIRED_VALID_CONTROL+getName(), REQUIRED_INVALID_CONTROL+getName() });
    }

    public Result process(Map parameter, Context context) throws Exception {
        String state = parameter.get(STATE).toString();
        Template fragment = optionalFragment;
        if ( state.equals(REQUIRED_CONTROL) ) {
            fragment = requiredFragment;
        }
        if ( state.equals(OPTIONAL_VALID_CONTROL) ) {
            fragment = validOptionalFragment;
        } else if ( state.equals(OPTIONAL_INVALID_CONTROL) ) {
            fragment = invalidOptionalFragment;
        } else if ( state.equals(REQUIRED_VALID_CONTROL) ) {
            fragment = validRequiredFragment;
        } else if ( state.equals(REQUIRED_INVALID_CONTROL) ) {
            fragment = invalidRequiredFragment;
        }
        return new Result(state, OUTPUT, renderFragment(fragment, parameter));
    }

    public void configure(Streamable config) throws ConfigurationException {
        super.configure(config);
        ParameterDescriptor[] parentParameterDescriptors = super.getParameterDescriptors();
        parameterDescriptors = new ParameterDescriptor[parentParameterDescriptors.length + 3];
        int counter = 0;
        for ( int i = 0; i < parentParameterDescriptors.length; i++ ) {
            if (!parentParameterDescriptors[i].getName().equals(FRAGMENT)) {
                parameterDescriptors[counter] = parentParameterDescriptors[i];
                counter++;
            }
        }
        parameterDescriptors[parentParameterDescriptors.length - 1] =
                new ParameterDescriptor(ACTION, new ParameterMessage("control/action"), new URIConstraints());
        parameterDescriptors[parentParameterDescriptors.length ] =
                new ParameterDescriptor(PARAMETER, new ParameterMessage("control/parameter"), new URIConstraints());
        parameterDescriptors[parentParameterDescriptors.length + 1] =
                new ParameterDescriptor(VALUE, new ParameterMessage("control/value"), new AnyConstraints(), null);
        parameterDescriptors[parentParameterDescriptors.length + 2] =
                new ParameterDescriptor(STATE, new ParameterMessage("control/state"), new StringConstraints(new String[] {OPTIONAL_VALID_CONTROL, OPTIONAL_INVALID_CONTROL, OPTIONAL_CONTROL, REQUIRED_CONTROL, REQUIRED_VALID_CONTROL, REQUIRED_INVALID_CONTROL}), new String(OPTIONAL));

        try {
            optionalFragment = getRequiredFragment(OPTIONAL_CONTROL + getName());
            requiredFragment = getRequiredFragment(REQUIRED_CONTROL + getName());
        } catch ( ProcessException exception ) {
            throw new ConfigurationException(new LocalizedError("control/requiredFragmentMissing", new String[] { OPTIONAL+getName()}), exception);
        }
        validOptionalFragment = getOptionalFragment(OPTIONAL_VALID_CONTROL + getName(), optionalFragment);
        invalidOptionalFragment = getOptionalFragment(OPTIONAL_INVALID_CONTROL + getName(), optionalFragment);
        validRequiredFragment = getOptionalFragment(REQUIRED_VALID_CONTROL + getName(), requiredFragment);
        invalidRequiredFragment = getOptionalFragment(REQUIRED_INVALID_CONTROL + getName(), requiredFragment);
    }

    public ParameterDescriptor[] getParameterDescriptors() {
        return parameterDescriptors;
    }

    public ResultDescriptor[] getResultDescriptors() {
        return resultDescriptors;
    }

    static ParameterDescriptor getParameterDescriptor(Map parameter, Context context) throws ProcessException {
        URI actionUri = (URIValue)parameter.get(ACTION);
        Processor action = ProcessorManager.getInstance().getProcessor(actionUri);
        String parameterName = parameter.get(PARAMETER).toString();
        ParameterDescriptor parameterDescriptor = ProcessorManager.getParameterDescriptor(action, parameterName);
        if ( parameterDescriptor == null ) throw new ProcessException(new LocalizedError("control/actionParameterNotFound", new Object[] { parameterName, actionUri }));
        return parameterDescriptor;
    }

    protected String getName() {
        return NAME;
    }
}