// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)

package org.xbill.DNS;

/**
 * Routines for parsing BIND-style TTL values. These values consist of numbers
 * followed by 1 letter units of time (W - week, D - day, H - hour, M - minute,
 * S - second).
 * 
 * @author Brian Wellington
 */

public final class TTL {

	public static final long MAX_VALUE = 0x7FFFFFFFL;

	private TTL() {
	}

	static void check(long i) {
		if (i < 0 || i > MAX_VALUE)
			throw new InvalidTTLException(i);
	}

	/**
	 * Parses a TTL-like value, which can either be expressed as a number or a
	 * BIND-style string with numbers and units.
	 * 
	 * @param s
	 *            The string representing the numeric value.
	 * @param clamp
	 *            Whether to clamp values in the range [MAX_VALUE + 1, 2^32 -1]
	 *            to MAX_VALUE. This should be donw for TTLs, but not other
	 *            values which can be expressed in this format.
	 * @return The value as a number of seconds
	 * @throws NumberFormatException
	 *             The string was not in a valid TTL format.
	 */
	public static long parse(String s, boolean clamp) {
		if (s == null || s.length() == 0 || !Character.isDigit(s.charAt(0)))
			throw new NumberFormatException();
		long value = 0;
		long ttl = 0;
		for (int i = 0; i < s.length(); i++) {
			char c = s.charAt(i);
			long oldvalue = value;
			if (Character.isDigit(c)) {
				value = (value * 10) + Character.getNumericValue(c);
				if (value < oldvalue)
					throw new NumberFormatException();
			} else {
				switch (Character.toUpperCase(c)) {
				case 'W':
					value *= 7;
				case 'D':
					value *= 24;
				case 'H':
					value *= 60;
				case 'M':
					value *= 60;
				case 'S':
					break;
				default:
					throw new NumberFormatException();
				}
				ttl += value;
				value = 0;
				if (ttl > 0xFFFFFFFFL)
					throw new NumberFormatException();
			}
		}
		if (ttl == 0)
			ttl = value;

		if (ttl > 0xFFFFFFFFL)
			throw new NumberFormatException();
		else if (ttl > MAX_VALUE && clamp)
			ttl = MAX_VALUE;
		return ttl;
	}

	/**
	 * Parses a TTL, which can either be expressed as a number or a BIND-style
	 * string with numbers and units.
	 * 
	 * @param s
	 *            The string representing the TTL
	 * @return The TTL as a number of seconds
	 * @throws NumberFormatException
	 *             The string was not in a valid TTL format.
	 */
	public static long parseTTL(String s) {
		return parse(s, true);
	}

	public static String format(long ttl) {
		TTL.check(ttl);
		StringBuffer sb = new StringBuffer();
		long secs, mins, hours, days, weeks;
		secs = ttl % 60;
		ttl /= 60;
		mins = ttl % 60;
		ttl /= 60;
		hours = ttl % 24;
		ttl /= 24;
		days = ttl % 7;
		ttl /= 7;
		weeks = ttl;
		if (weeks > 0)
			sb.append(weeks + "W");
		if (days > 0)
			sb.append(days + "D");
		if (hours > 0)
			sb.append(hours + "H");
		if (mins > 0)
			sb.append(mins + "M");
		if (secs > 0 || (weeks == 0 && days == 0 && hours == 0 && mins == 0))
			sb.append(secs + "S");
		return sb.toString();
	}

}
