package cnrg.itx.ds;

import java.util.*;
import java.io.*;

/**
 * This class provides a abstraction of where the application or system is located. <br>
 * There are 3 properties: <br>
 * 1. Type - type of location: Telephone or Internet or User (e.g. bergmark@cs.cornell.edu, vmailsrv, etc) <br>
 * 2. Value - hostname:port or areacode-phonenumber or userid <br>
 * 3. Label - alias (standard) name of the location. E.g. "home"; "Spot" <br>
 */
public class Location implements Serializable {
	
	public static final String TELEPHONE_TYPE = "T";
	public static final String INTERNET_TYPE = "I";
	public static final String USER_TYPE = "U";
	
	public static final int ROAMING = 1;
	public static final int DEFAULT = 2;
	public static final int DYNAMIC = 3;
	
	private static final String SEPARATOR_LOCATION = "+";
	private static final String SEPARATOR_INTERNET = ":";
	private static final String SEPARATOR_TELEPHONE = "-";
	
	// private data members
	private String m_type = null;
	private String m_value = null;
	private String m_label = null;

	/**
	 * Copy constructor
	 * @param otherLoc other location object
	 */
	public Location(Location otherLoc){
		m_type = new String(otherLoc.getType());
		m_value = new String(otherLoc.getValue());
		m_label = new String(otherLoc.getLabel());
	}
	
	/**
	 * Constructor for formatted record string retrieved from the directory database.  If the record string is not in expected format, the location remains un-initialized.
	 * @param rawLocationStr a raw string from record entry (e.g. "pit052.cs.cornell.edu:5000+I+testapp"; "607-2775627+T"; "vmailsrv+U+voice mail server")
	 */
	public Location(String rawLocationStr){
		if (rawLocationStr == null || rawLocationStr.equals("") )
			return;

		StringTokenizer st = new StringTokenizer(rawLocationStr, SEPARATOR_LOCATION);
		int nCount = st.countTokens();
		if (nCount >= 2) {
			String field1 = st.nextToken();
			String field2 = st.nextToken();
			m_value = new String(field1);
			m_type = new String(field2);
			if (nCount > 2)
				m_label = new String(st.nextToken());
			else
				m_label = new String("");
		}
		else
			return;
	}
	
	/**
	 * Constructor <br>
	 * For telephone: type = Location.TELEPHONE_TYPE; value = Areacode-PhoneNumber; label = "home" <br>
	 *   e.g. SetLocation(Location.TELEPHONE_TYPE, "607-2551595", "home"); <br>
	 * For internet: type = Location.INTERNET_TYPE; value = hostname:port; label = App. name <br>
	 *   e.g. SetLocation(Location.INTERNET_TYPE, "pnt01.cs.cornell.edu:5001", "Moonlight"); <br>
	 * For user: type = Location.USER_TYPE; value = email OR server; label = [applicable name] <br>
	 *   e.g. SetLocation(Location.USER_TYPE, "bergmark@cs.cornell.edu", ""); <br>
	 * @param type type of the location (e.g. Location.TELEPHONE_TYPE or Location.INTERNET_TYPE or Location.USER_TYPE)
	 * @param value value of the location
	 * @param label label of the location (can be empty)
	 */ 
	public Location(String type, String value, String label){
		m_type = type;
		m_value = value;
		m_label = label;
	}

	/**
	 * Determine if this location can be directly dialed (connected)
	 * @return Ture if it can be dialied; False otherwise
	 */
	public boolean isDialable() {
		return (m_type.equals(INTERNET_TYPE));
	}
	
	/**
	 * Return the hostname address if this location can be directly dialed.  Otherwise, return null.
	 * @return string value containing the hostname address; null otherwise
	 */
	public String getIP() {
		if (isDialable()) {
			String[] array = enumerateValue();
			return array[0];
		}
		else
			return null;
	}
	
	/**
	 * Return the port number if this location can be directly dialed.  Otherwise, returns -1.
	 * @return int value containing the port number; -1 otherwise
	 */
	public int getPort() {
		if (isDialable()) {
			String[] array = enumerateValue();
			return Integer.parseInt(array[1]);
		}
		else
			return -1;
	}

	/**
	 * Return the UID of this user-type location (if this is a internet-type location, returns null).
	 * @return UserID object of the user-type location
	 * 
	 */
	public UserID getUID() {
		if (m_type.equals(USER_TYPE)) {
			return new UserID(m_value);
		}
		else if (m_type.equals(TELEPHONE_TYPE)){
			String arrstr[] = enumerateValue();
			return new UserID(arrstr[0] + arrstr[1]);			
		}
		else
			return null;
	}
	
	/**
	 * Get the Location type
	 * @return location type in string
	 */
	public String getType(){
		return m_type;
	}

	/**
	 * Get the Location value
	 * @return location value in string
	 */
	public String getValue(){
		return m_value;
	}

	/**
	 * Get the Location (optional) label
	 * @return location label in string
	 */
	public String getLabel(){
		return m_label;
	}

	/**
	 * Set the Location (optional) label
	 * @param label location label in string
	 */
	public void setLabel(String label){
		m_label = label;
	}
	
	/**
	 * Determine if the Location is destined to the telephone end
	 * @return true if the location is destined to the telephone end; false otherwise
	 */
	public boolean isPhone(){
		return (m_type.equals(TELEPHONE_TYPE));
	}

	/**
	 * Compares with other location object to determine if they are identical
	 * @param otherlocation other location object to be compared
	 * @return true if identical; false otherwise
	 */
	public boolean isSameAs(Location otherlocation){
		if (m_type.equals(otherlocation.getType()) == false)
			return false;
		else if (m_label.equals(otherlocation.getLabel()) == false)
			return false;
		else if (m_value.equals(otherlocation.getValue()) == false)
			return false;
		return true;
	}

	/**
	 * Convert into raw location string format.  Returns empty if the location object's properties are not sufficient enough to form a string.
	 * @return string representation of the location object.
	 */
	public String toString(){
		if ((m_value.equals("")==false) && (m_type.equals("")==false)) {
			if (m_label.equals("") == false)
				return new String(m_value + SEPARATOR_LOCATION + m_type + SEPARATOR_LOCATION + m_label);
			else
				return new String(m_value + SEPARATOR_LOCATION + m_type);
		}
		return new String("");			
	}
	
	/**
	 * Parses the value of location into a array of string and returns <br>
	 *   e.g. String[0] = "pnt01.cs.cornell.edu"; String[1] = "5001" <br>
	 *   e.g. String[0] = "607"; String[1] = "2551595" <br>
	 *   e.g. String[0] = "bergmark@cs.cornell.edu" <br>
	 * @return an array of string; null if not applicable
	 */
	public String [] enumerateValue(){
		String [] array;
		
		if (m_type.equals(INTERNET_TYPE)) {
			array = new String[2];
			StringTokenizer st = new StringTokenizer(m_value, SEPARATOR_INTERNET);
			String field = st.nextToken();
			array[0] = new String(field);
			field = st.nextToken();
			array[1] = new String(field);
			return array;
		}
		else if (m_type.equals(TELEPHONE_TYPE)) {
			array = new String[2];
			StringTokenizer st = new StringTokenizer(m_value, SEPARATOR_TELEPHONE);
			String field = st.nextToken();
			array[0] = new String(field);
			field = st.nextToken();
			array[1] = new String(field);
			return array;
		}
		else if (m_type.equals(USER_TYPE)) {
			array = new String[1];
			array[0] = new String(m_value);
			return array;
		}
		else
			return null;
	}
}
