/*************************GO-LICENSE-START*********************************
 * Copyright 2014 ThoughtWorks, Inc.
 *
 * Licensed 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.
 *************************GO-LICENSE-END***********************************/

package com.thoughtworks.go.util;

import java.io.StringReader;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;

import com.thoughtworks.go.domain.materials.Modification;
import com.thoughtworks.go.domain.materials.ModifiedAction;

import static com.thoughtworks.go.util.ExceptionUtils.bomb;

import com.thoughtworks.go.domain.materials.svn.SvnCommand;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

public class SvnLogXmlParser {

    public List<Modification> parse(String svnLogOutput, String path, SAXBuilder builder) {
        try {
            Document document = builder.build(new StringReader(svnLogOutput));
            return parseDOMTree(document, path);
        } catch (Exception e) {
            throw bomb("Unable to parse svn log output: " + svnLogOutput, e);
        }
    }

    private List<Modification> parseDOMTree(Document document, String path) throws ParseException {
        List<Modification> modifications = new ArrayList<Modification>();

        Element rootElement = document.getRootElement();
        List logEntries = rootElement.getChildren("logentry");
        for (Iterator iterator = logEntries.iterator(); iterator.hasNext();) {
            Element logEntry = (Element) iterator.next();

            modifications.add(parseLogEntry(logEntry, path));
        }

        return modifications;
    }

    private Modification parseLogEntry(Element logEntry, String path) throws ParseException {
        Date modifiedTime = convertDate(logEntry.getChildText("date"));
        String author = logEntry.getChildText("author");
        String comment = logEntry.getChildText("msg");
        String revision = logEntry.getAttributeValue("revision");

        Modification modification = new Modification(author, comment, null, modifiedTime, revision);

        Element logEntryPaths = logEntry.getChild("paths");
        if (logEntryPaths != null) {
            List paths = logEntryPaths.getChildren("path");
            for (Iterator iterator = paths.iterator(); iterator.hasNext();) {
                Element node = (Element) iterator.next();
                if (underPath(path, node.getText())) {
                    ModifiedAction action = convertAction(node.getAttributeValue("action"));
                    modification.createModifiedFile(node.getText(), null, action);
                }
            }
        }

        return modification;
    }

    private boolean underPath(String path, String text) {
        return text.startsWith(path);
    }

    /**
     * Converts the specified SVN date string into a Date.
     *
     * @param date with format "yyyy-MM-dd'T'HH:mm:ss.SSS" + "...Z"
     * @return converted date
     * @throws java.text.ParseException if specified date doesn't match the expected format
     */
    static Date convertDate(String date) throws ParseException {
        final int zIndex = date.indexOf('Z');
        if (zIndex - 3 < 0) {
            throw new ParseException(date
                    + " doesn't match the expected subversion date format", date.length());
        }
        String withoutMicroSeconds = date.substring(0, zIndex - 3);

        return getOutDateFormatter().parse(withoutMicroSeconds);
    }

    public static DateFormat getOutDateFormatter() {
        DateFormat f = new SimpleDateFormat(SvnCommand.SVN_DATE_FORMAT_OUT);
        f.setTimeZone(TimeZone.getTimeZone("GMT"));
        return f;
    }

    private ModifiedAction convertAction(String action) {
        if (action.equals("A")) {
            return ModifiedAction.added;
        }
        if (action.equals("M")) {
            return ModifiedAction.modified;
        }
        if (action.equals("D")) {
            return ModifiedAction.deleted;
        }
        return ModifiedAction.unknown;
    }

    public HashMap<String, String> parseInfoToGetUUID(String output, String queryURL, SAXBuilder builder) {
        HashMap<String, String> uidToUrlMap = new HashMap<String, String>();
        try {
            Document document = builder.build(new StringReader(output));
            Element root = document.getRootElement();
            List<Element> entries = root.getChildren("entry");
            for (Element entry : entries) {
                uidToUrlMap.put(queryURL, entry.getChild("repository").getChild("uuid").getValue());
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return uidToUrlMap;
    }


}

@@ -2237,14 +2237,11 @@
 tion
-s.add(
+ = 
 pars
@@ -2265,18 +2265,120 @@
 y, path)
-)
 ;
+%0A            if (modification != null) %7B%0A                modifications.add(modification);%0A            %7D
 %0A       
@@ -2504,32 +2504,288 @@
 arseException %7B%0A
+        Element logEntryPaths = logEntry.getChild(%22paths%22);%0A        if (logEntryPaths == null) %7B%0A            /* Path-based access control forbids us from learning%0A             * details of this log entry, so skip it. */%0A            return null;%0A        %7D%0A%0A
         Date mod
@@ -3120,25 +3120,24 @@
 ision);%0A
-%0A
         
 Element 
@@ -3132,101 +3132,9 @@
     
-Element logEntryPaths = logEntry.getChild(%22paths%22);%0A        if (logEntryPaths != null) %7B%0A    
+%0A
     
@@ -3181,28 +3181,24 @@
 en(%22path%22);%0A
-    
         for 
@@ -3263,36 +3263,32 @@
 ) %7B%0A            
-  
-  
 Element node = (
@@ -3321,28 +3321,24 @@
             
-    
 if (underPat
@@ -3376,28 +3376,24 @@
             
-  
-  
 ModifiedActi
@@ -3461,36 +3461,32 @@
                 
-    
 modification.cre
@@ -3536,26 +3536,8 @@
 n);%0A
-                %7D%0A
     

fix #993 by skipping empty svn log entries