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

import java.util.Locale;
import java.util.Map;
import java.util.logging.Logger;

import org.apache.commons.contract.Context;
import org.apache.commons.contract.Processor;
import org.apache.commons.contract.Result;
import org.apache.commons.contract.constraints.LocaleConstraints;
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.commons.i18n.MessageNotFoundException;
import org.apache.slide.projector.constraints.AnyConstraints;
import org.apache.slide.projector.processor.ProcessException;

public class ExceptionRenderer implements Processor {
    private static Logger logger = Logger.getLogger(ExceptionRenderer.class.getName());

    final static String OK = "ok";

    public final static String EXCEPTION = "exception";
    public final static String CLASS = "class";
    public final static String TITLE = "title";
    public final static String STACK_TRACE = "stackTrace";
    public final static String TEXT = "text";
    public final static String SUMMARY = "summary";
    public final static String DETAILS = "details";
    public final static String LOCALE = "locale";
    
    private final static ParameterDescriptor[] parameterDescriptors = new ParameterDescriptor[]{
            new ParameterDescriptor(EXCEPTION, new ParameterMessage("exceptionRenderer/exception"), new AnyConstraints()),
            new ParameterDescriptor(LOCALE, new ParameterMessage("exceptionRenderer/locale"), new LocaleConstraints(), Locale.getDefault())
    };
    
    private final static ResultDescriptor []resultDescriptors = new ResultDescriptor[] {
            new ResultDescriptor(StateDescriptor.OK_DESCRIPTOR,
            new ResultEntryDescriptor[]{
                new ResultEntryDescriptor(TITLE, new LocalizedMessage("exceptionRenderer/result/title"), StringConstraints.UNCONSTRAINED),
                new ResultEntryDescriptor(TEXT, new LocalizedMessage("exceptionRenderer/result/text"), StringConstraints.UNCONSTRAINED),
                new ResultEntryDescriptor(SUMMARY, new LocalizedMessage("exceptionRenderer/result/summary"), StringConstraints.UNCONSTRAINED),
                new ResultEntryDescriptor(DETAILS, new LocalizedMessage("exceptionRenderer/result/details"), StringConstraints.UNCONSTRAINED),
                new ResultEntryDescriptor(CLASS, new LocalizedMessage("exceptionRenderer/result/class"), StringConstraints.UNCONSTRAINED),
                new ResultEntryDescriptor(STACK_TRACE, new LocalizedMessage("exceptionRenderer/result/stackTrace"), StringConstraints.UNCONSTRAINED)
            })
    };

    public Result process(Map parameter, Context context) throws Exception {
        Locale locale = (Locale)parameter.get(LOCALE);
        Throwable throwable = (Throwable)parameter.get(EXCEPTION);
        Result result = new Result(OK);
        String title = null, text = null, summary = null;
        StringBuffer details = new StringBuffer();
        if ( throwable instanceof ProcessException ) {
            try {
                title = ((ProcessException)throwable).getLocalizedError().getTitle(locale);
                text = ((ProcessException)throwable).getLocalizedError().getText(locale);
                summary = ((ProcessException)throwable).getLocalizedError().getSummary(locale);
            } catch ( MessageNotFoundException e ) {
                title = ((ProcessException)throwable).getLocalizedError().getId();
                text = ((ProcessException)throwable).getLocalizedError().getId();
                summary = ((ProcessException)throwable).getLocalizedError().getId();
            }
            appendNestedDetails(details, throwable, locale);
        } else {
            title = throwable.getLocalizedMessage();
            text = throwable.getLocalizedMessage();
            summary = throwable.getLocalizedMessage();
            appendNestedDetails(details, throwable, locale);
        }
        if ( title == null ) title = "";
        if ( text == null ) text = "";
        if ( summary == null ) summary = "";
        result.addResultEntry(TITLE, new String(title));
        result.addResultEntry(TEXT, new String(text));
        result.addResultEntry(SUMMARY, new String(summary));
        result.addResultEntry(DETAILS, new String(details.toString()));
        result.addResultEntry(CLASS, new String(throwable.getClass().getName()));
        StackTraceElement []trace = throwable.getStackTrace();
        StringBuffer buffer = new StringBuffer(256);
        for ( int i = 0; i < trace.length; i++ ) {
            buffer.append(trace[i].toString());
            buffer.append(" ");
        }
        result.addResultEntry(STACK_TRACE, new String(buffer.toString()));
        return result;
    }

    private void appendNestedDetails(StringBuffer details, Throwable throwable, Locale locale) {
        if ( details.length() > 0 ) details.append(' ');
        if ( throwable instanceof ProcessException ) {
            LocalizedError message = ((ProcessException)throwable).getLocalizedError();
            details.append(message.getDetails(locale, ""));
        } else {
            details.append(throwable.getMessage());
        }
        if ( throwable.getCause() != null ) appendNestedDetails(details, throwable.getCause(), locale);
    }

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


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