/*   **********************************************************************  **
 **   Copyright notice                                                       **
 **                                                                          **
 **   (c) 2003-2006 RSSOwl Development Team                                  **
 **   http://www.rssowl.org/                                                 **
 **                                                                          **
 **   All rights reserved                                                    **
 **                                                                          **
 **   This program and the accompanying materials are made available under   **
 **   the terms of the Eclipse Public License 1.0 which accompanies this     **
 **   distribution, and is available at:                                     **
 **   http://www.rssowl.org/legal/epl-v10.html                               **
 **                                                                          **
 **   A copy is found in the file epl-v10.html and important notices to the  **
 **   license from the team is found in the textfile LICENSE.txt distributed **
 **   in this package.                                                       **
 **                                                                          **
 **   This copyright notice MUST APPEAR in all copies of the file!           **
 **                                                                          **
 **   Contributors:                                                          **
 **     Niko Schmuck -  initial API and implementation (niko@nava.de)        **
 **     RSSOwl       -  Additional API / Added Formats (bpasero@rssowl.org)  **
 **                                                                          **
 **  **********************************************************************  */

package net.sourceforge.rssowl.util;

import net.sourceforge.rssowl.util.shop.StringShop;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

/**
 * Utility class providing convenience methods to (XML) parsing mechanisms.
 * 
 * @author Niko Schmuck (niko@nava.de)
 * @autohr Benjamin Pasero (bpasero@rssowl.org)
 * @version 1.2.3
 */
public final class DateParser {

  /** Formatter for Date based on OS Locale */
  private static final DateFormat DATE_FORMAT = DateFormat.getDateInstance(DateFormat.SHORT);

  /** Formatter for Date (long) based on OS Locale */
  private static final DateFormat DATE_LONG_FORMAT = DateFormat.getDateInstance(DateFormat.FULL);

