package org.jxmpp.util;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class XmppDateTime {
   private static final XmppDateTime.DateFormatType dateFormatter;
   private static final Pattern datePattern;
   private static final XmppDateTime.DateFormatType timeFormatter;
   private static final Pattern timePattern;
   private static final XmppDateTime.DateFormatType timeNoZoneFormatter;
   private static final Pattern timeNoZonePattern;
   private static final XmppDateTime.DateFormatType timeNoMillisFormatter;
   private static final Pattern timeNoMillisPattern;
   private static final XmppDateTime.DateFormatType timeNoMillisNoZoneFormatter;
   private static final Pattern timeNoMillisNoZonePattern;
   private static final XmppDateTime.DateFormatType dateTimeFormatter;
   private static final Pattern dateTimePattern;
   private static final XmppDateTime.DateFormatType dateTimeNoMillisFormatter;
   private static final Pattern dateTimeNoMillisPattern;
   private static final DateFormat xep0091Formatter;
   private static final DateFormat xep0091Date6DigitFormatter;
   private static final DateFormat xep0091Date7Digit1MonthFormatter;
   private static final DateFormat xep0091Date7Digit2MonthFormatter;
   private static final Pattern xep0091Pattern;
   private static final List<XmppDateTime.PatternCouplings> couplings;
   private static final Pattern SECOND_FRACTION;

   public static Date parseXEP0082Date(String dateString) throws ParseException {
      Iterator i$ = couplings.iterator();

      XmppDateTime.PatternCouplings coupling;
      Matcher matcher;
      do {
         if (!i$.hasNext()) {
            synchronized(dateTimeNoMillisFormatter) {
               return dateTimeNoMillisFormatter.parse(dateString);
            }
         }

         coupling = (XmppDateTime.PatternCouplings)i$.next();
         matcher = coupling.pattern.matcher(dateString);
      } while(!matcher.matches());

      return coupling.formatter.parse(dateString);
   }

   public static Date parseDate(String dateString) throws ParseException {
      Matcher matcher = xep0091Pattern.matcher(dateString);
      if (matcher.matches()) {
         int length = dateString.split("T")[0].length();
         if (length >= 8) {
            synchronized(xep0091Formatter) {
               return xep0091Formatter.parse(dateString);
            }
         }

         Date date = handleDateWithMissingLeadingZeros(dateString, length);
         if (date != null) {
            return date;
         }
      }

      return parseXEP0082Date(dateString);
   }

   public static String formatXEP0082Date(Date date) {
      synchronized(dateTimeFormatter) {
         return dateTimeFormatter.format(date);
      }
   }

   public static String convertXep82TimezoneToRfc822(String dateString) {
      return dateString.charAt(dateString.length() - 1) == 'Z' ? dateString.replace("Z", "+0000") : dateString.replaceAll("([\\+\\-]\\d\\d):(\\d\\d)", "$1$2");
   }

   public static String convertRfc822TimezoneToXep82(String dateString) {
      int length = dateString.length();
      String res = dateString.substring(0, length - 2);
      res = res + ':';
      res = res + dateString.substring(length - 2, length);
      return res;
   }

   public static String asString(TimeZone timeZone) {
      int rawOffset = timeZone.getRawOffset();
      int hours = rawOffset / 3600000;
      int minutes = Math.abs(rawOffset / '\uea60' - hours * 60);
      return String.format("%+d:%02d", hours, minutes);
   }

   private static Date handleDateWithMissingLeadingZeros(String stampString, int dateLength) throws ParseException {
      if (dateLength == 6) {
         synchronized(xep0091Date6DigitFormatter) {
            return xep0091Date6DigitFormatter.parse(stampString);
         }
      } else {
         Calendar now = Calendar.getInstance();
         Calendar oneDigitMonth = parseXEP91Date(stampString, xep0091Date7Digit1MonthFormatter);
         Calendar twoDigitMonth = parseXEP91Date(stampString, xep0091Date7Digit2MonthFormatter);
         List<Calendar> dates = filterDatesBefore(now, oneDigitMonth, twoDigitMonth);
         return !dates.isEmpty() ? determineNearestDate(now, dates).getTime() : null;
      }
   }

   private static Calendar parseXEP91Date(String stampString, DateFormat dateFormat) {
      try {
         synchronized(dateFormat) {
            dateFormat.parse(stampString);
            return dateFormat.getCalendar();
         }
      } catch (ParseException var5) {
         return null;
      }
   }

   private static List<Calendar> filterDatesBefore(Calendar now, Calendar... dates) {
      List<Calendar> result = new ArrayList();
      Calendar[] arr$ = dates;
      int len$ = dates.length;

      for(int i$ = 0; i$ < len$; ++i$) {
         Calendar calendar = arr$[i$];
         if (calendar != null && calendar.before(now)) {
            result.add(calendar);
         }
      }

      return result;
   }

   private static String handleMilliseconds(String dateString) {
      Matcher matcher = SECOND_FRACTION.matcher(dateString);
      if (!matcher.matches()) {
         return dateString;
      } else {
         int fractionalSecondsDigitCount = matcher.group(1).length();
         if (fractionalSecondsDigitCount == 3) {
            return dateString;
         } else {
            int posDecimal = dateString.indexOf(".");
            StringBuilder sb = new StringBuilder(dateString.length() - fractionalSecondsDigitCount + 3);
            if (fractionalSecondsDigitCount > 3) {
               sb.append(dateString.substring(0, posDecimal + 4));
            } else {
               sb.append(dateString.substring(0, posDecimal + fractionalSecondsDigitCount + 1));

               for(int i = fractionalSecondsDigitCount; i < 3; ++i) {
                  sb.append('0');
               }
            }

            sb.append(dateString.substring(posDecimal + fractionalSecondsDigitCount + 1));
            return sb.toString();
         }
      }
   }

   private static Calendar determineNearestDate(final Calendar now, List<Calendar> dates) {
      Collections.sort(dates, new Comparator<Calendar>() {
         public int compare(Calendar o1, Calendar o2) {
            Long diff1 = new Long(now.getTimeInMillis() - o1.getTimeInMillis());
            Long diff2 = new Long(now.getTimeInMillis() - o2.getTimeInMillis());
            return diff1.compareTo(diff2);
         }
      });
      return (Calendar)dates.get(0);
   }

   static {
      dateFormatter = XmppDateTime.DateFormatType.XEP_0082_DATE_PROFILE;
      datePattern = Pattern.compile("^\\d+-\\d+-\\d+$");
      timeFormatter = XmppDateTime.DateFormatType.XEP_0082_TIME_MILLIS_ZONE_PROFILE;
      timePattern = Pattern.compile("^(\\d+:){2}\\d+.\\d+(Z|([+-](\\d+:\\d+)))$");
      timeNoZoneFormatter = XmppDateTime.DateFormatType.XEP_0082_TIME_MILLIS_PROFILE;
      timeNoZonePattern = Pattern.compile("^(\\d+:){2}\\d+.\\d+$");
      timeNoMillisFormatter = XmppDateTime.DateFormatType.XEP_0082_TIME_ZONE_PROFILE;
      timeNoMillisPattern = Pattern.compile("^(\\d+:){2}\\d+(Z|([+-](\\d+:\\d+)))$");
      timeNoMillisNoZoneFormatter = XmppDateTime.DateFormatType.XEP_0082_TIME_PROFILE;
      timeNoMillisNoZonePattern = Pattern.compile("^(\\d+:){2}\\d+$");
      dateTimeFormatter = XmppDateTime.DateFormatType.XEP_0082_DATETIME_MILLIS_PROFILE;
      dateTimePattern = Pattern.compile("^\\d+(-\\d+){2}+T(\\d+:){2}\\d+.\\d+(Z|([+-](\\d+:\\d+)))?$");
      dateTimeNoMillisFormatter = XmppDateTime.DateFormatType.XEP_0082_DATETIME_PROFILE;
      dateTimeNoMillisPattern = Pattern.compile("^\\d+(-\\d+){2}+T(\\d+:){2}\\d+(Z|([+-](\\d+:\\d+)))?$");
      xep0091Formatter = new SimpleDateFormat("yyyyMMdd'T'HH:mm:ss");
      xep0091Date6DigitFormatter = new SimpleDateFormat("yyyyMd'T'HH:mm:ss");
      xep0091Date7Digit1MonthFormatter = new SimpleDateFormat("yyyyMdd'T'HH:mm:ss");
      xep0091Date7Digit2MonthFormatter = new SimpleDateFormat("yyyyMMd'T'HH:mm:ss");
      xep0091Pattern = Pattern.compile("^\\d+T\\d+:\\d+:\\d+$");
      couplings = new ArrayList();
      TimeZone utc = TimeZone.getTimeZone("UTC");
      xep0091Formatter.setTimeZone(utc);
      xep0091Date6DigitFormatter.setTimeZone(utc);
      xep0091Date7Digit1MonthFormatter.setTimeZone(utc);
      xep0091Date7Digit1MonthFormatter.setLenient(false);
      xep0091Date7Digit2MonthFormatter.setTimeZone(utc);
      xep0091Date7Digit2MonthFormatter.setLenient(false);
      couplings.add(new XmppDateTime.PatternCouplings(datePattern, dateFormatter));
      couplings.add(new XmppDateTime.PatternCouplings(dateTimePattern, dateTimeFormatter));
      couplings.add(new XmppDateTime.PatternCouplings(dateTimeNoMillisPattern, dateTimeNoMillisFormatter));
      couplings.add(new XmppDateTime.PatternCouplings(timePattern, timeFormatter));
      couplings.add(new XmppDateTime.PatternCouplings(timeNoZonePattern, timeNoZoneFormatter));
      couplings.add(new XmppDateTime.PatternCouplings(timeNoMillisPattern, timeNoMillisFormatter));
      couplings.add(new XmppDateTime.PatternCouplings(timeNoMillisNoZonePattern, timeNoMillisNoZoneFormatter));
      SECOND_FRACTION = Pattern.compile(".*\\.(\\d{1,})(Z|((\\+|-)\\d{4}))");
   }

   private static class PatternCouplings {
      final Pattern pattern;
      final XmppDateTime.DateFormatType formatter;

      public PatternCouplings(Pattern datePattern, XmppDateTime.DateFormatType dateFormat) {
         this.pattern = datePattern;
         this.formatter = dateFormat;
      }
   }

   public static enum DateFormatType {
      XEP_0082_DATE_PROFILE("yyyy-MM-dd"),
      XEP_0082_DATETIME_PROFILE("yyyy-MM-dd'T'HH:mm:ssZ"),
      XEP_0082_DATETIME_MILLIS_PROFILE("yyyy-MM-dd'T'HH:mm:ss.SSSZ"),
      XEP_0082_TIME_PROFILE("hh:mm:ss"),
      XEP_0082_TIME_ZONE_PROFILE("hh:mm:ssZ"),
      XEP_0082_TIME_MILLIS_PROFILE("hh:mm:ss.SSS"),
      XEP_0082_TIME_MILLIS_ZONE_PROFILE("hh:mm:ss.SSSZ"),
      XEP_0091_DATETIME("yyyyMMdd'T'HH:mm:ss");

      private final String FORMAT_STRING;
      private final DateFormat FORMATTER;
      private final boolean CONVERT_TIMEZONE;
      private final boolean HANDLE_MILLIS;

      private DateFormatType(String dateFormat) {
         this.FORMAT_STRING = dateFormat;
         this.FORMATTER = new SimpleDateFormat(this.FORMAT_STRING);
         this.FORMATTER.setTimeZone(TimeZone.getTimeZone("UTC"));
         this.CONVERT_TIMEZONE = dateFormat.charAt(dateFormat.length() - 1) == 'Z';
         this.HANDLE_MILLIS = dateFormat.contains("SSS");
      }

      public String format(Date date) {
         String res;
         synchronized(this.FORMATTER) {
            res = this.FORMATTER.format(date);
         }

         if (this.CONVERT_TIMEZONE) {
            res = XmppDateTime.convertRfc822TimezoneToXep82(res);
         }

         return res;
      }

      public Date parse(String dateString) throws ParseException {
         if (this.CONVERT_TIMEZONE) {
            dateString = XmppDateTime.convertXep82TimezoneToRfc822(dateString);
         }

         if (this.HANDLE_MILLIS) {
            dateString = XmppDateTime.handleMilliseconds(dateString);
         }

         synchronized(this.FORMATTER) {
            return this.FORMATTER.parse(dateString);
         }
      }
   }
}
