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

import java.util.Map;

import org.apache.commons.contract.Context;
import org.apache.commons.contract.Result;
import org.apache.commons.contract.constraints.NumberConstraints;
import org.apache.commons.contract.descriptor.ParameterDescriptor;
import org.apache.commons.contract.i18n.ParameterMessage;
import org.apache.commons.i18n.LocalizedError;
import org.apache.slide.projector.processor.ConfigurationException;
import org.apache.slide.projector.processor.ProcessException;
import org.apache.slide.projector.processor.TemplateArrayRenderer;
import org.apache.slide.projector.value.Streamable;
import org.apache.slide.projector.value.Text;

public class TableArrayRenderer extends TemplateArrayRenderer {
    final private static String HEADER_FRAGMENT = "header";
    final private static String FOOTER_FRAGMENT = "footer";
    final private static String FIRST_FRAGMENT = "first";
    final private static String LAST_FRAGMENT = "last";
    final private static String EVEN_FRAGMENT = "even";
    final private static String DEFAULT_FRAGMENT = "default";
    final private static String EMPTY_FRAGMENT = "empty";

    final private static String OFFSET = "offset";
    final private static String LENGTH = "length";

    private Template headerTemplate;
    private Template footerTemplate;
    private Template firstTemplate;
    private Template lastTemplate;
    private Template evenTemplate;
    private Template defaultTemplate;
    private Template emptyTemplate;

    private ParameterDescriptor []parameterDescriptors;

    public TableArrayRenderer() {
        setRequiredFragments(new String[] { DEFAULT_FRAGMENT });
        setOptionalFragments(new String[] { EMPTY_FRAGMENT, HEADER_FRAGMENT, FOOTER_FRAGMENT, FIRST_FRAGMENT, LAST_FRAGMENT, EVEN_FRAGMENT });
    }

    public void configure(Streamable config) throws ConfigurationException {
        super.configure(config);
        ParameterDescriptor []parentParameterDescriptors = super.getParameterDescriptors();
        // remove fragment parameter
        parameterDescriptors = new ParameterDescriptor[parentParameterDescriptors.length+1];
        int index = 0;
        for ( int i = 0; i < parentParameterDescriptors.length; i++ ) {
            ParameterDescriptor descriptor = parentParameterDescriptors[i];
            if ( descriptor.getName() != FRAGMENT ) {
                parameterDescriptors[index] = parentParameterDescriptors[i];
                index++;
            }
        }
        parameterDescriptors[parentParameterDescriptors.length-1] = new ParameterDescriptor(OFFSET, new ParameterMessage("tableRenderer/offset"), new NumberConstraints(), new Integer(0));
        parameterDescriptors[parentParameterDescriptors.length] = new ParameterDescriptor(LENGTH, new ParameterMessage("tableRenderer/length"), new NumberConstraints(), new Integer(0));

        headerTemplate = getOptionalFragment(HEADER_FRAGMENT);
        footerTemplate = getOptionalFragment(FOOTER_FRAGMENT);
        firstTemplate = getOptionalFragment(FIRST_FRAGMENT);
        lastTemplate = getOptionalFragment(LAST_FRAGMENT);
        evenTemplate = getOptionalFragment(EVEN_FRAGMENT);
        emptyTemplate = getOptionalFragment(EMPTY_FRAGMENT);
        try {
            defaultTemplate = getRequiredFragment(DEFAULT_FRAGMENT);
        } catch ( ProcessException exception ) {
            throw new ConfigurationException(new LocalizedError("tableRenderer/fragmentsMissing"), exception);
        }
    }

    public Result process(Map parameter, Context context) throws Exception {
        StringBuffer buffer = new StringBuffer(1024);
        int offset = ((Number)parameter.get(OFFSET)).intValue();
        int length = ((Number)parameter.get(LENGTH)).intValue();
        parameter.remove(OFFSET);
        parameter.remove(LENGTH);
        int maxIndex = getMaxIndex(parameter);
        offset = Math.min(offset, maxIndex);
        if ( length > 0 ) {
            length = Math.min(length, maxIndex-offset);
        } else {
            length = maxIndex-offset;
        }
        if ( maxIndex == 0 ) {
            emptyTemplate.evaluate(buffer, parameter);
        } else {
            if ( headerTemplate != null ) headerTemplate.evaluate(buffer, parameter);
            for ( int i = 0; i < length; i++ ) {
                if ( i == 0 && firstTemplate != null ) {
                    firstTemplate.evaluate(buffer, parameter, i+offset);
                } else if ( i == length -1 && lastTemplate != null ) {
                    lastTemplate.evaluate(buffer, parameter, i+offset);
                } else if ( evenTemplate != null && i%2 == 0 ) {
                    evenTemplate.evaluate(buffer, parameter, i+offset);
                } else {
                    defaultTemplate.evaluate(buffer, parameter, i+offset);
                }
            }
            if ( footerTemplate != null ) footerTemplate.evaluate(buffer, parameter);
        }
        return new Result(OK, OUTPUT, new Text(buffer.toString(), defaultTemplate.getContentType(), false ));
    }

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