  /** Formatter for Date and Time based on OS Locale */
  private static final DateFormat DATE_TIME_FORMAT = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);

  /** Formatter for Date and Time (long) based on OS Locale */
  private static final DateFormat DATE_TIME_LONG_FORMAT = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.SHORT);

  /** An array of common date formats */
  private static SimpleDateFormat[] dateFormats = null;

  /** This utility class constructor is hidden */
  private DateParser() {
  // Protect default constructor
  }

  /**
   * Remove special chars of a date to use it into a filename
   * 
   * @param date The date as String
   * @return String The given date with replaced special chars
   */
  public static String dateToFileName(String date) {
    String separator = "_";
    date = date.replaceAll(" ", separator);
    date = date.replaceAll("\\.", separator);
    date = date.replaceAll(":", separator);
    date = date.replaceAll("-", separator);
    date = date.replaceAll("/", separator);
    return date;
  }

  /**
   * Format the current date to a String using the selected Locale and do not
   * add the time to the String
   * 
   * @return String Formatted Date
   */
  public static String formatDate() {
    return formatDate(new Date(), false, false);
  }

  /**
   * Format the current date to a String using the selected Locale.
   * 
   * @param withTime If TRUE set time to date String
   * @return String Formatted Date
   */
  public static String formatDate(boolean withTime) {
    return formatDate(new Date(), false, withTime);
  }

  /**
   * Format the date to a String using the selected Locale.
   * 
   * @param date The date to format
   * @param withTime If TRUE set time to date String
   * @return String Formatted Date
   */
  public static String formatDate(Date date, boolean withTime) {
    return formatDate(date, false, withTime);
  }

  /**
   * Format the date to a String using the selected Locale.
   * 
   * @param date The date to format
   * @param longDateFormat If TRUE use the Long Date Format
   * @param withTime If TRUE set time to date String
   * @return String Formatted Date
   */
  public static String formatDate(Date date, boolean longDateFormat, boolean withTime) {

    /** Long Date Format with Time */
    if (longDateFormat && withTime)
      return DATE_TIME_LONG_FORMAT.format(date);

    /** Short Date Format without Time */
    else if (withTime)
      return DATE_TIME_FORMAT.format(date);

    /** Long Date Format Without Time */
    else if (longDateFormat)
      return DATE_LONG_FORMAT.format(date);

    /** Short Date Format Without Time */
    return DATE_FORMAT.format(date);
  }

  /**
   * Get the localized long date format for the given language.
   * 
   * @param aDate The date to format
   * @return String The date format
   */
  public static String formatLongDate(Date aDate) {
    return DATE_LONG_FORMAT.format(aDate);
  }

  /**
   * Tries different date formats to parse against the given string
   * representation to retrieve a valid Date object.
   * 
   * @param strdate Date as String
   * @return Date The parsed Date
   */
  public static Date getDate(String strdate) {

    /** Return in case the string date is not set */
    if (!StringShop.isset(strdate))
      return null;

    Date result = null;
    strdate = strdate.trim();
    if (strdate.length() > 10) {

      /** Open: deal with +4:00 (no zero before hour) */
      if ((strdate.substring(strdate.length() - 5).indexOf("+") == 0 || strdate.substring(strdate.length() - 5).indexOf("-") == 0) && strdate.substring(strdate.length() - 5).indexOf(":") == 2) {
        String sign = strdate.substring(strdate.length() - 5, strdate.length() - 4);
        strdate = strdate.substring(0, strdate.length() - 5) + sign + "0" + strdate.substring(strdate.length() - 4);
      }

      String dateEnd = strdate.substring(strdate.length() - 6);

      /**
       * try to deal with -05:00 or +02:00 at end of date replace with -0500 or
       * +0200
       */
      if ((dateEnd.indexOf("-") == 0 || dateEnd.indexOf("+") == 0) && dateEnd.indexOf(":") == 3) {
        if (!"GMT".equals(strdate.substring(strdate.length() - 9, strdate.length() - 6))) {
          String oldDate = strdate;
          String newEnd = dateEnd.substring(0, 3) + dateEnd.substring(4);
          strdate = oldDate.substring(0, oldDate.length() - 6) + newEnd;
        }
      }
    }

    /** Try to parse the date */
    int i = 0;
    while (i < dateFormats.length) {
      try {

        /**
         * This Block needs to be synchronized, because the parse-Method in
         * SimpleDateFormat is not Thread-Safe.
         */
        synchronized (dateFormats[i]) {
          return dateFormats[i].parse(strdate);
        }
      } catch (ParseException e) {
        i++;
      } catch (NumberFormatException e) {
        i++;
      }
    }
    return result;
  }

  /** Initialize the array of common date formats */
  static {
    final String[] possibleDateFormats = {

    /** RFC 1123 with 2-digit Year */
    "EEE, dd MMM yy HH:mm:ss z",

    /** RFC 1123 with 4-digit Year */
    "EEE, dd MMM yyyy HH:mm:ss z",

    /** RFC 1123 with no seconds */
    "EEE, dd MMM yyyy HH:mm z",

    /** RFC 1123 with no Timezone */
    "EEE, dd MMM yy HH:mm:ss",

    /** Variant of RFC 1123 */
    "EEE, MMM dd yy HH:mm:ss",

    /** Variant of RFC 1123 */
    "EEE dd MMM yyyy HH:mm:ss",
    
    /** Another Variant */
    "EEE MMM dd HH:mm:ss yyyy",

    /** ISO 8601 slightly modified */
    "yyyy-MM-dd'T'HH:mm:ssZ",

    /** ISO 8601 slightly modified */
    "yyyy-MM-dd'T'HH:mm:ss'Z'",

    /** ISO 8601 slightly modified */
    "yyyy-MM-dd'T'HH:mm:sszzzz",

    /** ISO 8601 slightly modified */
    "yyyy-MM-dd'T'HH:mm:ss z",

    /** ISO 8601 */
    "yyyy-MM-dd'T'HH:mm:ssz",

    /** ISO 8601 slightly modified */
    "yyyy-MM-dd'T'HH:mm:ss",

    /** ISO 8601 slightly modified */
    "yyyy-MM-dd'T'HHmmss.SSSz",

    /** ISO 8601 slightly modified */
    "yyyy-MM-dd'T'HH:mm:ss",

    /** ISO 8601 w/o seconds */
    "yyyy-MM-dd'T'HH:mmZ",

    /** ISO 8601 w/o seconds */
    "yyyy-MM-dd'T'HH:mm'Z'",

    /** European Date Format */
    "dd-mm-yyyy HH:mm:ss",

    /** RFC 1123 without Day Name */
    "dd MMM yyyy HH:mm:ss z",

    /** RFC 1123 without Day Name and Timezone */
    "dd MMM yyyy HH:mm:ss",

    /** RFC 1123 without Day Name and Seconds */
    "dd MMM yyyy HH:mm z",

    /** Simple Date Format #1 */
    "yyyy-MM-dd",

    /** Simple Date Format #2 */
    "MMM dd, yyyy"

    };

    /** Create the dateformats */
    dateFormats = new SimpleDateFormat[possibleDateFormats.length];
    TimeZone gmtTZ = TimeZone.getTimeZone("GMT");
    for (int i = 0; i < possibleDateFormats.length; i++) {
      dateFormats[i] = new SimpleDateFormat(possibleDateFormats[i], Locale.ENGLISH);
      dateFormats[i].setTimeZone(gmtTZ);
    }
  }
}