package com.logicaldoc.gui.common.client.util;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.gwt.core.client.GWT;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.Timer;
import com.logicaldoc.gui.common.client.Constants;
import com.logicaldoc.gui.common.client.CookiesManager;
import com.logicaldoc.gui.common.client.Feature;
import com.logicaldoc.gui.common.client.Session;
import com.logicaldoc.gui.common.client.beans.GUIInfo;
import com.logicaldoc.gui.common.client.beans.GUIParameter;
import com.logicaldoc.gui.common.client.i18n.I18N;
import com.logicaldoc.gui.common.client.log.Log;
import com.logicaldoc.gui.common.client.widgets.ToastNotification;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.RecordList;
import com.smartgwt.client.types.ListGridFieldType;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;

public class Util {
	public static String[] OFFICE_EXTS = new String[] { ".doc", ".xls", ".xlsm", ".ppt", ".docx", ".docxm", ".xlsx",
			".xlsm", ".pptx", ".rtf", ".odt", ".ods", ".odp" };

	public static String[] IMAGE_EXTS = new String[] { ".gif", ".jpg", ".jpeg", ".bmp", ".tif", ".tiff", ".png", ".jfif" };

	public static String[] VIDEO_EXTS = new String[] { ".mp4", ".avi", ".mpg", ".wmv", ".wma", ".asf", ".mov", ".rm",
			".flv", ".aac", ".vlc", ".ogg", ".webm", ".swf", ".mpeg", ".swf", ".m2v", ".m2ts", ".mkv" };

	public static String[] AUDIO_EXTS = new String[] { ".mp3", ".m4p", ".m4a", ".wav" };

	public static String[] WEBCONTENT_EXTS = new String[] { ".html", ".htm", ".xhtml" };

	public static String[] EMAIL_EXTS = new String[] { ".eml", ".msg" };

	public static String[] DICOM_EXTS = new String[] { ".dcm", ".dicom" };

	/**
	 * Generates HTML image code with style.
	 * 
	 * @param imageName the name of the icon image
	 * @param alt the image alt
	 * @param style CSS style specification
	 * 
	 * @return the resultant HTML
	 */
	public static String imageHTML(String imageName, String alt, String style) {
		return "<img border=\"0\" align=\"absmidle\" alt=\"" + (alt != null ? alt : "") + "\" title=\""
				+ (alt != null ? alt : "") + "\"" + (style != null ? "style='" + style + "'" : "") + " src='"
				+ Util.imageUrl(imageName) + "' />";
	}

	public static String imageHTML(String imageName, Integer width, Integer height, String style) {
		String html = "<img border='0' alt='' title='' src='" + Util.imageUrl(imageName) + "' ";
		if (width != null)
			html += " width='" + width + "px' ";
		if (height != null)
			html += " height='" + height + "px' ";
		if (style != null)
			html += " style='" + style + "' ";
		html += " />";
		return html;
	}

	public static String downloadAttachmentURL(long docId, String fileVersion, String attachmentFileName) {
		String url = contextPath() + "download-attachment?docId=" + docId;
		if (fileVersion != null)
			url += "&fileVersion=" + fileVersion;
		if (attachmentFileName != null)
			url += "&attachmentFileName=" + URL.encode(attachmentFileName);
		return url;
	}

	public static String downloadURL(long docId, String fileVersion, String suffix, boolean open) {
		String url = contextPath() + "download?docId=" + docId;
		if (fileVersion != null)
			url += "&fileVersion=" + fileVersion;
		if (suffix != null)
			url += "&suffix=" + suffix;
		if (open)
			url += "&open=true";
		return url;
	}

	public static String downloadURL(long docId, String fileVersion, boolean open) {
		return downloadURL(docId, fileVersion, null, open);
	}

	public static String downloadURL(long docId, String fileVersion) {
		return downloadURL(docId, fileVersion, false);
	}

