/**
 * This work was created by participants in the DataONE project, and is
 * jointly copyrighted by participating institutions in DataONE. For 
 * more information on DataONE, see our web site at http://dataone.org.
 *
 *   Copyright ${year}
 *
 * 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.
 * 
 * $Id$
 */

package org.dataone.service.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Pattern;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;

/**
 *
 * @author waltz
 */
public class DateTimeMarshaller {
    // http://www.w3.org/TR/xmlschema-2/#dateTime
    // The lexical representation of a timezone is a string of the form: (('+' | '-') hh ':' mm) | 'Z'
    static final Pattern timezonePattern = Pattern.compile(".+(?:(?:[\\+\\-]\\d\\d:\\d\\d)|Z)$");

    static final DateTimeFormatter basicDateIsoFmt = ISODateTimeFormat.basicDate();
    static final DateTimeFormatter basicDateTimeIsoFmt = ISODateTimeFormat.basicDateTime();
    static final DateTimeFormatter basicDateTimeNoMillisIsoFmt = ISODateTimeFormat.basicDateTimeNoMillis();
    static final DateTimeFormatter extendedDateIsoFmt = ISODateTimeFormat.date();
    static final DateTimeFormatter extendedDateTimeIsoFmt = ISODateTimeFormat.dateTime();
    static final DateTimeFormatter extendedDateTimeNoMillisIsoFmt = ISODateTimeFormat.dateTimeNoMillis();
    static final DateTimeFormatter fmt = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
//    static final DateTimeFormatter zFmt = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");

    /**
     * convert a date to GMT
     *
     * @param d
     * @return dateTime string that is ISO 8601 compliant
     */
    public static String serializeDateToUTC(Date d) {
        DateTime dt = new DateTime(d);
        DateTime dtUTC = dt.withZone(DateTimeZone.UTC);
        return fmt.print(dtUTC);

    }

    /**
     * convert a String to GMT date
     * The STring must either be ISO 8601 compliant or
     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3.1 compliant
     * @param d
     * @return date in UTC
     */
    public static Date deserializeDateToUTC(String dt) {
        // if the string can not be parsed, then a Null Pointer Exception will be thrown
        DateTime dateTime = null;


        if (Character.isDigit(dt.charAt(0))) {
            // Assume it is ISO 8601 compliant
            // either simple or extended date
            String tzAppend = "";
            if (!(timezonePattern.matcher(dt).matches())) {
                tzAppend = "Z";
            }
            if (Character.isDigit(dt.charAt(4))) {
               //basic
                if (dt.contains(".")) {
                    dateTime = basicDateTimeIsoFmt.parseDateTime(dt+tzAppend);
                } else {
                    dateTime = basicDateTimeNoMillisIsoFmt.parseDateTime(dt+tzAppend);
                }
            } else {
                // extended
                if (dt.contains(".")) {
                    dateTime = extendedDateTimeIsoFmt.parseDateTime(dt+tzAppend);

                } else {
                    dateTime = extendedDateTimeNoMillisIsoFmt.parseDateTime(dt+tzAppend);
                }
            }
        } else {
            if (Character.isSpaceChar(dt.charAt(3))) {
                // it better be a string that looks something like
                // Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
                SimpleDateFormat format = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy");
                try {
                    dateTime = new DateTime(format.parse(dt));
                } catch (ParseException ex) {
                    dateTime = null;
                }
            } else if (Character.isLetter(dt.charAt(3))) {
                // it better be a string that looks something like
                // Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
                SimpleDateFormat format = new SimpleDateFormat("EEEE, dd-MMM-yy HH:mm:ss zzz");
                try {
                    dateTime = new DateTime(format.parse(dt));
                } catch (ParseException ex) {
                    dateTime = null;
                }
            } else {
                // it better be a string that looks something like
                // Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
                SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
                try {
                    dateTime = new DateTime(format.parse(dt));
                } catch (ParseException ex) {
                    dateTime = null;
                }
            }
        }
        dateTime = dateTime.withZone(DateTimeZone.UTC);
        return dateTime.toDate();
    }
}