/*
 * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */
package com.arsdigita.cms.dispatcher;

import com.arsdigita.dispatcher.ChainedDispatcher;
import com.arsdigita.dispatcher.DispatcherHelper;
import com.arsdigita.dispatcher.RequestContext;
import com.arsdigita.web.LoginSignal;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.libreccm.cdi.utils.CdiUtil;
import org.libreccm.security.PermissionChecker;
import org.libreccm.security.Shiro;
import org.librecms.contentsection.ContentItem;
import org.librecms.contentsection.ContentSection;
import org.librecms.contentsection.ContentSectionManager;
import org.librecms.contentsection.ContentSectionServlet;
import org.librecms.contentsection.privileges.ItemPrivileges;
import org.librecms.dispatcher.ItemResolver;

/**
 * Dispatches to the JSP or Servlet for rendering a content item.
 *
 * @author Karl Goldstein (karlg@arsdigita.com)
 *
 */
public class ItemDispatcher implements ChainedDispatcher {

    private static Logger LOGGER = LogManager.getLogger(ItemDispatcher.class);

    public static Map s_itemResolverCache = Collections.synchronizedMap(
        new HashMap());
    public static Map s_templateResolverCache = Collections.synchronizedMap(
        new HashMap());

    public static final String FILE_SUFFIX = ".jsp";
    public static final String INDEX_FILE = "/index";
//  public static final String TEMPLATE_ROOT =
//                             "/packages/content-section/templates";
//  public static final String DEFAULT_ITEM_TEMPLATE = "/default/item.jsp";
//  public static final String DEFAULT_FOLDER_TEMPLATE = "/default/folder.jsp";

    public static final String XML_SUFFIX = ".xml";
    public static final String XML_MODE = "xmlMode";

    private static boolean m_cacheItems = true;

    /**
     * The context for previewing items
     */
    public static final String PREVIEW = "/preview";

    protected ItemXML m_itemXML;

    public ItemDispatcher() {
        super();
        m_itemXML = new ItemXML();
    }

    public static void setCacheItems(boolean value) {
        m_cacheItems = value;
    }

    public int chainedDispatch(final HttpServletRequest request,
                               final HttpServletResponse response,
                               final RequestContext actx)
        throws IOException, ServletException {
        String queryString = request.getQueryString();
        String url = actx.getRemainingURLPart();

        LOGGER.info("Resolving item URL " + url);

        if (url.endsWith(XML_SUFFIX)) {
            request.setAttribute(XML_MODE, Boolean.TRUE);
            LOGGER.debug("StraightXML Requested");
            url = "/" + url.substring(0, url.length() - XML_SUFFIX.length());
        } else {
            request.setAttribute(XML_MODE, Boolean.FALSE);
            // it's neither a .jsp or a .xml, thus its an error
            if (url.endsWith(FILE_SUFFIX)) {
                url = "/" + url
                    .substring(0, url.length() - FILE_SUFFIX.length());
            } else if (url.endsWith("/")) {
                url = "/" + url.substring(0, url.length() - 1);
            } else {
                LOGGER.warn("Fail: URL doesn't have right suffix.");
                return ChainedDispatcher.DISPATCH_CONTINUE;
            }
        }

        final ContentSection section = ContentSectionServlet.getContentSection(
            request);
        // ContentSectionDispatcher.getContentSection(request);

        final ContentItem item = getItem(section, url);
        if (item == null) {
            LOGGER.warn("Fail: No live item found matching " + url);
            return ChainedDispatcher.DISPATCH_CONTINUE;
        }

        request.setAttribute(ContentSectionDispatcher.CONTENT_ITEM, item);

        LOGGER.debug("MATCHED " + item.getObjectId());

        // Work out how long to cache for....
        // We take minimum(default timeout, lifecycle expiry)
        //ToDo
//        Lifecycle cycle = item.getLifecycle();
        int expires = DispatcherHelper.getDefaultCacheExpiry();
//        if (cycle != null) {
//            Date endDate = cycle.getEndDate();
//
//            if (endDate != null) {
//                int maxAge = (int) ( ( endDate.getTime() - System.currentTimeMillis() ) / 1000l );
//                if (maxAge < expires) {
//                    expires = maxAge;
//                }
//            }
//        }
//ToDo end
        // NB, this is not the same as the security check previously
        // We are checking if anyone can access - ie can we allow
        // this page to be publically cached
        if (m_cacheItems && !url.startsWith(PREVIEW)) {
//            if (sm.canAccess((User)null, SecurityManager.PUBLIC_PAGES, item)) {
            if (CdiUtil.createCdiUtil().findBean(PermissionChecker.class)
                .isPermitted(
                    ItemPrivileges.VIEW_PUBLISHED, item)) {
                DispatcherHelper.cacheForWorld(response, expires);
            } else {
                DispatcherHelper.cacheForUser(response, expires);
            }
        } else {
            DispatcherHelper.cacheDisable(response);
        }

        if (((Boolean) request.getAttribute(XML_MODE)).booleanValue()) {
            m_itemXML.dispatch(request, response, actx);
            return ChainedDispatcher.DISPATCH_BREAK;
        } else {

            // normal dispatching
            // This part assumes the template is JSP.
//            final String templateURL = getTemplateURL(section, item, request,
//                                                      actx);
//            s_log.debug("TEMPLATE " + templateURL);
            DispatcherHelper.setRequestContext(request, actx);
            DispatcherHelper.forwardRequestByPath(null, request,
                                                  response);
            return ChainedDispatcher.DISPATCH_BREAK;
        }
    }

