package smart_gs.logical;

import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import reasoning_web.logical.inter_face.RWElement;
import smart_gs.GSConstants;

public class URIObject {
	public static final String ANONYMOUS_USER = "AnonymousUser";
	public static String LAST_CREATED_DATE;
	public static int ADDITIONAL_EXTENSION;

	public static final String HEADER = GSConstants.URI_HEADER;
	public static final String SEP1 = "/";
	public static final String SEP2 = "^";
	public static final String SEP3 = "#";
	public static final String SEP4 = "@";

	public static final String SPREAD_DIRECTORY = "directory";
	public static final String SPREAD = "spread";
	public static final String REGION = "region";
	public static final String LINE_SEGMENT = "linesegment";
	public static final String BOOKMARK = "bookmark";
	public static final String POLYGON = "polygon";
	public static final String RECTANGLE = "rect";
	public static final String MARKER = "marker";
	public static final String LINE = "line";
	public static final String MEMOPAD = "memopad";
	public static final String STANDARD_TEXT_DOCUMENT = "document";
	public static final String IDAT = "Idat";
	public static final String FIRST_IDAT = "firstIdat";
	public static final String SECOND_IDAT = "secondIdat";
	public static final String THIRD_IDAT = "thirdIdat";
	public static final String USERS_NOTE = "usersNote";
	public static final String EXPLANATORY_NOTE = "explanatory";
	public static final String TEXT_SEGMENT = "segment";
	public static final String CONNECTION = "connection";
	public static final String RWPACKAGE = "package";
	public static final String GS_RWPACKAGE = "gsRWPackage";
	public static final String GSQUERY = "query";
	public static final String SHORTCUT = "shortcut";
	public static final String BUCKET = "bucket";
	public static final String DESKTOP_FOLDER = "desktopFolder";
	public static final String USERS_NOTE_DIRECTORY = "usersNoteDirectory";
	public static final String ZOMBIE = "zombie";
	public static final String ANCHOR = "anchor";
	public static final String ANCHOR_TEXT_SEGMENT = "anchorTextSegment";
	public static final String RESOURCE_BASKET = "resourceBasket";

	private String type;
	private URIObject container;
	private URIObject source;
	private URIObject target;
	private String name;
	private String usersName;
	private String date;
	private String exception = "UNKNOWN";
	
	public URIObject(String type) {
		this.type = type;
		this.usersName = getUsersName();
		String date = getDate();
		String addExt = getAdditionalExtension(date);
		this.date = date + addExt;
	}
	public URIObject(String type, String usersName, String date) {
		this(type);
		this.usersName = usersName;
		this.date = date;		
	}
	
	public static boolean isDirectoryType(String type) {
		return type.equals(SPREAD_DIRECTORY) || type.equals(USERS_NOTE_DIRECTORY)  || type.equals(DESKTOP_FOLDER);
	}
	
	// arg is assumed to be a correct URI
	public static URIObject parse(String arg) {
		URIObject uri = new URIObject(getTypeFrom(arg));
		if (uri.type == null) {
			uri.exception = arg;
			return uri;
		}
		String uriWithoutHeader = chopHeader(arg);
		String uriRest = rest(uriWithoutHeader);
		if (isDirectoryType(uri.type)) {
			uri.usersName = getUsersNameFrom(arg);
			uri.date = getDateFrom(arg);
			if (rest(uriWithoutHeader).equals("")) {
				uri.container = null;
				uri.name = "root";
				return uri;
			} else {
				uri.container = parse(HEADER + SPREAD_DIRECTORY + SEP1 + chopLastTwo(uriWithoutHeader));
				uri.name = getLast(uriWithoutHeader);
				return uri;
			}
		}

		if (isNamed(arg)) {
			uri.name = getNameFrom(arg);
		}
		if (hasContainer(arg)) {
			uri.container = parse(getContainerURIFrom(arg));
		}
		if (getTypeFrom(arg).equals(CONNECTION)) {
			uri.source = parse(getSourceURIFrom(arg));
			uri.target = parse(getTargetURIFrom(arg));
		}
		return uri;
	}

