/* Copyright (c) 2013 OpenPlans. All rights reserved.
 * This code is licensed under the BSD New License, available at the root
 * application directory.
 */

package org.geogit.api.plumbing;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;

import javax.xml.datatype.DatatypeConfigurationException;

import org.geogit.api.AbstractGeoGitOp;
import org.geogit.api.Platform;

import com.google.common.base.Preconditions;
import com.google.inject.Inject;

/**
 * Parses a string representing a timestamp.
 * 
 * Supported formats are:
 * 
 * - Standard format supported by the DateFormat class in the current locale - git-like time strings
 * (yesterday, 2.days.ago, etc) - a long representing miliseconds since the standard UNIX epoch
 * 
 */
public class ParseTimestamp extends AbstractGeoGitOp<Long> {

    private String string;

    private Platform platform;

    private static HashMap<String, Integer> units = new HashMap<String, Integer>();

    static {
        units.put("second", 1000);
        units.put("minute", 1000 * 60);
        units.put("hour", 1000 * 60 * 60);
        units.put("day", 1000 * 60 * 60 * 24);
        units.put("week", 1000 * 60 * 60 * 24 * 7);
        units.put("year", 1000 * 60 * 60 * 24 * 365);
    }

    @Inject
    public ParseTimestamp(Platform platform) {
        this.platform = platform;
    }

    /**
     * @param string the String to parse
     * @return {@code this}
     */
    public ParseTimestamp setString(String string) {
        this.string = string;
        return this;
    }

    /**
     * Parses a string with a timestamp
     * 
     * @return a Long with the timestamp represented by the specified string
     */
    @Override
    public Long call() {
        Preconditions.checkState(string != null, "String has not been set.");

        try { // see if it is a timestamp in milisecs
            Long milis = new Long(string);
            return milis;
        } catch (NumberFormatException e) {
        }

        SimpleDateFormat formatter;
        if (string.equals("yesterday")) { // return the beginning of yesterday, not just 24h ago
                                          // from current time
            try {
                formatter = new SimpleDateFormat("dd/MM/yyyy");
                Date today = new Date(platform.currentTimeMillis());
                long todayOnlyDate = formatter.parse(formatter.format(today)).getTime();
                long millisecsInOneDay = 60 * 60 * 24 * 1000;
                long yesterday = todayOnlyDate - millisecsInOneDay;
                return yesterday;
            } catch (ParseException e) {
                // shouldn't reach this
            }
        }

        if (string.equals("today")) {
            try {
                formatter = new SimpleDateFormat("dd/MM/yyyy");
                Date today = new Date(platform.currentTimeMillis());
                long todayOnlyDate = formatter.parse(formatter.format(today)).getTime();
                return todayOnlyDate;
            } catch (ParseException e) {
                // shouldn't reach this
            }
        }

        // parse it as a git-like time reference
        String[] tokens = string.split("\\.");
        if (tokens.length % 2 != 0) {
            if (tokens[tokens.length - 1].toLowerCase().equals("ago")) {
                long currentTime = platform.currentTimeMillis();
                int i;
                for (i = 0; i < tokens.length - 1; i++) {
                    try {
                        double number = Double.parseDouble(tokens[i]);
                        i++;
                        String s = tokens[i].toLowerCase();
                        if (s.endsWith("s")) {
                            s = s.substring(0, s.length() - 1);
                        }
                        if (units.containsKey(s)) {
                            currentTime -= units.get(s) * number;
                        } else {
                            break;
                        }
                    } catch (Exception e) {
                        break;
                    }
                }
                if (i == tokens.length - 1) {
                    return currentTime;
                }
            }
        }

        // finally, try to parse it as a Date object
        try {
            long time = javax.xml.datatype.DatatypeFactory.newInstance()
                    .newXMLGregorianCalendar(string).toGregorianCalendar().getTimeInMillis();
            return time;
        } catch (DatatypeConfigurationException e) {
        } catch (IllegalArgumentException e) {
        }

        throw new IllegalArgumentException("Invalid timestamp string: " + string);

    }
}

@@ -601,18 +601,28 @@
 %0A * %0A * 
-- 
+%3Cul%3E%0A * %3Cli%3E
 Standard
@@ -684,19 +684,24 @@
 t locale
- - 
+%0A * %3Cli%3E
 git-like
@@ -713,19 +713,16 @@
  strings
-%0A *
  (yester
@@ -746,17 +746,30 @@
 etc)
- - a
+%0A * %3Cli%3Ea %7B@code
  long
+%7D
  rep
@@ -820,16 +820,25 @@
 X epoch%0A
+ * %3C/ul%3E%0A
  * %0A */%0A

fix javadoc format for ParseTimestamp.