	public static String downloadURL(long docId) {
		return downloadURL(docId, null, false);
	}

	public static String displayURL(Long docId, Long folderId) {
		String url = contextPath() + "display?";
		if (docId != null)
			url += Constants.DOC_ID + "=" + docId;
		else
			url += Constants.FOLDER_ID + "=" + folderId;
		return url;
	}

	public static String webEditorUrl(long docId, String fileName, int height) {
		String url = contextPath() + "ckeditor/index.jsp?docId=" + docId + "&lang=" + I18N.getLocale() + "&fileName="
				+ fileName + "&height=" + height + "&sid=" + Session.get().getSid();
		return url;
	}

	public static String webEditorUrl(int height) {
		String url = contextPath() + "ckeditor/index.jsp?docId=nodoc&lang=" + I18N.getLocale() + "&height=" + height
				+ "&sid=" + Session.get().getSid();
		return url;
	}

	public static String webstartURL(String appName, Map<String, String> params) {
		StringBuffer url = new StringBuffer(GWT.getHostPageBaseURL());
		url.append("webstart/");
		url.append(appName);
		url.append(".jsp?random=");
		url.append(new Date().getTime());
		url.append("&language=");
		url.append(I18N.getLocale());
		url.append("&docLanguage=");
		url.append(I18N.getDefaultLocaleForDoc());
		url.append("&sid=");
		url.append(Session.get().getSid());
		if (params != null)
			for (String p : params.keySet()) {
				url.append("&");
				url.append(p);
				url.append("=");
				url.append(URL.encode(params.get(p)));
			}
		return url.toString();
	}

	/**
	 * Generates HTML code for reproducing video files
	 * 
	 * @param mediaUrl URL of the media file to reproduce
	 * @param width width specification
	 * @param height height specification
	 * 
	 * @return the HTML content
	 */
	public static String videoHTML(String mediaUrl, String width, String height) {
		String tmp = "<video controls ";
		if (width != null)
			tmp += "width='" + width + "' ";
		if (height != null)
			tmp += "height='" + height + "' ";
		tmp += ">";
		tmp += "<source src='" + mediaUrl + "' />";
		tmp += "</video>";
		return tmp;
	}

	/**
	 * Generates HTML code for reproducing audio files
	 * 
	 * @param mediaUrl URL of the media file to reproduce
	 * 
	 * @return the HTML content
	 */
	public static String audioHTML(String mediaUrl) {
		String tmp = "<audio style='margin-top: 20px; vertical-align: middle; text-align: center' controls >";
		tmp += "<source src='" + mediaUrl + "' />";
		tmp += "</audio>";
		return tmp;
	}

	public static String thumbnailUrl(long docId, String fileVersion) {
		String url = GWT.getHostPageBaseURL() + "thumbnail?docId=" + docId + "&random=" + new Date().getTime();
		if (fileVersion != null)
			url += "&fileVersion=" + fileVersion;
		return url;
	}

	public static String thumbnailImgageHTML(long docId, String fileVersion, Integer width, Integer height) {
		String style = "";
		if (width != null)
			style += "width:" + width + "px; ";
		if (height != null)
			style += "height:" + height + "px; ";

		String img = "<img src='" + thumbnailUrl(docId, fileVersion) + "' style='" + style + "' />";
		return img;
	}

	public static String tileUrl(long docId, String fileVersion) {
		return thumbnailUrl(docId, fileVersion) + "&suffix=tile.jpg";
	}

	public static String tileImageHTML(long docId, String fileVersion, Integer width, Integer height) {
		String style = "";
		if (width != null)
			style += "width:" + width + "px; ";
		if (height != null)
			style += "height:" + height + "px; ";

		String img = "<img src='" + tileUrl(docId, fileVersion) + "' style='" + style + "' />";
		return img;
	}

	public static String imageUrl(String imageName) {
		return imagePrefix() + imageName;
	}

