package org.rhq.enterprise.gui.coregui.client.util.selenium;

import com.google.gwt.user.client.ui.UIObject;
import com.smartgwt.client.widgets.BaseWidget;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.layout.Layout;

import org.rhq.enterprise.gui.coregui.client.CoreGUI;
import org.rhq.enterprise.gui.coregui.client.util.message.Message;
import org.rhq.enterprise.gui.coregui.client.util.message.Message.Severity;

/**
 * Utilities for assisting with Selenium Automation
 * 
 * @author Jay Shaughnessy
 */
public class SeleniumUtility {

    /** A default id that is not ecommended as it will clash with any other element set to the default */
    public static final String DEFAULT_ID = "DefaultID";

    /** 
     * When true ignore any explicit locatorId assignment and use the standard
     * ids generated by smartgwt.
     */
    private static boolean useDefaultIds = true;

    /**
     * A utility for assigning an ID to a smartgwt widget. Any current ID will be overwritten.  The algorithm is:
     * <pre>
     * ID Format: "simpleClassname_locatorId"
     * </pre>
     * @return the updated widget
     */
    static public <T extends BaseWidget> T setID(final T widget, String locatorId) {
        if (useDefaultIds) {
            return widget;
        }

        String unsafeId = getSimpleClassName(widget) + "_" + locatorId;
        String safeId = SeleniumUtility.getSafeId(unsafeId, DEFAULT_ID);
        Canvas canvasWithId = Canvas.getById(safeId);
        if (null != canvasWithId) {
            try {
                canvasWithId.destroy();
                CoreGUI.getMessageCenter().notify(
                    new Message("ID Conflict resolved: " + safeId, getSmallStackTrace(null), Severity.Warning));
            } catch (Throwable t) {
                CoreGUI.getMessageCenter().notify(
                    new Message("ID Conflict unresolved: " + getSmallStackTrace(t), Severity.Info));
            }
        }
        widget.setID(safeId);

        return widget;
    }

    static public String getSimpleClassName(final Object widget) {
        String className = widget.getClass().getName();
        return className.substring(className.lastIndexOf(".") + 1);
    }

    static private String getSmallStackTrace(Throwable t) {
        StringBuilder smallStack = new StringBuilder();

        StackTraceElement[] stack = (null == t) ? new Exception().getStackTrace() : t.getStackTrace();
        for (int i = 1; i < stack.length; i++) {
            StackTraceElement ste = stack[i];
            if (ste.getClassName().startsWith("org.rhq")) {
                smallStack.append(ste.toString());
                smallStack.append("\n");
            }
        }
        return smallStack.toString();
    }

    /**
     * A utility for assigning an ID to a gwt uiobject.  For a smartgwt widget use {@link setId(BaseWidget)}.
     * Any current ID will be overwritten.  The algorithm is:
     * <pre>
     * If    the unsafeId parameter is non-empty, the ID is set to the unsafeId, with spaces removed.
     * Else  the ID is set to the uiobject's hashcode
     * </pre>
     * 
     * @param uiObject
     * @param unsafeId
     * @return the updated uiObject
     */
    static public <T extends UIObject> T setHtmlId(final T uiObject, String unsafeId) {
        if (useDefaultIds) {
            return uiObject;
        }

        String id = getSafeId(unsafeId, String.valueOf(uiObject.hashCode()));
        uiObject.getElement().setAttribute("id", id);

        return uiObject;
    }

    /**
     * Use only if you are sure the unsafeId is not null or empty, or if DEFAULT_ID is acceptable.  
     * <pre>
     * Equivalent to:
     * 
     *   getSafeId( unsafeId, DEFAULT_ID );
     * </pre>
     * @param unsafeId The desired Id but with potential problems
     * @return a safe version of unsafeId, or, if unsafeId is null or empty, DEFAULT_ID
     */
    static public String getSafeId(String unsafeId) {
        return getSafeId(unsafeId, DEFAULT_ID);
    }

    /** 
     * Currently performs the following:
     * <pre>
     * - removes spaces
     * - removes dots
     * - converts '-' to '_'
     * </pre>
     * @param unsafeId The desired Id but with potential problems
     * @param defaultId
     * @return a safe version of unsafeId, or, if unsafeId is null or empty, defaultId
     */
    static public String getSafeId(String unsafeId, String defaultId) {
        if ((null == unsafeId || unsafeId.trim().isEmpty())) {
            return defaultId;
        }

        String safeId = unsafeId.replace(" ", "").replace(".", "").replace("-", "_");
        return safeId;
    }

    static public boolean isUseDefaultIds() {
        return useDefaultIds;
    }

    /**
     * Like Layout.removeMembers() but ensures a synchronous destroy of each member.
     * @param <T>
     * @param layout
     */
    static public <T extends Layout> void destroyMembers(T layout) {
        if (null == layout) {
            return;
        }

        Canvas[] members = layout.getMembers();
        if (null == members) {
            return;
        }

        for (Canvas currentMember : members) {
            layout.removeMember(currentMember);
            currentMember.destroy();
        }
    }

    public static boolean getUseDefaultIds() {
        return SeleniumUtility.useDefaultIds;
    }

    public static void setUseDefaultIds(boolean useDefaultIds) {
        SeleniumUtility.useDefaultIds = useDefaultIds;
    }

    /** 
     * Return a locatable href element for locatable links: The return id will be: 
     * <pre>
     *   value[_suffix]
     *   
     *   Unsafe characters will be removed or converted as per getSafeId().  The "_suffix" is
     *   added only if a non-null suffix is supplied. The suffix can be used a discriminator, if necessary,
     *   for unique ids among duplicated values.
     * </pre>
     * 
     * @param url the target url
     * @param value the display value for the link
     * @param suffix optional discriminator
     * @return
     */
    static public String getLocatableHref(String url, String value, String suffix) {
        String safeId = getSafeId(value + ((null == suffix) ? "" : ("_" + suffix)));
        String result = "<a id=\"" + safeId + "\" href=\"" + url + "\">" + value + "</a>";
        return result;
    }

}
