/*
 * ====================================================================
 * 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.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

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

import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.internal.io.dav.DAVElement;
import org.tmatesoft.svn.core.internal.io.fs.FSLocationsFinder;
import org.tmatesoft.svn.core.internal.server.dav.DAVException;
import org.tmatesoft.svn.core.internal.server.dav.DAVPathUtil;
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.DAVXMLUtil;
import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.util.SVNXMLUtil;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.io.ISVNLocationSegmentHandler;
import org.tmatesoft.svn.core.io.SVNLocationSegment;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.util.SVNLogType;


/**
 * @version 1.3
 * @author  TMate Software Ltd.
 */
public class DAVGetLocationSegmentsHandler extends DAVReportHandler implements ISVNLocationSegmentHandler {

    private static final String GET_LOCATION_SEGMENTS_REPORT = "get-location-segments-report";
    private static final String LOCATION_SEGMENT_TAG = "location-segment";
    private static final String RANGE_START_ATTR = "range-start";
    private static final String RANGE_END_ATTR = "range-end";
    
    private DAVGetLocationSegmentsRequest myDAVRequest;
    private boolean myIsOpenerSent;
    private DAVReportHandler myCommonReportHandler;
    
    protected DAVGetLocationSegmentsHandler(DAVRepositoryManager repositoryManager, HttpServletRequest request, HttpServletResponse response, 
            DAVReportHandler commonReportHandler) {
        super(repositoryManager, request, response);
        myCommonReportHandler = commonReportHandler;
    }

    public void execute() throws SVNException {
        myCommonReportHandler.checkSVNNamespace(null);

        DAVResource resource = getRequestedDAVResource(false, false); 
        setDAVResource(resource);
        
        DAVGetLocationSegmentsRequest request = getLocationSegmentsRequest();
        
        String path = null; 
        long startRev = -1;
        long endRev = -1;
        long pegRev = -1;

        DAVElementProperty rootElement = request.getRootElement();
        List children = rootElement.getChildren();
        for (Iterator iterator = children.iterator(); iterator.hasNext();) {
            DAVElementProperty childElement = (DAVElementProperty) iterator.next();
            DAVElement childElementName = childElement.getName();
            if (!DAVElement.SVN_NAMESPACE.equals(childElementName.getNamespace())) {
                continue;
            }
            
            if (childElementName == DAVElement.PATH) {
                path = childElement.getFirstValue(false);
                DAVPathUtil.testCanonical(path);
                String resourcePath = resource.getResourceURI().getPath();
                path = SVNPathUtil.append(resourcePath, path);
            } else if (childElementName == DAVElement.START_REVISION) {
                try {
                    startRev = Long.parseLong(childElement.getFirstValue(true));
                } catch (NumberFormatException nfe) {
                    SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED, nfe), SVNLogType.NETWORK);
                }
            } else if (childElementName == DAVElement.END_REVISION) {
                try {
                    endRev = Long.parseLong(childElement.getFirstValue(true));
                } catch (NumberFormatException nfe) {
                    SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED, nfe), SVNLogType.NETWORK);
                }
            } else if (childElementName == DAVElement.PEG_REVISION) {
                try {
                    pegRev = Long.parseLong(childElement.getFirstValue(true));
                } catch (NumberFormatException nfe) {
                    SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.RA_DAV_REQUEST_FAILED, nfe), SVNLogType.NETWORK);
                }
            }
        }
    
        if (path == null) {
            throw new DAVException("Not all parameters passed.", HttpServletResponse.SC_BAD_REQUEST, SVNLogType.NETWORK, DAVXMLUtil.SVN_DAV_ERROR_TAG, 
                    DAVElement.SVN_DAV_ERROR_NAMESPACE);
        }
        
        if (SVNRevision.isValidRevisionNumber(startRev) && SVNRevision.isValidRevisionNumber(endRev) && 
                endRev > startRev) {
            throw new DAVException("End revision must not be younger than start revision", HttpServletResponse.SC_BAD_REQUEST, SVNLogType.NETWORK, 
                    DAVXMLUtil.SVN_DAV_ERROR_TAG, DAVElement.SVN_DAV_ERROR_NAMESPACE);
        }
        
        if (SVNRevision.isValidRevisionNumber(pegRev) && SVNRevision.isValidRevisionNumber(startRev) && 
                startRev > pegRev) {
            throw new DAVException("Start revision must not be younger than peg revision", HttpServletResponse.SC_BAD_REQUEST, SVNLogType.NETWORK, 
                    DAVXMLUtil.SVN_DAV_ERROR_TAG, DAVElement.SVN_DAV_ERROR_NAMESPACE);
        }
        
        FSLocationsFinder locationsFinder = new FSLocationsFinder(getDAVResource().getFSFS());
        locationsFinder.getNodeLocationSegments(path, pegRev, startRev, endRev, this);
        try {
            maybeSendOpener(); 
        } catch (SVNException svne) {
            throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 
                    "Error beginning REPORT reponse", null);
        }
        try {
            writeXMLFooter(GET_LOCATION_SEGMENTS_REPORT);
        } catch (SVNException svne) {
            throw DAVException.convertError(svne.getErrorMessage(), HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 
                    "Error ending REPORT reponse", null);
        }
    }

    public void handleLocationSegment(SVNLocationSegment locationSegment) throws SVNException {
        maybeSendOpener();
        Map attrs = new HashMap();

        String path = locationSegment.getPath();
        if (path != null) {
            String quotedPath = SVNEncodingUtil.xmlEncodeCDATA(path, true);
            attrs.put(PATH_ATTR, quotedPath);
            attrs.put(RANGE_START_ATTR, String.valueOf(locationSegment.getStartRevision()));
            attrs.put(RANGE_END_ATTR, String.valueOf(locationSegment.getEndRevision()));
        } else {
            attrs.put(RANGE_START_ATTR, String.valueOf(locationSegment.getStartRevision()));
            attrs.put(RANGE_END_ATTR, String.valueOf(locationSegment.getEndRevision()));
        }
        StringBuffer buffer = SVNXMLUtil.openXMLTag(SVNXMLUtil.SVN_NAMESPACE_PREFIX, LOCATION_SEGMENT_TAG, SVNXMLUtil.XML_STYLE_SELF_CLOSING, attrs, null);
        write(buffer);
    }

    protected DAVRequest getDAVRequest() {
        return getLocationSegmentsRequest();
    }

    private DAVGetLocationSegmentsRequest getLocationSegmentsRequest() {
        if (myDAVRequest == null) {
            myDAVRequest = new DAVGetLocationSegmentsRequest();
        }
        return myDAVRequest;
    }

    private void maybeSendOpener() throws SVNException {
        if (!myIsOpenerSent) {
            writeXMLHeader(GET_LOCATION_SEGMENTS_REPORT);
            myIsOpenerSent = true;
        }
    }
}
