/*
* Licensed Materials - Property of IBM Corp.
* IBM UrbanCode Build
* (c) Copyright IBM Corporation 2012, 2014. All Rights Reserved.
*
* U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by
* GSA ADP Schedule Contract with IBM Corp.
*/
package com.urbancode.air.plugin.scm.changelog

import java.text.SimpleDateFormat;
import java.text.DateFormat;
import java.util.TimeZone;

public class ChangeSetXMLHelper {

    //**************************************************************************
    // CLASS
    //**************************************************************************

    static private final TimeZone GMT = TimeZone.getTimeZone("GMT")
    static private final String AIR_DATE_FORMAT_STRING = "yyyy-MM-dd HH:mm:ss.S Z";

    //**************************************************************************
    // INSTANCE
    //**************************************************************************

    String repoType;
    
    public def toXML(Map<String, Map<String, ChangeSet>> componentToChangeSetsMap) {
        final DateFormat airDateFormat = getAirDateFormat();
        assert repoType : "An air repository type must be specified";

        def xmlBuilder = new groovy.xml.StreamingMarkupBuilder();
        xmlBuilder.encoding = "UTF-8"
        def xml = xmlBuilder.bind{
            mkp.xmlDeclaration()
            'change-log'{
                componentToChangeSetsMap.each { component, changeSets ->
                    changeSets.each { changeSetId, changeSet ->
                        'change-set'() {
                            'id'(stripInvalidXMLChars(changeSetId))
                            'user'(stripInvalidXMLChars(changeSet.user))
                            'date'(airDateFormat.format(changeSet.date))
                            if (changeSet.message) {
                                'comment'(stripInvalidXMLChars(changeSet.message))
                            }
                            //module('')
                            'repository-type'(stripInvalidXMLChars(repoType))
                            'repository-id'(System.getenv("REPOSITORY_ID"))
                            'source-config-id'(System.getenv("SOURCE_CONFIG_ID"))
                            'file-set' {
                                changeSet.fileSet.each { change ->
                                    'file'(
                                            'change-type': stripInvalidXMLChars(change.type),
                                            'revision-number': stripInvalidXMLChars(change.revision),
                                            stripInvalidXMLChars(change.path))
                                }
                            }

                            if (changeSet.properties) {
                                'properties' {
                                    changeSet.properties.each { prop ->
                                        if (prop.value) {
                                            'property' {
                                                'name'(stripInvalidXMLChars(prop.key))
                                                'value'(stripInvalidXMLChars(prop.value))
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        return xml.toString()
    }
    
    /**
    * Strip any invalid xml characters (such as backspace) from the value.
    * @param value the value to strip (can be null)
    * @return the sanitized value (null if and only if value was null)
    */
   protected String stripInvalidXMLChars(String value) {

       // legal xml chars: #x9 | #xA | #xD | #x20-#xD7FF | #xE000-#xFFFD | #x10000-#x10FFFF
       def legalChars = ['\\u0009', '\\u000A', '\\u000D', '\\u0020-\\uD7FF', '\\uE000-\\uFFFD', '\\u10000-\\u10FFFF']
       def illegalChars = "[^${legalChars.join()}]"
       return !value ? value : value.replaceAll(illegalChars, '?')
   }

    /**
     * Create a new AirDate format instance based upon {@link #AIR_DATE_FORMAT_STRING}
     * @return a new {@link DateFormat}
     */
    protected DateFormat getAirDateFormat() {
        DateFormat airDateFormat = new SimpleDateFormat(AIR_DATE_FORMAT_STRING);
        airDateFormat.timeZone = GMT;
        return airDateFormat;
    }
}
