/*
 * ====================================================================
 * Copyright (c) 2004-2012 TMate Software Ltd.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://svnkit.com/license.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 * ====================================================================
 */
package org.tmatesoft.svn.core.internal.server.dav.handlers;

import java.io.IOException;
import java.util.Date;
import java.util.List;

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

import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.io.dav.DAVElement;
import org.tmatesoft.svn.core.internal.io.dav.http.HTTPHeader;
import org.tmatesoft.svn.core.internal.server.dav.DAVDepth;
import org.tmatesoft.svn.core.internal.server.dav.DAVException;
import org.tmatesoft.svn.core.internal.server.dav.DAVLock;
import org.tmatesoft.svn.core.internal.server.dav.DAVRepositoryManager;
import org.tmatesoft.svn.core.internal.server.dav.DAVResource;
import org.tmatesoft.svn.core.internal.server.dav.DAVResourceState;
import org.tmatesoft.svn.core.internal.server.dav.DAVXMLUtil;
import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
import org.tmatesoft.svn.core.internal.util.SVNXMLUtil;


/**
 * @version 1.3
 * @author  TMate Software Ltd.
 */
public class DAVLockHandler extends ServletDAVHandler {
    private DAVLockRequest myLockRequest;
    
    protected DAVLockHandler(DAVRepositoryManager repositoryManager, HttpServletRequest request, HttpServletResponse response) {
        super(repositoryManager, request, response);
    }

    public void execute() throws SVNException {
        long readLength = readInput(false);
        
        DAVDepth depth = null;
        try {
            depth = getRequestDepth(DAVDepth.DEPTH_INFINITY);
        } catch (SVNException svne) {
            throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_BAD_REQUEST, null, null);
        }

        if (depth != DAVDepth.DEPTH_ZERO && depth != DAVDepth.DEPTH_INFINITY) {
            sendError(HttpServletResponse.SC_BAD_REQUEST, "Depth must be 0 or \"infinity\" for LOCK.");
            return;
        }
        
        DAVLockInfoProvider lockProvider = null;


        DAVResource resource = getRequestedDAVResource(false, false);
        
        try {
            lockProvider = DAVLockInfoProvider.createLockInfoProvider(this, false);
        } catch (SVNException svne) {
            throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR, null, null);
        }
            
        boolean isNewLockRequest = false;
        DAVLock lock = null;

        if (readLength > 0) {
            lock = getLockRequest().parseLockInfo(this, resource, getNamespaces());
            isNewLockRequest = true;
        }
            
        DAVResourceState resourceState = getResourceState(resource);
        try {
            int flags = resourceState == DAVResourceState.NULL ? DAV_VALIDATE_PARENT : DAV_VALIDATE_RESOURCE;
            flags |= DAV_VALIDATE_ADD_LD;
            validateRequest(resource, depth, flags, isNewLockRequest ? lock.getScope() : null, null, lockProvider);
        } catch (DAVException dave) {
            DAVException next = new DAVException("Could not LOCK {0} due to a failed precondition (e.g. other locks).", 
                    new Object[] { SVNEncodingUtil.xmlEncodeCDATA(resource.getResourceURI().getRequestURI(), true) }, dave.getResponseCode(), dave, 0);
            throw next;
        }
            
        if (!isNewLockRequest) {
            List lockTokens = null;
            try {
                lockTokens = getLockTokensList();
            } catch (DAVException dave) {
                DAVException next = new DAVException("The lock refresh for {0} failed because no lock tokens were specified in an \"If:\" header.", 
                        new Object[] { SVNEncodingUtil.xmlEncodeCDATA(resource.getResourceURI().getRequestURI(), true) }, dave.getResponseCode(), dave, 0);
                throw next;
            }
                
            String lockToken = (String) lockTokens.get(0);
            lock = lockProvider.refreshLock(resource, lockToken, getTimeout());
        } else {
            if (lock.getTimeOutDate() != null) {
                //TODO: add expiration date renewal
                //Date timeoutDate = lock.getTimeOutDate();
            }
            
            lockProvider.addLock(lock, resource);
            setResponseHeader(HTTPHeader.LOCK_TOKEN_HEADER, "<" + lock.getLockToken() + ">");
        }
        
        HttpServletResponse servletResponse = getHttpServletResponse(); 
        servletResponse.setContentType(DEFAULT_XML_CONTENT_TYPE);
        servletResponse.setStatus(HttpServletResponse.SC_OK);
        
        try {
            StringBuffer xmlBuffer = SVNXMLUtil.addXMLHeader(null);
            DAVXMLUtil.openNamespaceDeclarationTag(SVNXMLUtil.DAV_NAMESPACE_PREFIX, DAVElement.PROP.getName(), null, null, xmlBuffer, true, false);
            if (lock == null) {
                SVNXMLUtil.openXMLTag(SVNXMLUtil.DAV_NAMESPACE_PREFIX, DAVElement.LOCK_DISCOVERY.getName(), SVNXMLUtil.XML_STYLE_SELF_CLOSING, null, xmlBuffer);
            } else {
                SVNXMLUtil.openXMLTag(SVNXMLUtil.DAV_NAMESPACE_PREFIX, DAVElement.LOCK_DISCOVERY.getName(), SVNXMLUtil.XML_STYLE_PROTECT_CDATA, null, xmlBuffer);
                xmlBuffer.append(DAVLockInfoProvider.getActiveLockXML(lock));
                xmlBuffer.append('\n');
                SVNXMLUtil.closeXMLTag(SVNXMLUtil.DAV_NAMESPACE_PREFIX, DAVElement.LOCK_DISCOVERY.getName(), xmlBuffer);
            }
            SVNXMLUtil.closeXMLTag(SVNXMLUtil.DAV_NAMESPACE_PREFIX, DAVElement.PROP.getName(), xmlBuffer);
            getResponseWriter().write(xmlBuffer.toString());
        } catch (IOException e) {
            throw new DAVException(e.getMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 0);
        }
    }

    protected DAVRequest getDAVRequest() {
        return getLockRequest();
    }
    
    private DAVLockRequest getLockRequest() {
        if (myLockRequest == null) {
            myLockRequest = new DAVLockRequest();
        }
        return myLockRequest;
    }

}