	public static String licenseUrl() {
		return contextPath() + "license";
	}

	public static String websocketUrl() {
		String url = contextPath() + "wk-event";
		return "ws" + url.substring(url.indexOf(':'));
	}

	public static String strip(String src) {
		if (src == null)
			return null;
		else
			return src.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
	}

	public static String contextPath() {
		String url = GWT.getModuleBaseURL().replace(GWT.getModuleName() + "/", "");
		if (!url.endsWith("/"))
			url += "/";
		return url;
	}

	private static String currentSkin() {
		String skin = null;
		try {
			if (Session.get() != null && Session.get().getInfo() != null
					&& Session.get().getInfo().getBranding() != null)
				skin = Session.get().getInfo().getBranding().getSkin();
			if (skin == null)
				skin = detectSkin();
		} catch (Throwable t) {

		}

		if (skin == null)
			skin = "Simplicity";
		return skin;
	}

	public static String imagePrefix() {
		String base = GWT.getModuleBaseURL();
		if (!base.endsWith("/"))
			base = base + "/";
		return base + "sc/skins/" + currentSkin() + "/images/";
	}

	/**
	 * Generates HTML image code.
	 * 
	 * @param imageName the image name
	 * 
	 * @return the resultant HTML
	 */
	public static String imageHTML(String imageName) {
		return imageHTML(imageName, "", null);
	}

	public static boolean isCommunity() {
		return !Feature.enabled(Feature.ADDITIONAL_FORMATS);
	}

	public static boolean isOfficeFile(String fileName) {
		String tmp = fileName.toLowerCase();
		for (String ext : OFFICE_EXTS) {
			if (tmp.endsWith(ext))
				return true;
		}
		return false;
	}

	public static boolean isDICOMFile(String fileName) {
		String tmp = fileName.toLowerCase();
		for (String ext : DICOM_EXTS) {
			if (tmp.endsWith(ext))
				return true;
		}
		return false;
	}

	public static boolean isTextFile(String fileName) {
		String[] exts = new String[] { "txt" };
		String extensions = Session.get().getConfig("gui.text.extensions");
		if (extensions != null && !extensions.isEmpty()) {
			exts = extensions.trim().toLowerCase().split(",");
			for (int i = 0; i < exts.length; i++) {
				exts[i] = exts[i].trim();
				if (!exts[i].startsWith("."))
					exts[i] = "." + exts[i];
			}
		}

		String tmp = fileName.toLowerCase();
		for (String ext : exts) {
			if (tmp.endsWith(ext))
				return true;
		}
		return false;
	}

	public static boolean isImageFile(String fileName) {
		String tmp = fileName.toLowerCase();
		for (String ext : IMAGE_EXTS) {
			if (tmp.endsWith(ext))
				return true;
		}
		return false;
	}

	public static boolean isWebContentFile(String fileName) {
		String tmp = fileName.toLowerCase();
		for (String ext : WEBCONTENT_EXTS) {
			if (tmp.endsWith(ext))
				return true;
		}
		return false;
	}

	public static boolean isMediaFile(String fileName) {
		String tmp = fileName.toLowerCase();
		for (String ext : VIDEO_EXTS) {
			if (tmp.endsWith(ext))
				return true;
		}
		for (String ext : AUDIO_EXTS) {
			if (tmp.endsWith(ext))
				return true;
		}
		return false;
	}

	public static boolean isAudioFile(String fileName) {
		String tmp = fileName.toLowerCase();
		for (String ext : AUDIO_EXTS) {
			if (tmp.endsWith(ext))
				return true;
		}
		return false;
	}

	public static boolean isOfficeFileType(String type) {
		for (String ext : OFFICE_EXTS) {
			if (type.equalsIgnoreCase(ext))
				return true;
		}
		return false;
	}