	public String toStringWithoutHeader() {
		if (this.type == null) {
			return this.exception;
		}
		String uri = this.type + SEP1;
		if (this.type.equals(SPREAD_DIRECTORY)) {
			if (this.container == null) {
				uri += "root" + SEP1;
				return uri;
			} else {
				uri += rest(this.container.toStringWithoutHeader()) 
					+ this.name + SEP1;
				return uri;
			}
		}
		if (this.name != null) {
			uri += this.name + SEP1;
		}
		uri += this.usersName + SEP1 + this.date + SEP1;
		if (this.container != null) {
			uri += SEP2 + this.container.toStringWithoutHeader();
		}
		if (this.type.equals(CONNECTION)) {
			uri += SEP3 + this.source.toStringWithoutHeader();
			uri += SEP4 + this.target.toStringWithoutHeader();
		}
		return uri;
	}
	@Override
	public String toString() {
		return HEADER + this.toStringWithoutHeader();
	}
	
	public Element createXMLElement(Document document) {
		Element element = document.createElement("uri");
		element.setAttribute("type",this.type);
		element.setAttribute("name",this.name);
		element.setAttribute("usersName",this.usersName);	
		element.setAttribute("date",this.date);
		element.setAttribute("exception",this.exception);
		element.appendChild(this.container.createXMLElement(document));
		element.appendChild(this.source.createXMLElement(document));
		element.appendChild(this.target.createXMLElement(document));
		return element;
	}
	public static URIObject restore(Element elem) {
		String type = elem.getAttribute("type");
		URIObject uri = new URIObject(type);
		uri.name = elem.getAttribute("name");
		uri.usersName = elem.getAttribute("usersName");
		uri.date = elem.getAttribute("date");
		uri.exception = elem.getAttribute("exception");
		uri.container = restore((Element)elem.getChildNodes().item(0));
		uri.source = restore((Element)elem.getChildNodes().item(1));
		uri.target = restore((Element)elem.getChildNodes().item(2));
		return uri;
	}
	
	
	public static String chopHeader(String uri) {
		if (uri.startsWith(HEADER)) {
			return uri.substring(HEADER.length());
		} else {
			return uri;
		}
	}
	public static String getTypeFrom(String uri) {
		return first(chopHeader(uri));
	}
	private static String getNameFrom(String uri) {
		if (isNamed(uri)) {
			return first(rest(chopHeader(uri)));
		} else {
			return "";
		}
	}
	private static String getUsersNameFrom(String uri) {
		if (isNamed(uri)) {
			return first(rest(rest(chopHeader(uri))));
		} else {
			return first(rest(chopHeader(uri)));
		}
	}
	private static String getDateFrom(String uri) {
		if (isNamed(uri)) {
			return first(rest(rest(rest(chopHeader(uri)))));
		} else {
			return first(rest(rest(chopHeader(uri))));
		}
	}
	
	public static String first(String arg) {
		if (indexOfNextSEP1(arg) >= 0) {
			return arg.substring(0, indexOfNextSEP1(arg));
		} else {
			return null;
		}
	}
	public static String rest(String arg) {
		if (indexOfNextSEP1(arg) >= 0) {
			return arg.substring(indexOfNextSEP1(arg) + 1);
		} else {
			return null;
		}
	}
	public static int indexOfNextSEP1(String arg) {
		for (int i = 0; i < arg.length(); i++) {
			 if (arg.charAt(i) == SEP1.charAt(0)) return i;
		}
		return -1;
	}

