/**
 * Encoding to and decoding from the BidInfo format used by the web shops and
 * bidding clients to transmit bidding relevant data in a compact form.
 *
 * @author  Martin Rubli
 * @version 1.0
 */
public class BidInfo
{
	/** The delimiter to the left and the right of an encoded string */
	private static final String DELIMITER = "/";

	/** The delimiter separating the different fields */
	private static final String FIELDDELIMITER = "_";


	/** The decoded vendor number */
	private int vendor;

	/** The decoded item identifier */
	private String item;

	/** The decoded list price */
	private float price;


	/** Exception thrown by the BidInfo constructor signalling an invalid
	 *  input string format
	 */
	public class InvalidFormatException extends Exception
	{
		public InvalidFormatException(String message) {
			super(message);
		}
	}


	/** Creates a new BidInfo object that decodes the given string */
	public BidInfo(String encoded) throws InvalidFormatException
	{
		if(!encoded.startsWith(DELIMITER) || !encoded.endsWith(DELIMITER))
			throw new InvalidFormatException("Invalid format: Missing delimiter");
		
		encoded = encoded.substring(1, encoded.length() - 1);
		String tokens[] = encoded.split("(?<!_)_(?!_)", 3);
		if(tokens.length != 3)
			throw new InvalidFormatException("Invalid format: Wrong number of tokens");
		
		try {
			vendor = Integer.valueOf(tokens[0]).intValue();
			item = unescape(tokens[1]);
			price = Float.valueOf(tokens[2]).floatValue();
		} catch(NumberFormatException e) {
			throw new InvalidFormatException("Invalid number format: " + e.getMessage());
		}
	}


	/** Returns the decoded vendor number */
	public int getVendor() { return vendor; }

	/** Returns the decoded item identifier */
	public String getItem() { return item; }

	/** Returns the decoded list price */
	public float getPrice() { return price; }

	/** Encode the object contents into a string */
	public String encode()
	{
		return encode(this);
	}


	/** Returns a string representing the BidInfo object */
	public String toString()
	{
		return "{ vendor = " + vendor + ", item = '" + item + "', price = " + price + " }";
	}


	/** Encodes a given set of items into a string */
	public static String encode(int vendor, String item, float price)
	{
		String s = DELIMITER;
		s += vendor + FIELDDELIMITER;
		s += escape(item) + FIELDDELIMITER;
		s += price;
		s += DELIMITER;
		return s;
	}

	/** Encode a given BidInfo object into a string */
	public static String encode(BidInfo info)
	{
		return encode(info.vendor, info.item, info.price);
	}


	/** Escape field delimiters in a string */
	private static String escape(String s)
	{
		return s.replaceAll(FIELDDELIMITER, FIELDDELIMITER + FIELDDELIMITER);
	}

	/** Unescape field delimiters in a string */
	private static String unescape(String s)
	{
		return s.replaceAll(FIELDDELIMITER + FIELDDELIMITER, FIELDDELIMITER);
	}


	/** Run several test cases */
	public static void main(String args[])
	{
		// Valid test cases
		try {
			String a = BidInfo.encode(1, "Simple item", 11.11f);
			System.out.println(a);
			System.out.println(new BidInfo(a));
			String b = BidInfo.encode(2, "Complex_item_with_underlines", 22.22f);
			System.out.println(b);
			System.out.println(new BidInfo(b));
			String c = BidInfo.encode(3, "More_complex__item___string", 33.33f);
			System.out.println(c);
			System.out.println(new BidInfo(c));
		} catch(Exception e) {
			e.printStackTrace();
		}

		// Invalid test cases
		System.out.println();
		try {
			String s = "/4_Overlength item_44.44_trash/";
			System.out.println(s);
			System.out.println(new BidInfo(s));
		} catch(InvalidFormatException e) {
			System.out.println("FAILED: " + e);
		}
		try {
			String s = "/X_Invalid vendor_55.55/";
			System.out.println(s);
			System.out.println(new BidInfo(s));
		} catch(InvalidFormatException e) {
			System.out.println("FAILED: " + e);
		}
		try {
			String s = "/6_Invalid price_6X6.66/";
			System.out.println(s);
			System.out.println(new BidInfo(s));
		} catch(InvalidFormatException e) {
			System.out.println("FAILED: " + e);
		}
	}
}