	public static boolean isEmailFile(String fileName) {
		String tmp = fileName.toLowerCase();
		for (String ext : EMAIL_EXTS) {
			if (tmp.endsWith(ext))
				return true;
		}
		return false;
	}

	/**
	 * Format file size in Bytes, KBytes, MBytes or GBytes.
	 * 
	 * @param size The file size in bytes.
	 * @return The formated file size.
	 */
	public static native String formatSize(double size) /*-{
		if (size / 1024 < 1) {
			str = size + " Bytes";
		} else if (size / 1048576 < 1) {
			str = (size / 1024).toFixed(1) + " KBytes";
		} else if (size / 1073741824 < 1) {
			str = (size / 1048576).toFixed(1) + " MBytes";
		} else {
			str = (size / 1073741824).toFixed(1) + " GBytes";
		}
		return str;
	}-*/;

	/**
	 * Format file size in Bytes, KB, MB, GB.
	 * 
	 * @param size The file size in bytes.
	 * @return The formated file size.
	 */
	public static native String formatSizeCompact(double size) /*-{
		if (size / 1024 < 1) {
			str = size + " Bytes";
		} else if (size / 1048576 < 1) {
			str = (size / 1024).toFixed(1) + " KB";
		} else if (size / 1073741824 < 1) {
			str = (size / 1048576).toFixed(1) + " MB";
		} else {
			str = (size / 1073741824).toFixed(1) + " GB";
		}
		return str;
	}-*/;

	public static String formatLong(long number) {
		String str;
		NumberFormat fmt = NumberFormat.getFormat("#,###");
		str = fmt.format(number);
		str = str.replace(',', I18N.groupingSepator());
		return str;
	}

	public static String formatSizeKB(Object value) {
		if (value == null)
			return null;
		if (value instanceof Double)
			return Util.formatSizeKB(((Double) value).doubleValue());
		if (value instanceof Long)
			return Util.formatSizeKB(((Long) value).doubleValue());
		else if (value instanceof Integer)
			return Util.formatSizeKB(((Integer) value).doubleValue());
		if (value instanceof String)
			return Util.formatSizeKB(Long.parseLong(value.toString()));
		else
			return Util.formatSizeKB(0L);
	}

	/**
	 * Format file size in KB.
	 * 
	 * @param size The file size in bytes.
	 * 
	 * @return The formated file size.
	 */
	public static String formatSizeKB(double size) {
		String str;
		if (size < 1) {
			str = "0 KB";
		} else if (size < 1024) {
			str = "1 KB";
		} else {
			NumberFormat fmt = NumberFormat.getFormat("#,###");
			str = fmt.format(Math.ceil(size / 1024)) + " KB";
			str = str.replace(',', I18N.groupingSepator());
		}
		return str;
	}

	/**
	 * Format file size in Windows 7 Style.
	 * 
	 * @param value The file size in bytes(can be Float, Long, Integer or
	 *        String)
	 * 
	 * @return The formated file size
	 */
	public static String formatSizeW7(Object value) {
		if (value == null)
			return null;
		if (value instanceof Float)
			return Util.formatSizeKB(((Float) value).doubleValue());
		if (value instanceof Long)
			return Util.formatSizeW7(((Long) value).doubleValue());
		else if (value instanceof Integer)
			return Util.formatSizeW7(((Integer) value).doubleValue());
		else if (value instanceof Float)
			return Util.formatSizeW7(((Float) value).doubleValue());
		if (value instanceof String)
			return Util.formatSizeW7(Long.parseLong(value.toString()));
		else
			return Util.formatSizeW7(0L);
	}