	public static String getContainerURIFrom(String uri) {
		if (hasContainer(uri)) {
			int index = uri.indexOf(SEP2);
			if (index >= 0) {
				return HEADER + uri.substring(uri.indexOf(SEP2) + 1);
			} else {
				return null;
			}
		} else {
			return null;
		}
	}
	public static String getSourceURIFrom(String uri) {
		String type = getTypeFrom(uri);
		if (type.equals(CONNECTION)) {
			int beginIndex = uri.indexOf(SEP3)+1;
			int countSep3 = 1;
			for (int endIndex = beginIndex; endIndex < uri.length(); endIndex++) {
				if (uri.substring(endIndex).startsWith(SEP3)) {
					countSep3++;
				} else if (uri.substring(endIndex).startsWith(SEP4)) {
					countSep3--;
				}
				if (countSep3 == 0) {
					return uri.substring(beginIndex, endIndex);
				}
			}
			return "";
		} else {
			return "";
		}
	}
	public static String getTargetURIFrom(String uri) {
		String type = getTypeFrom(uri);
		if (type.equals(CONNECTION)) {
			int countSep3 = 1;
			for (int beginIndex = uri.indexOf(SEP3)+1; 
				beginIndex < uri.length(); beginIndex++) {
				if (uri.substring(beginIndex).startsWith(SEP3)) {
					countSep3++;
				} else if (uri.substring(beginIndex).startsWith(SEP4)) {
					countSep3--;
				}
				if (countSep3 == 0) {
					return uri.substring(beginIndex + 1);
				}
			}
			return "";
		} else {
			return "";
		}
	}
	public static boolean isNamed(String uri) {
		String type = getTypeFrom(uri);
		return type.equals(SPREAD)
				|| type.equals(USERS_NOTE)
				|| type.equals(GS_RWPACKAGE)
				|| type.equals(GSQUERY)
				|| type.equals(CONNECTION)
				|| type.equals(EXPLANATORY_NOTE);
	}
	public static boolean hasContainer(String uri) {
		String type = getTypeFrom(uri);
		return type.equals(SPREAD)
				|| type.equals(FIRST_IDAT)
				|| type.equals(SECOND_IDAT)
				|| type.equals(THIRD_IDAT)
				|| type.equals(REGION)
				|| type.equals(RECTANGLE)
				|| type.equals(MARKER)
				|| type.equals(POLYGON)
				|| type.equals(LINE)
				|| type.equals(LINE_SEGMENT)
				|| type.equals(MEMOPAD)
				|| type.equals(BOOKMARK)
				|| type.equals(EXPLANATORY_NOTE)
				|| type.equals(TEXT_SEGMENT)
				|| type.equals(USERS_NOTE)
				|| type.equals(GSQUERY)
				|| type.equals(SHORTCUT);
	}
	public static String getLast(String arg) {
		if (rest(arg).equals("")) {
			return first(arg);
		} else {
			return getLast(rest(arg));
		}
	}
	// chop all the chars after the last to the second occurrence of  '/' not including itself.
	// arg is supposed to ends with '/'
	// e.g. .../aaa/ -> .../
	public static String chopLast(String arg) {
		String token[] = arg.split(SEP1);
		if (token.length < 2) return null;
		String ans = new String();
		for (int i = 0; i < token.length-1;i++) {
			ans += token[i] + "/";
		}
		return ans;
	}
	
	// chop all the chars after the last to the third occurrence of  '/' not including itself.
	// arg is supposed to ends with '/'
	// e.g. .../aaa/bbb/ -> .../
	static String chopLastTwo(String arg) {
		String token[] = arg.split(SEP1);
		if (token.length < 3) return null;
		String ans = new String();
		for (int i = 0; i < token.length-2;i++) {
			ans += token[i] + "/";
		}
		return ans;
	}
	
	public String getType() {
		return this.type;
	}
	public static String getUsersName() {
		String name = Preference.getInstance().getUserName();
		if (name == null || name == "") {
			return ANONYMOUS_USER;
		} else {
			return modifyName(name);
		}		
	}
	private static String getAdditionalExtension(String date) {
		String s = "";
		if (date.equals(LAST_CREATED_DATE)) {
			ADDITIONAL_EXTENSION++;
			s = "(" + Integer.toString(ADDITIONAL_EXTENSION) + ")";
		} else {
			ADDITIONAL_EXTENSION = 0;
		}
		return s;
	}

