package org.ovirt.engine.core.compat;

import java.io.Serializable;
import java.util.regex.Pattern;

public class TimeSpan implements Comparable<TimeSpan>, Serializable {
    private static final long serialVersionUID = -7809305197875724416L;

    public static final int MS_PER_SECOND = 1000;
    public static final int MS_PER_MINUTE = 60 * 1000;
    public static final int MS_PER_HOUR = 60 * 60 * 1000;
    public static final  int MS_PER_DAY = 24 * 60 * 60 * 1000;

    public int Days;
    public int Hours;
    public int Minutes;
    public int Seconds;
    public int Milliseconds;
    public double TotalDays;
    public double TotalHours;
    public double TotalMinutes;
    public double TotalSeconds;
    public long TotalMilliseconds;

    public TimeSpan() {
    }

    public TimeSpan(long milliseconds) {
        TotalMilliseconds = milliseconds;
        computeProperties();
    }

    public TimeSpan(int hours, int minutes, int seconds) {
        this(0, hours, minutes, seconds, 0);
    }

    public TimeSpan(int days, int hours, int minutes, int seconds) {
        this(days, hours, minutes, seconds, 0);
    }

    public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds) {
        TotalMilliseconds = milliseconds;
        TotalMilliseconds += seconds * MS_PER_SECOND;
        TotalMilliseconds += minutes * MS_PER_MINUTE;
        TotalMilliseconds += hours * MS_PER_HOUR;
        TotalMilliseconds += days * MS_PER_DAY;
        computeProperties();
    }

    public TimeSpan(int[] data) {
        this(data[0], data[1], data[2], data[3], data[4]);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + Days;
        result = prime * result + Hours;
        result = prime * result + Milliseconds;
        result = prime * result + Minutes;
        result = prime * result + Seconds;
        long temp;
        temp = Double.doubleToLongBits(TotalDays);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(TotalHours);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        result = prime * result + (int) (TotalMilliseconds ^ (TotalMilliseconds >>> 32));
        temp = Double.doubleToLongBits(TotalMinutes);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        temp = Double.doubleToLongBits(TotalSeconds);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        TimeSpan other = (TimeSpan) obj;
        if (Days != other.Days)
            return false;
        if (Hours != other.Hours)
            return false;
        if (Milliseconds != other.Milliseconds)
            return false;
        if (Minutes != other.Minutes)
            return false;
        if (Seconds != other.Seconds)
            return false;
        if (Double.doubleToLongBits(TotalDays) != Double.doubleToLongBits(other.TotalDays))
            return false;
        if (Double.doubleToLongBits(TotalHours) != Double.doubleToLongBits(other.TotalHours))
            return false;
        if (TotalMilliseconds != other.TotalMilliseconds)
            return false;
        if (Double.doubleToLongBits(TotalMinutes) != Double.doubleToLongBits(other.TotalMinutes))
            return false;
        if (Double.doubleToLongBits(TotalSeconds) != Double.doubleToLongBits(other.TotalSeconds))
            return false;
        return true;
    }

    protected void computeProperties() {
        long remainder = TotalMilliseconds;
        // Days
        Days = (int) (remainder / MS_PER_DAY);
        remainder = remainder % MS_PER_DAY;
        // Hours
        Hours = (int) (remainder / MS_PER_HOUR);
        remainder = remainder % MS_PER_HOUR;
        remainder = remainder % MS_PER_DAY;
        // Minutes
        Minutes = (int) (remainder / MS_PER_MINUTE);
        remainder = remainder % MS_PER_MINUTE;
        // Minutes
        Seconds = (int) (remainder / MS_PER_SECOND);
        remainder = remainder % MS_PER_SECOND;

        Milliseconds = (int) remainder;
    }

    // The format for a timespan is:
    // [ws][-]{ d | d.hh:mm[:ss[.ff]] | hh:mm[:ss[.ff]] }[ws]
    public static TimeSpan Parse(String argvalue) {
        String cleaned = argvalue.trim();
        int multiplier = 1;
        if (cleaned.contains("-")) {
            multiplier = -1;
            cleaned = cleaned.replace("-", "").trim();
        }

        if (Pattern.matches("[0-9]+", cleaned)) {
            int days = Integer.parseInt(cleaned);
            return new TimeSpan(days * multiplier, 0, 0, 0);
        }

        String regex = "[0-9]+.[0-9]{2}:[0-9]{2}(:[0-9]{2}(.[0-9]{2})?)?";
        if (Pattern.matches(regex, cleaned)) {
            String[] items = cleaned.split("[:.]");
            int[] data = new int[5];

            for (int x = 0; x < items.length; x++) {
                data[x] = Integer.parseInt(items[x]) * multiplier;
            }

            return new TimeSpan(data);
        }

        regex = "[0-9]{2}:[0-9]{2}(:[0-9]{2}(.[0-9]{2})?)?";
        if (Pattern.matches(regex, cleaned)) {
            String[] items = cleaned.split("[:.]");
            int[] data = new int[5];

            for (int x = 0; x < items.length; x++) {
                data[x + 1] = Integer.parseInt(items[x]) * multiplier;
            }

            return new TimeSpan(data);
        }
        // If we get to here, it is invalid
        throw new CompatException("Invalid TimeSpan");
    }

    @Override
    public String toString() {
        String prefix = "";
        if (TotalMilliseconds < 0) {
            prefix = "-";
        }
        return StringFormat.format("%s%d.%02d:%02d:%02d.%03d", prefix, Days, Hours, Minutes, Seconds, Milliseconds);
    }

    public static TimeSpan tryParse(String string) {
        try {
            return TimeSpan.Parse(string);
        } catch (CompatException e) {
            // eat it, return null
            return null;
        }
    }

    @Override
    public int compareTo(TimeSpan o) {
        // TODO Auto-generated method stub
        throw new NotImplementedException();
    }

}