	/**
	 * Format file size in Windows 7 Style.
	 * 
	 * @param size The file size in bytes.
	 * 
	 * @return The formated file size.
	 */
	public static String formatSizeW7(double size) {
		if (size < 0)
			return "";

		double KB = 1024;
		double MB = 1024 * KB;
		double GB = 1024 * MB;
		double TB = 1024 * GB;

		String str;
		if (size < 1) {
			str = "0 bytes";
		} else if (size < KB) {
			str = size + " bytes";
		} else if (size < MB) {
			double tmp = size / KB;
			if (tmp < 10) {
				NumberFormat fmt = NumberFormat.getFormat("###.##");
				str = fmt.format(tmp) + " KB";
			} else if (tmp < 100) {
				NumberFormat fmt = NumberFormat.getFormat("###.#");
				str = fmt.format(tmp) + " KB";
			} else {
				NumberFormat fmt = NumberFormat.getFormat("###");
				str = fmt.format(tmp) + " KB";
			}
			str = str.replace('.', I18N.decimalSepator());
		} else if (size < GB) {
			double tmp = size / MB;
			if (tmp < 10) {
				NumberFormat fmt = NumberFormat.getFormat("###.##");
				str = fmt.format(tmp) + " MB";
			} else if (tmp < 100) {
				NumberFormat fmt = NumberFormat.getFormat("###.#");
				str = fmt.format(tmp) + " MB";
			} else {
				NumberFormat fmt = NumberFormat.getFormat("###");
				str = fmt.format(tmp) + " MB";
			}
			str = str.replace('.', I18N.decimalSepator());
		} else if (size < TB) {
			double tmp = size / GB;
			if (tmp < 10) {
				NumberFormat fmt = NumberFormat.getFormat("###.##");
				str = fmt.format(tmp) + " GB";
			} else if (tmp < 100) {
				NumberFormat fmt = NumberFormat.getFormat("###.#");
				str = fmt.format(tmp) + " GB";
			} else {
				NumberFormat fmt = NumberFormat.getFormat("###");
				str = fmt.format(tmp) + " GB";
			}
			str = str.replace('.', I18N.decimalSepator());
		} else {
			double tmp = size / TB;
			if (tmp < 10) {
				NumberFormat fmt = NumberFormat.getFormat("###.##");
				str = fmt.format(tmp) + " TB";
			} else if (tmp < 100) {
				NumberFormat fmt = NumberFormat.getFormat("###.#");
				str = fmt.format(tmp) + " TB";
			} else {
				NumberFormat fmt = NumberFormat.getFormat("###");
				str = fmt.format(tmp) + " TB";
			}
			str = str.replace('.', I18N.decimalSepator());
		}
		return str;
	}

	/**
	 * Format file size in bytes
	 * 
	 * @param size The file size in bytes
	 * 
	 * @return the formatted size
	 */
	public static String formatSizeBytes(double size) {
		String str;
		NumberFormat fmt = NumberFormat.getFormat("#,###");
		str = fmt.format(size) + " bytes";
		str = str.replace(',', I18N.groupingSepator());
		return str;
	}

	/**
	 * Format number percentage.
	 * 
	 * @param value The value to be formatted.
	 * @param fixed The number of decimal places.
	 * @return The formated value.
	 */
	public static native String formatPercentage(double value, int fixed) /*-{
		str = value.toFixed(fixed);
		return str + "%";
	}-*/;

	/**
	 * Get browser language
	 * 
	 * @return The language in ISO 639 format.
	 */
	public static native String getBrowserLanguage() /*-{
		var lang = window.navigator.language ? window.navigator.language
				: window.navigator.userLanguage;
		if (lang != null && lang != "") {
			return lang.replace('-', '_');
		} else {
			return "en";
		}
	}-*/;

	/**
	 * Detects the user agent(browser's family)
	 *
	 * @return 'opera', 'safari', 'ie6', 'ie7', 'gecko', or 'unknown'
	 */
	public static native String getUserAgent() /*-{
		try {
			if (window.opera)
				return 'opera';
			var ua = navigator.userAgent.toLowerCase();
			if (ua.indexOf('webkit') != -1)
				return 'safari';
			if (ua.indexOf('msie 6.0') != -1)
				return 'ie6';
			if (ua.indexOf('msie 7.0') != -1)
				return 'ie7';
			if (ua.indexOf('gecko') != -1)
				return 'gecko';
			return 'unknown';
		} catch (e) {
			return 'unknown'
		}
	}-*/;