	public static String getDate() {
		Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
		cal.setTime(new Date());
		return getDateFrom(cal);
	}
	public static String getDateFrom(Calendar cal) {
		String y = Integer.toString(cal.get(Calendar.YEAR));
		String m = adjust(cal.get(Calendar.MONTH) + 1);
		String d = adjust(cal.get(Calendar.DATE));
		String h = adjust(cal.get(Calendar.HOUR_OF_DAY));
		String min = adjust(cal.get(Calendar.MINUTE));
		String sec = adjust(cal.get(Calendar.SECOND));
		return y + m + d + h + min + sec;
	}
	public static Calendar getCalendarFrom(String gmtTime) {
		Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
		int year = new Integer(gmtTime.substring(0, 4));
		int month = new Integer(gmtTime.substring(4, 6))-1;
		int date = new Integer(gmtTime.substring(6, 8));
		int hourOfDay = new Integer(gmtTime.substring(8, 10));
		int minute = new Integer(gmtTime.substring(10, 12));
		int second = new Integer(gmtTime.substring(12, 14));
		cal.set(year, month, date, hourOfDay, minute, second);
		return cal;
	}
	public static Calendar getLocalCalendar(TimeZone timeZone,Calendar cal) {
		Date utc = cal.getTime();
		Calendar localCal = Calendar.getInstance(timeZone);
		localCal.setTime(utc);
		return localCal;
	}
	public static String localizeDate(TimeZone timeZone, String gmtTime) {
		Calendar cal = URIObject.getCalendarFrom(gmtTime);
		Calendar localCal = URIObject.getLocalCalendar(timeZone, cal);
		return URIObject.getDateFrom(localCal);
	}

	private static String adjust(int x) {
		if (x < 10) {
			return "0" + Integer.toString(x);
		} else {
			return Integer.toString(x);
		}
	}
	public static final String modifyName(String name) {
		if (name.equals("")) {
			return "UNNAMED";
		} else {
			return name;
		}
	}
	public static final String getURIWithoutHeader(RWElement elm) {
		return elm.getURI().substring(HEADER.length());
	}
	public static final boolean isARegion(String type) {
		return type.equals(RECTANGLE) ||
			type.equals(MARKER) ||
			type.equals(POLYGON) ||
			type.equals(MEMOPAD) ||
			type.equals(BOOKMARK) ||
			type.equals(LINE);
	}
	public static final boolean isAnIDAT(String type) {
		return type.equals(FIRST_IDAT) ||
			type.equals(SECOND_IDAT) ||
			type.equals(THIRD_IDAT);
	}
	
	public static boolean isATextSegmentURI(String uri) {
		return uri.startsWith(HEADER + TEXT_SEGMENT);
	}
	public static boolean isATextSegmentFromUsersNote(String uri) {
		return isATextSegmentURI(uri) && uri.contains(SEP2 + USERS_NOTE) && !uri.contains(SEP2 + EXPLANATORY_NOTE);
	}

	public static boolean isATextSegmentFromFirstIDAT(String uri) {
		return isATextSegmentURI(uri) && uri.contains(SEP2 + FIRST_IDAT) && !uri.contains(SEP2 + EXPLANATORY_NOTE);
	}

	public static boolean isATextSegmentFromSecondIDAT(String uri) {
		return isATextSegmentURI(uri) && uri.contains(SEP2 + SECOND_IDAT) && !uri.contains(SEP2 + EXPLANATORY_NOTE);
	}

	public static boolean isATextSegmentFromThirdIDAT(String uri) {
		return isATextSegmentURI(uri) && uri.contains(SEP2 + THIRD_IDAT) && !uri.contains(SEP2 + EXPLANATORY_NOTE);
	}
	
	public static boolean isATextSegmentFromExplanatoryNote(String uri) {
		return isATextSegmentURI(uri) && uri.contains(SEP2 + EXPLANATORY_NOTE);		
	}

	public void setContainer(URIObject uriObj) {
		this.container = uriObj;
	}
	
}