
package info.freelibrary.jiiify.handlers;

import static info.freelibrary.jiiify.Constants.MESSAGES;

import java.io.File;

import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.jknack.handlebars.Context;
import com.github.jknack.handlebars.JsonNodeValueResolver;

import info.freelibrary.jiiify.Configuration;
import info.freelibrary.util.Logger;
import info.freelibrary.util.LoggerFactory;
import info.freelibrary.util.PairtreeRoot;
import info.freelibrary.util.PairtreeUtils;

import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;

abstract class JiiifyHandler implements Handler<RoutingContext> {

    protected final Logger LOGGER = LoggerFactory.getLogger(getClass(), MESSAGES);

    protected final Configuration myConfig;

    /**
     * Creates a default handler from which other Jiiify Handlers can be derived.
     *
     * @param aConfig A Jiiify configuration object
     */
    protected JiiifyHandler(final Configuration aConfig) {
        myConfig = aConfig;
    }

    /**
     * Returns the appropriate Pairtree data directory.
     *
     * @param aID An ID to access
     * @param aConfig An application configuration
     * @return The appropriate Pairtree data directory
     */
    PairtreeRoot getPairtreeRoot(final String aID, final Configuration aConfig) {
        if (aConfig.hasIDPrefixMatch(aID)) {
            final String idPrefix = aConfig.getIDPrefix(aID);

            if (aConfig.hasDataDir(idPrefix)) {
                return aConfig.getDataDir(idPrefix);
            } else {
                LOGGER.warn("Checking an ID with a prefix for which there isn't a data directory");
                return aConfig.getDataDir();
            }
        } else {
            return aConfig.getDataDir();
        }
    }

    /**
     * Returns a Pairtree path for a file in a Pairtree structure.
     *
     * @param aPairtreeRoot The root of the Pairtree structure
     * @param aID The ID of the object in the Pairtree structure
     * @param aFileName The name of the file from the object in the Pairtree structure
     * @return The Pairtree path for the supplied file
     */
    String getPairtreePath(final PairtreeRoot aPairtreeRoot, final String aID, final String aFileName) {
        final String objPath = PairtreeUtils.mapToPtPath(aPairtreeRoot.getAbsolutePath(), aID, aID);
        return objPath + File.separator + aFileName;
    }

    /**
     * Prepares the supplied JSON object for use in the Handlebars context.
     *
     * @param aJsonNode A JSON object
     * @param aContext A context with the current session information
     * @return A Handlebars context that can be passed to the template engine
     */
    Context toHbsContext(final ObjectNode aJsonObject, final RoutingContext aContext) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("{} JSON passed to template page: {}", getClass().getSimpleName(), aJsonObject.toString());
        }

        // Are we logged into the administrative interface?
        if (aContext.user() == null) {
            aJsonObject.put("logged-in", false);
        } else {
            aJsonObject.put("logged-in", true);
        }

        return Context.newBuilder(aJsonObject).resolver(JsonNodeValueResolver.INSTANCE).build();
    }

    /**
     * Shorthand for a commonly used call that formats a property before passing it to the Handlebars template engine.
     *
     * @param aString A property string that needs to be formatted before giving to the Handlebars template engine
     * @return A string formatted for use by the Handlebars template engine
     */
    String fmt(final String aString) {
        return aString.replace('.', '-');
    }

    /**
     * Shorthand for a commonly used call that formats a message string.
     *
     * @param aMessage A message that needs to be formatted with additional information before using
     * @param aDetails Additional information that will fill in the details of the message string
     * @return A string formatted and ready for use in a message
     */
    String msg(final String aMessage, final Object... aDetails) {
        return LOGGER.getMessage(aMessage, aDetails);
    }

    /**
     * A convenience method for failing a particular context.
     *
     * @param aContext The context to mark as a failure
     * @param aThrowable The exception that caused the context to fail
     */
    void fail(final RoutingContext aContext, final Throwable aThrowable) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("{} is failing this RoutingContext", getClass().getName());
        }

        aContext.fail(500);

        if (aThrowable != null) {
            aContext.fail(aThrowable);
        }

        // This is just a default message... a more detailed one can be set
        aContext.put(FailureHandler.ERROR_MESSAGE, aThrowable.getMessage());
    }
}

@@ -4477,294 +4477,993 @@
     
-if (LOGGER.isDebugEnabled()) %7B%0A            LOGGER.debug(%22%7B%7D is failing this RoutingContext%22, getClass().getName());%0A        %7D%0A%0A        aContext.fail(500);%0A%0A        if (aThrowable != null) %7B%0A            aContext.fail(aThrowable);%0A        %7D%0A%0A        // This is just a default m
+fail(aContext, aThrowable, aThrowable.getMessage());%0A    %7D%0A%0A    /**%0A     * A convenience method for failing a particular context.%0A     *%0A     * @param aContext The context to mark as a failure%0A     * @param aThrowable The exception that caused the context to fail%0A     * @param aMessage A more detailed message to supplement the exception message%0A     */%0A    void fail(final RoutingContext aContext, final Throwable aThrowable, final String aMessage) %7B%0A        if (LOGGER.isDebugEnabled()) %7B%0A            LOGGER.debug(%22%7B%7D is failing this RoutingContext%22, getClass().getName());%0A        %7D%0A%0A        aContext.fail(500);%0A%0A        if (aThrowable != null) %7B%0A            aContext.fail(aThrowable);%0A        %7D%0A%0A        aContext.put(FailureHandler.ERROR_MESSAGE, aMessage);%0A    %7D%0A%0A    /**%0A     * A convenience method for failing a particular context.%0A     *%0A     * @param aContext The context to mark as a failure%0A     * @param aFailCode The type of HTTP response failure%0A     * @param aM
 essage
-... a
+ A
  mor
@@ -5477,22 +5477,319 @@
 led 
-one can be set
+message to supplement the exception message%0A     */%0A    void fail(final RoutingContext aContext, final int aFailCode, final String aMessage) %7B%0A        if (LOGGER.isDebugEnabled()) %7B%0A            LOGGER.debug(%22%7B%7D is failing this RoutingContext%22, getClass().getName());%0A        %7D%0A%0A        aContext.fail(aFailCode);
 %0A   
@@ -5837,38 +5837,23 @@
 SSAGE, a
-Throwable.get
 Message
-()
 );%0A    %7D

Update JiiifyHandler's RoutingContext fail() convenience methods