/*
 * Copyright 2011 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.uibinder.test.client;

import com.google.gwt.dom.builder.client.DomBuilderFactory;
import com.google.gwt.dom.builder.shared.DivBuilder;
import com.google.gwt.dom.builder.shared.ElementBuilderBase;
import com.google.gwt.dom.builder.shared.ElementBuilderFactory;
import com.google.gwt.dom.builder.shared.HtmlBuilderFactory;
import com.google.gwt.dom.builder.shared.HtmlElementBuilderBase;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.IsRenderable;
import com.google.gwt.user.client.ui.PotentialElement;
import com.google.gwt.user.client.ui.RenderableStamper;
import com.google.gwt.user.client.ui.Widget;

/**
 * Used by {@link IsRenderableIntegrationTest}. A simple implementation of
 * IsRenderable. Note that the actual building for both string based and dom
 * based versions happens in one place, {@link #build(ElementBuilderFactory)}.
 * <p>
 * Note also that that the widget's contents are simply re-rendered when the
 * widget's state changes, in this case in {@link #setText}. This is a bad idea
 * for large widgets, but for little leaf widgets like this one is probably just
 * fine.
 * <p>
 * It would probably be a good refactor a production super class out of this
 * once the IsRenderable interface settles down.
 * <p>
 * It's HasValue aspect is just meant for unit testing, not anything to
 * generalize.
 */
public class SimpleRenderable extends Widget implements IsRenderable, HasText, HasValue<Object> {

  private String text = "";
  private Object value;

  public SimpleRenderable() {
    setElement(PotentialElement.build(this));
  }

  @Override
  public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Object> handler) {
    return addHandler(handler, ValueChangeEvent.getType());
  }

  @Override
  public void claimElement(Element element) {
    setElement(element);
  }

  @Override
  public String getText() {
    return text;
  }

  @Override
  public Object getValue() {
    return value;
  }

  @Override
  public void initializeClaimedElement() {
  }

  @Override
  public SafeHtml render(RenderableStamper stamper) {
    HtmlBuilderFactory factory = HtmlBuilderFactory.get();
    String realText = text;
    text = "[string built]" + text;
    HtmlElementBuilderBase<?> builder = (HtmlElementBuilderBase<?>) build(factory);
    text = realText;
    return stamper.stamp(builder.asSafeHtml());
  }

  @Override
  public void render(RenderableStamper stamper, SafeHtmlBuilder safeHtmlBuilder) {
    safeHtmlBuilder.append(render(stamper));
  }

  @Override
  public Element resolvePotentialElement() {
    String realText = text;
    text = "[dom built]" + text;
    ElementBuilderBase<?> builder = build(DomBuilderFactory.get());
    text = realText;
    setElement(builder.finish());
    return getElement();
  }

  @Override
  public void setText(String text) {
    this.text = text;
    updateElement();
  }

  @Override
  public void setValue(Object value) {
    setValue(value, false);
  }

  @Override
  public void setValue(Object newValue, boolean fireEvents) {
    Object oldValue = value;
    value = newValue;
    ValueChangeEvent.fireIfNotEqual(this, oldValue, newValue);
  }

  private ElementBuilderBase<?> build(ElementBuilderFactory factory) {
    DivBuilder builder = factory.createDivBuilder();
    builder.text(text).end();
    return builder;
  }

  private boolean domIsReal() {
    return !PotentialElement.isPotential(getElement());
  }

  private void updateElement() {
    if (domIsReal()) {
      String realText = text;
      text = "[updated]" + text;
      /*
       * Sleazey. If this is to get real, should split rendering into the outer
       * builder and the inner builder. Update can just call the inner builder.
       * But we'd still have to make a bunch of dom calls to tear down the old
       * guts, something like while (el.firstChild) {
       * el.removeChild(el.firstChild); }
       * 
       * Have to wonder if that actually would be any faster than rebuilding the
       * guts as string
       */
      ElementBuilderBase<?> builder = build(DomBuilderFactory.get());
      getElement().setInnerHTML(builder.finish().getInnerHTML());
      text = realText;
    }
  }
}
