
package uk.co.wingpath.util;

import java.util.*;

/**
* This class implements the {@link Reporter} interface by wrapping a
* {@code Reporter} and applying re-write rules to reported messages.
* <p>A re-write rule can change the text of a message, change the level of
* a message, add a help ID, or discard a message altogether.
*/
public class SubstituteReporter
    implements Reporter
{
    private class Rule
    {
        final ReporterLevel level;
        final Substituter substituter;
        final ReporterLevel newLevel;
        final String helpId;

        Rule (ReporterLevel level, Substituter substituter,
            ReporterLevel newLevel, String helpId)
        {
            this.level = level;
            this.substituter = substituter;
            this.newLevel = newLevel;
            this.helpId = helpId;
        }

        boolean apply (ReporterLevel level, String helpId, String msg,
            Throwable t)
        {
            if (level != this.level)
                return false;
            String newMsg = substituter.substitute (msg);
            if (newMsg == null)
                return false;
            if (this.helpId != null)
                helpId = this.helpId;
            switch (newLevel)
            {
            case FATAL:
                if (t == null)
                    reporter.fatal (newMsg);
                else
                    reporter.fatal (newMsg, t);
                break;
            case ERROR:
                reporter.error (helpId, newMsg);
                break;
            case WARNING:
                reporter.warning (helpId, newMsg);
                break;
            case INFO:
                reporter.info (helpId, newMsg);
                break;
            case DEBUG:
                if (t == null)
                    reporter.debug (newMsg);
                else
                    reporter.debug (newMsg, t);
                break;
            case TRACE:
                reporter.trace (newMsg);
                break;
            }
            return true;
        }
    }

    private final Reporter reporter;
    private final List<Rule> rules;

    /**
    * Constructs a {@code SubstituteReporter} wrapping the supplied
    * reporter.
    * @param reporter the reporter to be wrapped.
    */
    public SubstituteReporter (Reporter reporter)
    {
        this.reporter = reporter;
        rules = new ArrayList<Rule> ();
    }

    /**
    * Adds a re-write rule.
    * @param level the level to which the rule applies.
    * @param substituter the substitution to be applied.
    * @param newLevel the level at which messages are to be forwarded.
    * The special level {@code NONE} may be used to discard metching messages.
    * @param helpId the help ID to be added. May be {@code null} if no
    * help ID is to be added.
    */
    public void addRule (ReporterLevel level, Substituter substituter,
        ReporterLevel newLevel, String helpId)
    {
        rules.add (new Rule (level, substituter, newLevel, helpId));
    }

    /**
    * Adds a re-write rule.
    * @param level the level to which the rule applies.
    * @param pattern the pattern to match the message against.
    * @param format the format for constructing the new message.
    * @param newLevel the level at which messages are to be forwarded.
    * The special level {@code NONE} may be used to discard metching messages.
    * @param helpId the help ID to be added. May be {@code null} if no
    * help ID is to be added.
    */
    public void addRule (ReporterLevel level, String pattern, String format,
        ReporterLevel newLevel, String helpId)
    {
        rules.add (new Rule (level, new Substituter (pattern, format),
            newLevel, helpId));
    }

    private boolean applyRules (ReporterLevel level, String helpId, String msg,
        Throwable t)
    {
        for (Rule rule : rules)
        {
            if (rule.apply (level, helpId, msg, t))
                return true;
        }

        return false;
    }

    public void fatal (String msg)
    {
        if (!applyRules (ReporterLevel.FATAL, null, msg, null))
            reporter.fatal (msg);
    }

    public void fatal (String msg, Throwable t)
    {
        if (!applyRules (ReporterLevel.FATAL, null, msg, t))
            reporter.fatal (msg, t);
    }

    public void error (String helpId, String msg)
    {
        if (!applyRules (ReporterLevel.ERROR, helpId, msg, null))
            reporter.error (helpId, msg);
    }

    public void warning (String helpId, String msg)
    {
        if (!applyRules (ReporterLevel.WARNING, helpId, msg, null))
            reporter.warning (helpId, msg);
    }

    public void info (String helpId, String msg)
    {
        if (!applyRules (ReporterLevel.INFO, helpId, msg, null))
            reporter.info (helpId, msg);
    }

    public void debug (String msg)
    {
        if (!applyRules (ReporterLevel.DEBUG, null, msg, null))
            reporter.debug (msg);
    }

    public void debug (String msg, Throwable t)
    {
        if (!applyRules (ReporterLevel.DEBUG, null, msg, t))
            reporter.debug (msg, t);
    }

    public void trace (String msg)
    {
        if (!applyRules (ReporterLevel.TRACE, null, msg, null))
            reporter.trace (msg);
    }

    public void clear ()
    {
        reporter.clear ();
    }
}

