package com.google.gwt.user.client.impl;

import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.HandlerRegistration;

/**
 * Native implementation associated with
 * {@link com.google.gwt.user.client.History}. User classes should not use this
 * class directly.
 * 
 * <p>
 * This base version uses the HTML5 standard window.onhashchange event to
 * determine when the URL hash identifier changes.
 * </p>
 */
public class HistoryImpl implements HasValueChangeHandlers<String> {

  private static String token = "";

  public static String getToken() {
    return (token == null) ? "" : token;
  }

  /**
   * Sets whether the IE6 history implementation will update the URL hash when
   * creating a new item. This should be used only for applications with large
   * DOM structures that are suffering from performance problems when creating a
   * new history item on IE6 and 7.
   * 
   * @deprecated This is no longer necessary, as the underlying performance
   *             problem has been solved. It is now a no-op.
   */
  @Deprecated
  public static void setUpdateHashOnIE6(boolean updateHash) {
  }

  protected static void setToken(String token) {
    HistoryImpl.token = token;
  }

  private HandlerManager handlers = new HandlerManager(null);

  /**
   * Adds a {@link ValueChangeEvent} handler to be informed of changes to the
   * browser's history stack.
   * 
   * @param handler the handler
   */
  public HandlerRegistration addValueChangeHandler(
      ValueChangeHandler<String> handler) {
    return handlers.addHandler(ValueChangeEvent.getType(), handler);
  }

  public native String encodeFragment(String fragment) /*-{
    // encodeURI() does *not* encode the '#' character.
    return encodeURI(fragment).replace("#", "%23");
  }-*/;

  public void fireEvent(GwtEvent<?> event) {
    handlers.fireEvent(event);
  }

  /**
   * Fires the {@link ValueChangeEvent} to all handlers with the given tokens.
   */
  public void fireHistoryChangedImpl(String newToken) {
    ValueChangeEvent.fire(this, newToken);
  }

  public HandlerManager getHandlers() {
    return handlers;
  }
  
  public static String hash = "";
  
  public static HistoryImpl historyImpl = null;
  
  public boolean init() {
	  String token = "";
	  if (hash.length() > 0) {
		  token = decodeFragment(token);
	  }
	  
	  setToken(token);
	  
	  historyImpl = this;
	  
	  return true;
  }
  
  public static void onHashChanged() {
	  if (hash.length() > 0) {
		  token = historyImpl.decodeFragment(hash);
	  }
	  historyImpl.newItemOnEvent(token);
  }

  public native boolean init2() /*-{
//    var token = '';
//
//    // Get the initial token from the url's hash component.
//    var hash = $wnd.location.hash;
//    if (hash.length > 0) {
//      token = this.@com.google.gwt.user.client.impl.HistoryImpl::decodeFragment(Ljava/lang/String;)(hash.substring(1));
//    }
//
//    @com.google.gwt.user.client.impl.HistoryImpl::setToken(Ljava/lang/String;)(token);
//
//    var historyImpl = this;
//
//    var oldHandler = $wnd.onhashchange;
//
//    $wnd.onhashchange = $entry(function() {
//      var token = '', hash = $wnd.location.hash;
//      if (hash.length > 0) {
//        token = historyImpl.@com.google.gwt.user.client.impl.HistoryImpl::decodeFragment(Ljava/lang/String;)(hash.substring(1));
//      }
//
//      historyImpl.@com.google.gwt.user.client.impl.HistoryImpl::newItemOnEvent(Ljava/lang/String;)(token);
//
//      if (oldHandler) {
//        oldHandler();
//      }
//    });

    return true;
  }-*/;

  public final void newItem(String historyToken, boolean issueEvent) {
    historyToken = (historyToken == null) ? "" : historyToken;
    if (!historyToken.equals(getToken())) {
      setToken(historyToken);
      nativeUpdate(historyToken);
      if (issueEvent) {
        fireHistoryChangedImpl(historyToken);
      }
    }
  }

  public final void newItemOnEvent(String historyToken) {
    historyToken = (historyToken == null) ? "" : historyToken;
    if (!historyToken.equals(getToken())) {
      setToken(historyToken);
      nativeUpdateOnEvent(historyToken);
      fireHistoryChangedImpl(historyToken);
    }
  }

  protected native String decodeFragment(String encodedFragment) /*-{
    // decodeURI() does *not* decode the '#' character.
    return decodeURI(encodedFragment.replace("%23", "#"));
  }-*/;

  /**
   * The standard updateHash implementation assigns to location.hash() with an
   * encoded history token.
   */
  protected native void nativeUpdate(String historyToken) /*-{
    $wnd.location.hash = this.@com.google.gwt.user.client.impl.HistoryImpl::encodeFragment(Ljava/lang/String;)(historyToken);
  }-*/;

  protected void nativeUpdateOnEvent(String historyToken) {
    // Do nothing, the hash is already updated.
  }
}