	public static native void copyToClipboard(String text) /*-{
		new $wnd.copyToClipboard(text);
	}-*/;

	public static native boolean isValidEmail(String email) /*-{
		var reg1 = /(@.*@)|(\.\.)|(@\.)|(\[email protected])|(^\.)/; // not valid
		var reg2 = /^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,3}|[0-9]{1,3})(\]?)$/; // valid
		return !reg1.test(email) && reg2.test(email);
	}-*/;

	public static native void redirect(String url)
	/*-{
		$wnd.location.replace(url);
	}-*/;

	public static String padLeft(String s, int n) {
		if (s.length() > n) {
			return s.substring(0, n - 3) + "...";
		} else
			return s;
	}

	private static boolean isWebstartMode() {
		return WindowUtils.isChrome()
				|| (WindowUtils.isWindows() && "webstart".equals(Session.get().getConfig("gui.webstart.mode")));
	}

	public static void openScan() {
		if (!WindowUtils.isWindows())
			return;

		Map<String, String> params = new HashMap<String, String>();
		params.put("targetFolderId", "" + Session.get().getCurrentFolder().getId());
		String url = Util.webstartURL("scan", params);

		openWebstartApp(url);
	}

	public static void openBulkCheckout(List<Long> unlockedIds) {
		Map<String, String> params = new HashMap<String, String>();
		params.put("targetFolderId", "" + Session.get().getCurrentFolder().getId());
		params.put("docIds", unlockedIds.toString().replace('[', ' ').replace(']', ' ').trim());
		String url = Util.webstartURL("bulkcheckout", params);

		openWebstartApp(url);
	}

	private static void openWebstartApp(String url) {
		if (isWebstartMode()) {
			url = url.replace("=", "_x_");
			url = url.replace("&", "_y_");
			url = url.replace(",", "_z_");
			WindowUtils.openUrl("ldwebstart:" + url);
			ToastNotification.showNotification(I18N.message("webstarthintlauncher"));
		} else {
			WindowUtils.openUrl(url, "_self");
			ToastNotification.showNotification(I18N.message("webstarthint", url));
		}
	}

	/**
	 * Exports into the CSV format the content of a ListGrid.
	 * 
	 * @param listGrid Grid containing the data
	 * @param allFields True if all the fields(even if hidden) have to be
	 *        extracted
	 */
	public static void exportCSV(ListGrid listGrid, boolean allFields) {
		StringBuilder stringBuilder = new StringBuilder(); // csv data in here

		// column names
		ListGridField[] fields = listGrid.getFields();
		if (allFields)
			fields = listGrid.getAllFields();
		for (int i = 0; i < fields.length; i++) {
			ListGridField listGridField = fields[i];
			if (listGridField.getType().equals(ListGridFieldType.ICON)
					|| listGridField.getType().equals(ListGridFieldType.IMAGE)
					|| listGridField.getType().equals(ListGridFieldType.IMAGEFILE)
					|| listGridField.getType().equals(ListGridFieldType.BINARY) || "".equals(listGridField.getTitle())
					|| "&nbsp;".equals(listGridField.getTitle()))
				continue;

			stringBuilder.append("\"");
			stringBuilder.append(listGridField.getTitle());
			stringBuilder.append("\";");
		}
		stringBuilder.deleteCharAt(stringBuilder.length() - 1); // remove last
																// ";"
		stringBuilder.append("\n");

		// column data
		Record[] records = new Record[0];
		try {
			records = listGrid.getRecords();
		} catch (Throwable t) {
		}

		if (records == null || records.length < 1) {
			/*
			 * In case of data bound grid, we need to call th