    public ContentItem getItem(ContentSection section, String url) {

        ItemResolver itemResolver = getItemResolver(section);
        ContentItem item;
        // Check if the user has access to view public or preview pages
        boolean hasPermission = true;
        HttpServletRequest request = DispatcherHelper.getRequest();

        // If the remaining URL starts with "preview/", then try and
        // preview this item.  Otherwise look for the live item.
        boolean preview = false;
        if (url.startsWith(PREVIEW)) {
            url = url.substring(PREVIEW.length());
            preview = true;
        }

        final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
        final PermissionChecker permissionChecker = cdiUtil.findBean(
            PermissionChecker.class);

        if (preview) {
            item = itemResolver.getItem(section, url, "draft");
            if (item != null) {
                hasPermission = permissionChecker.isPermitted(
                    ItemPrivileges.PREVIEW, item);
            }
        } else {
            item = itemResolver.getItem(section, url, "live");
            if (item != null) {
                hasPermission = permissionChecker.isPermitted(
                    ItemPrivileges.VIEW_PUBLISHED, item);
            }
        }

        if (item == null && url.endsWith(INDEX_FILE)) {

            // look up folder if it's an index
            url = url.substring(0, url.length() - INDEX_FILE.length());
            LOGGER.info("Attempting to match folder " + url);
            item = itemResolver.getItem(section, url, "live");
            if (item != null) {
                hasPermission = permissionChecker.isPermitted(
                    ItemPrivileges.VIEW_PUBLISHED, item);
            }
        }
        // chris.gilbert@westsussex.gov.uk -  if user is not logged in, give them a chance to do that, else show them the door
        if (!hasPermission && !cdiUtil.findBean(Shiro.class).getSubject()
            .isAuthenticated()) {
            throw new LoginSignal(request);
        }
        if (!hasPermission) {
            throw new com.arsdigita.dispatcher.AccessDeniedException();
        }

        return item;
    }

    /**
     * Fetches the ItemResolver for a content section.
     *
     * @param section The content section
     *
     * @return The ItemResolver associated with the content section
     */
    public ItemResolver getItemResolver(ContentSection section) {

        final CdiUtil cdiUtil = CdiUtil.createCdiUtil();
        final ContentSectionManager sectionManager = cdiUtil.findBean(
            ContentSectionManager.class);

        return sectionManager.getItemResolver(section);
    }

    /**
     * Fetches the ItemResolver for a content section. Checks cache first.
     *
     * @param section The content section
     *
     * @return The ItemResolver associated with the content section
     */
//    public TemplateResolver getTemplateResolver(ContentSection section) {
//
//        String name = section.getName();
//        TemplateResolver ir = (TemplateResolver) s_templateResolverCache.get(
//            name);
//
//        if (ir == null) {
//            ir = section.getTemplateResolver();
//            s_templateResolverCache.put(name, ir);
//        }
//
//        return ir;
//    }
    /**
     * Fetches the URL of a template for an item. The returned URL is relative
     * to the webapp context.
     */
//    public String getTemplateURL(ContentSection section,
//                                 ContentItem item,
//                                 HttpServletRequest request,
//                                 RequestContext actx) {
//
//        String templateURL = getTemplateResolver(section).getTemplate(section,
//                                                                      item,
//                                                                      request);
//
//        return templateURL;
//    }
}
