/* 
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you under the Apache License, Version 2.0 (the
    "License"); you may not use this file except in compliance
    with the License.  You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing,
    software distributed under the License is distributed on an
    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    KIND, either express or implied.  See the License for the
    specific language governing permissions and limitations
    under the License.  
 */
package org.apache.wiki.ui;

import org.apache.wiki.util.TextUtil;


/**
 * Abstract, immutable Command implementation class. All of the fields in this
 * class are <code>final</code>. This class is thread-safe.
 * @since 2.4.22
 */
public abstract class AbstractCommand implements Command
{
    private static final Command[] ALL_COMMANDS = new Command[] {
        PageCommand.ATTACH,
        PageCommand.COMMENT,
        PageCommand.CONFLICT,
        PageCommand.DELETE,
        PageCommand.DIFF,
        PageCommand.EDIT,
        PageCommand.INFO,
        PageCommand.NONE,
        PageCommand.OTHER,
        PageCommand.PREVIEW,
        PageCommand.RENAME,
        PageCommand.RSS,
        PageCommand.UPLOAD,
        PageCommand.VIEW,
        GroupCommand.DELETE_GROUP,
        GroupCommand.EDIT_GROUP,
        GroupCommand.VIEW_GROUP,
        WikiCommand.CREATE_GROUP,
        WikiCommand.ERROR,
        WikiCommand.FIND,
        WikiCommand.INSTALL,
        WikiCommand.LOGIN,
        WikiCommand.LOGOUT,
        WikiCommand.MESSAGE,
        WikiCommand.PREFS,
        WikiCommand.WORKFLOW,
        WikiCommand.ADMIN,
        RedirectCommand.REDIRECT
    };

    private static final String    HTTPS = "HTTPS://";

    private static final String    HTTP = "HTTP://";

    private final String           m_jsp;

    private final String           m_jspFriendlyName;

    private final String           m_urlPattern;

    private final String           m_requestContext;

    private final String           m_contentTemplate;

    private final Object           m_target;

    /**
     * Constructs a new Command with a specified wiki context, URL pattern,
     * content template and target. The URL pattern is used to derive
     * the JSP; if it is a "local" JSP (that is, it does not contain
     * the <code>http://</code> or <code>https://</code> prefixes),
     * then the JSP will be a cleansed version of the URL pattern;
     * symbols (such as <code>%u</code>) will removed. If it the supplied
     * URL pattern points to a non-local destination, the JSP will be set
     * to the value supplied, unmodified.
     * @param requestContext the request context
     * @param urlPattern the URL pattern
     * @param contentTemplate the content template; may be <code>null</code>
     * @param target the target of the command, such as a WikiPage; may be
     *            <code>null</code>
     * @throws IllegalArgumentException if the request content or URL pattern is
     *             <code>null</code>
     */
    protected AbstractCommand( String requestContext, String urlPattern, String contentTemplate, Object target )
    {
        if ( requestContext == null || urlPattern == null )
        {
            throw new IllegalArgumentException( "Request context, URL pattern and type must not be null." );
        }

        m_requestContext = requestContext;

        if ( urlPattern.toUpperCase().startsWith( HTTP ) ||
             urlPattern.toUpperCase().endsWith( HTTPS ) )
        {
            // For an HTTP/HTTPS url, pass it through without modification
            m_jsp = urlPattern;
            m_jspFriendlyName = "Special Page";
        }
        else
        {
            // For local JSPs, take everything to the left of ?, then
            // delete all variable substitutions
            String jsp = urlPattern;
            int qPosition = urlPattern.indexOf( '?' );
            if ( qPosition != -1 )
            {
                jsp = jsp.substring( 0, qPosition );
            }
            m_jsp = removeSubstitutions(jsp);

            // Calculate the "friendly name" for the JSP
            if ( m_jsp.toUpperCase().endsWith( ".JSP" ) )
            {
                m_jspFriendlyName = TextUtil.beautifyString( m_jsp.substring( 0, m_jsp.length() - 4 ) );
            }
            else
            {
                m_jspFriendlyName = m_jsp;
            }
        }

        m_urlPattern = urlPattern;

        m_contentTemplate = contentTemplate;

        m_target = target;
    }

    //
    //  This is just *so* much faster than doing String.replaceAll().  It would, in fact,
    //  be worth to cache this value.
    //
    private String removeSubstitutions(String jsp)
    {
        //return jsp.replaceAll( "\u0025[a-z|A-Z]", "" );
    	StringBuilder newjsp = new StringBuilder( jsp.length() );
        for( int i = 0; i < jsp.length(); i++ )
        {
            char c = jsp.charAt(i);
            if( c == '%' && i < jsp.length()-1 && Character.isLetterOrDigit( jsp.charAt(i+1) ) )
            {
                i++;
                continue;
            }
            newjsp.append( c );
        }
        return newjsp.toString();
    }

    /**
     * Returns a defensively-created array of all
     * static Commands.
     * @return the array of commands
     */
    public static final Command[] allCommands()
    {
        return ALL_COMMANDS.clone();
    }

    /**
     * @see org.apache.wiki.ui.Command#targetedCommand(Object)
     */
    public abstract Command targetedCommand( Object target );

    /**
     * @see org.apache.wiki.ui.Command#getContentTemplate()
     */
    public final String getContentTemplate()
    {
        return m_contentTemplate;
    }

    /**
     * @see org.apache.wiki.ui.Command#getJSP()
     */
    public final String getJSP()
    {
        return m_jsp;
    }

    /**
     * @see org.apache.wiki.ui.Command#getName()
     */
    public abstract String getName();

    /**
     * @see org.apache.wiki.ui.Command#getRequestContext()
     */
    public final String getRequestContext()
    {
        return m_requestContext;
    }

    /**
     * @see org.apache.wiki.ui.Command#getTarget()
     */
    public final Object getTarget()
    {
        return m_target;
    }

    /**
     * @see org.apache.wiki.ui.Command#getURLPattern()
     */
    public final String getURLPattern()
    {
        return m_urlPattern;
    }

    /**
     * Returns the "friendly name" for this command's JSP, namely
     * a beatified version of the JSP's name without the .jsp suffix.
     * @return the friendly name
     */
    protected final String getJSPFriendlyName()
    {
        return m_jspFriendlyName;
    }

    /**
     * Returns a String representation of the Command.
     * @see java.lang.Object#toString()
     */
    public final String toString()
    {
        return "Command" +
               "[context=" + m_requestContext + "," +
               "urlPattern=" + m_urlPattern + "," +
               "jsp=" +  m_jsp +
               ( m_target == null ? "" : ",target=" + m_target + m_target.toString() ) +
               "]";
    }

}
