/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.app.dav;

import java.io.IOException;
import java.sql.SQLException;

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

import org.dspace.authorize.AuthorizeException;
import org.dspace.authorize.AuthorizeManager;
import org.dspace.content.Bitstream;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.handle.HandleManager;
import org.jdom.Element;


/**
 * Superclass for resources representing DSpace Objects like Item, Collection
 * etc. Defines the behavior of DSpace "resources" in the WebDAV interface; maps
 * DAV operations onto DSpace object.
 */
abstract class DAVDSpaceObject extends DAVResource
{
    
    /** Object of this resource, set by subclass' initializer. */
    protected DSpaceObject dso = null;

    /** Prototype of DAV Property "handle". */
    protected static final Element handleProperty = new Element("handle",
            DAV.NS_DSPACE);

    /** Special character used to separate handle prefix from suffix in DAV resource URIs - substitute this for the '/' normally used in Handle syntax since the '/' causes all sorts of problems for broken DAV clients. */
    private static final char handleSeparator = '$';

    /**
     * Instantiates a new DAVD space object.
     * 
     * @param context the context
     * @param request the request
     * @param response the response
     * @param pathElt the path elt
     * @param dso the dso
     */
    protected DAVDSpaceObject(Context context, HttpServletRequest request,
            HttpServletResponse response, String pathElt[], DSpaceObject dso)
    {
        super(context, request, response, pathElt);
        this.dso = dso;
    }

    /**
     * Make URI path element for a DSpaceObject.
     * 
     * @param dso the DSpaceObject, which needs to have a valid Handle.
     * 
     * @return path element string or null if no handle.
     */
    protected static String getPathElt(DSpaceObject dso)
    {
        String handle = dso.getHandle();
        if (handle == null)
        {
            return null;
        }
        return getPathElt(handle);
    }

    /**
     * Make URI path element for a DSpaceObject.
     * 
     * @param handle handle of a DSpaceObject.
     * 
     * @return path element string or null if no handle.
     */
    protected static String getPathElt(String handle)
    {
        int hs;
        if (handleSeparator != '/')
        {
            handle = handle.replaceFirst("/", String.valueOf(handleSeparator));
        }
        return "dso_" + encodeHandle(handle);
    }

    /**
     * Match the URIs this subclass understands and return the corresponding
     * resource. Since the "dso_" format can lead to several different resource
     * types, handle it here.
     * 
     * @param context the context
     * @param request the request
     * @param response the response
     * @param pathElt the path elt
     * 
     * @return the DAV resource
     * 
     * @throws DAVStatusException the DAV status exception
     * @throws SQLException the SQL exception
     * @throws AuthorizeException the authorize exception
     */
    protected static DAVResource matchResourceURI(Context context,
            HttpServletRequest request, HttpServletResponse response,
            String pathElt[]) throws DAVStatusException, SQLException,
            AuthorizeException
    {
        // Match /dso_<handle>{...} .. look for last "dso_" element
        if (pathElt[0].startsWith("dso_"))
        {
            int i = 1;
            for (; i < pathElt.length && pathElt[i].startsWith("dso_"); ++i)
            {
                // empty
            }
            --i;
            String handle = decodeHandle(pathElt[i].substring(4));

            // Replace substituted handle separator char with '/' to
            // get back a normal handle: (inverse of getPathElt() above)
            int sepIndex = handle.indexOf(handleSeparator);
            if (sepIndex >= 0)
            {
                char hc[] = handle.toCharArray();
                hc[sepIndex] = '/';
                handle = String.copyValueOf(hc);
            }

            DSpaceObject dso = HandleManager.resolveToObject(context, handle);
            if (dso == null)
            {
                throw new DAVStatusException(HttpServletResponse.SC_NOT_FOUND,
                        "Cannot resolve handle \"" + handle + "\"");
            }
            else if (dso.getType() == Constants.ITEM)
            {
                if (i + 1 < pathElt.length)
                {
                    if (pathElt[i + 1].startsWith("bitstream_"))
                    {
                        Bitstream bs = DAVBitstream.findBitstream(context,
                                (Item) dso, pathElt[i + 1]);
                        if (bs == null)
                        {
                            throw new DAVStatusException(
                                    HttpServletResponse.SC_NOT_FOUND,
                                    "Bitstream \"" + pathElt[i + 1]
                                            + "\" not found in item: "
                                            + pathElt[i]);
                        }
                        return new DAVBitstream(context, request, response,
                                pathElt, (Item) dso, bs);
                    }
                    else
                    {
                        throw new DAVStatusException(
                                HttpServletResponse.SC_NOT_FOUND,
                                "Illegal resource path, \""
                                        + pathElt[i + 1]
                                        + "\" is not a Bitstream identifier for item: "
                                        + pathElt[i]);
                    }
                }
                else
                {
                    return new DAVItem(context, request, response, pathElt,
                            (Item) dso);
                }
            }
            else if (dso.getType() == Constants.COLLECTION)
            {
                return new DAVCollection(context, request, response, pathElt,
                        (Collection) dso);
            }
            else if (dso.getType() == Constants.COMMUNITY)
            {
                return new DAVCommunity(context, request, response, pathElt,
                        (Community) dso);
            }
            else
            {
                throw new DAVStatusException(
                        HttpServletResponse.SC_BAD_REQUEST,
                        "Unrecognized DSpace object type for handle=" + handle);
            }
        }
        return null;
    }

    /**
     * Interposed between subclass and common props, take care of shared props
     * like privileges, handle, dspace:type.
     * 
     * @param property the property
     * 
     * @return the element
     * 
     * @throws SQLException the SQL exception
     * @throws AuthorizeException the authorize exception
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws DAVStatusException the DAV status exception
     */
    @Override
    protected Element propfindInternal(Element property) throws SQLException,
            AuthorizeException, IOException, DAVStatusException
    {
        String value = null;

        if (elementsEqualIsh(property, handleProperty))
        {
            value = canonicalizeHandle(this.dso.getHandle());
        }
        else if (elementsEqualIsh(property, current_user_privilege_setProperty))
        {
            Element c = (Element) current_user_privilege_setProperty.clone();

            // if we're an admin we have all privs everywhere.
            if (AuthorizeManager.isAdmin(this.context))
            {
                addPrivilege(c, new Element("all", DAV.NS_DAV));
            }
            else
            {
                for (int i = 0; i < Constants.actionText.length; ++i)
                {
                    if (AuthorizeManager
                            .authorizeActionBoolean(this.context, this.dso, i))
                    {
                        Element priv = actionToPrivilege(i);
                        if (priv != null)
                        {
                            addPrivilege(c, priv);
                        }
                    }
                }
            }
            return c;
        }
        else
        {
            return commonPropfindInternal(property,
                    this.dso.getType() != Constants.BITSTREAM);
        }

        // value was set up by "if" clause:
        if (value == null)
        {
            throw new DAVStatusException(HttpServletResponse.SC_NOT_FOUND,
                    "Not found.");
        }
        Element p = new Element(property.getName(), property.getNamespace());
        p.setText(filterForXML(value));
        return p;
    }

    /**
     * Return value of DSpace type property. It is conveniently the same as our
     * internal type name.
     * 
     * @return the element
     */
    @Override
    protected Element typeValue()
    {
        return new Element(Constants.typeText[this.dso.getType()].toLowerCase(),
                DAV.NS_DSPACE);
    }

}
