/*
    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.tags;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.wiki.api.core.ContextEnum;
import org.apache.wiki.pages.PageManager;
import org.apache.wiki.util.TextUtil;

import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspWriter;
import java.io.IOException;
import java.io.Serializable;
import java.util.LinkedList;

/**
 * Implement a "breadcrumb" (most recently visited) trail.  This tag can be added to any view jsp page.
 * Separate breadcrumb trails are not tracked across multiple browser windows.<br>
 * The optional attributes are:
 * <p>
 * <b>maxpages</b>, the number of pages to store, 10 by default<br>
 * <b>separator</b>, the separator string to use between pages, " | " by default<br>
 * </p>
 *
 * <p>
 * This class is implemented by storing a breadcrumb trail, which is a
 * fixed size queue, into a session variable "breadCrumbTrail".
 * This queue is displayed as a series of links separated by a separator
 * character.
 * </p>
 */
public class BreadcrumbsTag extends WikiTagBase
{
    private static final long serialVersionUID = 0L;

    private static final Logger LOG = LogManager.getLogger(BreadcrumbsTag.class);
    /** The name of the session attribute representing the breadcrumbtrail */
    public static final String BREADCRUMBTRAIL_KEY = "breadCrumbTrail";
    private int m_maxQueueSize = 11;
    private String m_separator = ", ";

    /**
     *  {@inheritDoc}
     */
    @Override
    public void initTag()
    {
        super.initTag();
        m_maxQueueSize = 11;
        m_separator = ", ";
    }

    /**
     *  Returns the maxpages.  This may differ from what was set by setMaxpages().
     *
     *  @return The current size of the pages.
     */
    public int getMaxpages()
    {
        return m_maxQueueSize;
    }

    /**
     *  Sets how many pages to show.
     *
     *  @param maxpages The amount.
     */
    public void setMaxpages( final int maxpages)
    {
        m_maxQueueSize = maxpages + 1;
    }

    /**
     *  Get the separator string.
     *
     *  @return The string set in setSeparator()
     */
    public String getSeparator()
    {
        return m_separator;
    }

    /**
     *  Set the separator string.
     *
     *  @param separator A string which separates the page names.
     */
    public void setSeparator( final String separator)
    {
        m_separator = TextUtil.replaceEntities( separator );
    }

    /**
     *  {@inheritDoc}
     */
    @Override
    public int doWikiStartTag() throws IOException {
        final HttpSession session = pageContext.getSession();
        FixedQueue trail = (FixedQueue) session.getAttribute(BREADCRUMBTRAIL_KEY);
        final String page = m_wikiContext.getPage().getName();

        if( trail == null ) {
            trail = new FixedQueue(m_maxQueueSize);
        } else {
            //  check if page still exists (could be deleted/renamed by another user)
            for (int i = 0;i<trail.size();i++) {
                if( !m_wikiContext.getEngine().getManager( PageManager.class ).wikiPageExists( trail.get( i ) ) ) {
                    trail.remove(i);
                }
            }
        }

        if( m_wikiContext.getRequestContext().equals( ContextEnum.PAGE_VIEW.getRequestContext() ) ) {
            if( m_wikiContext.getEngine().getManager( PageManager.class ).wikiPageExists( page ) ) {
                if( trail.isEmpty() ) {
                    trail.pushItem( page );
                } else {
                    // Don't add the page to the queue if the page was just refreshed
                    if( !trail.getLast().equals( page ) ) {
                        trail.pushItem( page );
                    }
                }
            } else {
                LOG.debug( "didn't add page because it doesn't exist: " + page );
            }
        }

        session.setAttribute(BREADCRUMBTRAIL_KEY, trail);

        //
        //  Print out the breadcrumb trail
        //

        // FIXME: this code would be much simpler if we could just output the [pagename] and then use the
        // wiki engine to output the appropriate wikilink

        final JspWriter out     = pageContext.getOut();
        final int queueSize     = trail.size();
        final String linkclass  = "wikipage";
        String curPage;

        for( int i = 0; i < queueSize - 1; i++ ) {
            curPage = trail.get(i);

            //FIXME: I can't figure out how to detect the appropriate jsp page to put here, so I hard coded Wiki.jsp
            //This breaks when you view an attachment metadata page
            out.print( "<a class=\"" + linkclass + "\" href=\"" + m_wikiContext.getViewURL(curPage) + "\">" +
                       TextUtil.replaceEntities( curPage ) +
                       "</a>" );

            if( i < queueSize - 2 ) {
                out.print(m_separator);
            }
        }

        return SKIP_BODY;
    }

    /**
     * Extends the LinkedList class to provide a fixed-size queue implementation
     */
    public static class FixedQueue extends LinkedList< String > implements Serializable {
        private final int m_size;
        private static final long serialVersionUID = 0L;

        FixedQueue( final int size ) {
            m_size = size;
        }

        String pushItem( final String o ) {
            add( o );
            if( size() > m_size ) {
                return removeFirst();
            }

            return null;
        }

        /**
         * @param pageName the page to be deleted from the breadcrumb
         */
        public void removeItem( final String pageName ) {
            for( int i = 0; i < size(); i++ ) {
                final String page = get( i );
                if( page != null && page.equals( pageName ) ) {
                    remove( page );
                }
            }
        }

    }
}

