// Copyright 2012 Google Inc. All Rights Reserved.
//
// 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.collide.client.ui.button;

import collide.client.common.CommonResources;
import collide.client.util.CssUtils;
import collide.client.util.Elements;

import com.google.collide.client.util.ImageResourceUtils;
import com.google.collide.mvp.CompositeView;
import com.google.collide.mvp.UiComponent;
import com.google.gwt.resources.client.ImageResource;

import elemental.dom.Element;
import elemental.events.Event;
import elemental.events.EventListener;
import elemental.html.AnchorElement;
import elemental.html.DivElement;

/**
 * A button containing and image and text.
 */
public class ImageButton extends UiComponent<ImageButton.View> {

  /**
   * Creates an {@link ImageButton}.
   */
  public static class Builder {
    private final CommonResources.BaseResources res;
    private ImageResource image;
    private String text;
    private AnchorElement element;

    public Builder(CommonResources.BaseResources res) {
      this.res = res;
    }

    public Builder setImage(ImageResource image) {
      this.image = image;
      return this;
    }

    public Builder setText(String text) {
      this.text = text;
      return this;
    }

    public Builder setElement(AnchorElement element) {
      this.element = element;
      return this;
    }

    public ImageButton build() {
      // Create an element if one is not specified.
      AnchorElement anchor = element;
      if (anchor == null) {
        anchor = Elements.createAnchorElement(res.baseCss().button());
        anchor.setHref("javascript:;");
      }

      View view = new View(res, anchor);
      ImageButton button = new ImageButton(view);
      button.setImageAndText(image, text);
      return button;
    }
  }

  /**
   * Called when the button is clicked.
   */
  public interface Listener {
    void onClick();
  }


  /**
   * The View for the Header.
   */
  public static class View extends CompositeView<ViewEvents> {

    /**
     * The spacing between the image and the text.
     */
    private static final int ICON_SPACING = 8;

    private final CommonResources.BaseCss baseCss;
    private final DivElement imageElem;
    private final DivElement imagePositioner;
    private final DivElement textElem;

    public View(CommonResources.BaseResources res, AnchorElement button) {
      baseCss = res.baseCss();

      // Create an outer element.
      setElement(button);
      button.getStyle().setPosition("relative");

      // Create a centered element to position the image.
      imagePositioner = Elements.createDivElement();
      button.appendChild(imagePositioner);
      imagePositioner.getStyle().setPosition("absolute");
      imagePositioner.getStyle().setTop("50%");

      // Add the image.
      imageElem = Elements.createDivElement();
      imagePositioner.appendChild(imageElem);
      imageElem.addClassName(baseCss.buttonImage());
      imageElem.getStyle().setPosition("absolute");

      // Override the margins set in base.css. Those margins are a hack.
      imageElem.getStyle().setMargin("0");

      // Add the text.
      textElem = Elements.createDivElement();
      textElem.getStyle().setHeight("100%");
      button.appendChild(textElem);

      attachEventListeners();
    }

    @Override
    public AnchorElement getElement() {
      return (AnchorElement) super.getElement();
    }

    public Element getImageElement() {
      return imageElem;
    }

    @SuppressWarnings("null")
    private void setImageAndText(ImageResource image, String text) {
      // Update the image.
      boolean hasImage = (image != null);
      CssUtils.setDisplayVisibility2(imageElem, hasImage);
      if (hasImage) {
        ImageResourceUtils.applyImageResource(imageElem, image);
      }

      // Update the text.
      boolean hasText = (text != null && text.length() > 0);
      textElem.setTextContent(hasText ? text : "");

      // Position the image.
      if (hasImage) {
        imageElem.getStyle().setTop(image.getHeight() / -2, "px");
        if (hasText) {
          // Left align the image if we have text.
          imagePositioner.getStyle().setLeft(baseCss.buttonHorizontalPadding(), "px");
          imageElem.getStyle().setLeft(0, "px");
        } else {
          // Horizontally center the image if we do not have text.
          imagePositioner.getStyle().setLeft("50%");
          imageElem.getStyle().setLeft(image.getWidth() / -2, "px");
        }
      }

      /*
       * Position the text. Even if there is no text, we use the text element to
       * force the button element to be wide enough to contain the image.
       */
      int left = 0;
      if (hasImage) {
        left += image.getWidth();
      }
      if (hasText) {
        left += ICON_SPACING;
      }
      textElem.getStyle().setPaddingLeft(left, "px");
    }

    private void attachEventListeners() {
      getElement().addEventListener(Event.CLICK, new EventListener() {
        @Override
        public void handleEvent(Event evt) {
          if (getDelegate() != null) {
            getDelegate().onButtonClicked();
          }
        }
      }, false);
    }
  }

  /**
   * Events reported by the View.
   */
  private interface ViewEvents {
    void onButtonClicked();
  }

  private Listener listener;
  private ImageResource image;
  private boolean isImageHidden;
  private String text;

  private ImageButton(View view) {
    super(view);
    handleEvents();
  }

  public void setListener(Listener listener) {
    this.listener = listener;
  }

  public void setImage(ImageResource image) {
    setImageAndText(image, this.text);
  }

  public void clearImage() {
    setImageAndText(null, this.text);
  }

  public void setText(String text) {
    setImageAndText(this.image, text);
  }

  public void clearText() {
    setImageAndText(this.image, null);
  }

  public void setImageAndText(ImageResource image, String text) {
    this.image = image;
    this.text = text;
    updateView();
  }

  /**
   * Shows or hides the image.
   */
  public void setImageHidden(boolean isHidden) {
    this.isImageHidden = isHidden;
    updateView();
  }

  private void updateView() {
    getView().setImageAndText(isImageHidden ? null : image, text);
  }

  private void handleEvents() {
    getView().setDelegate(new ViewEvents() {
      @Override
      public void onButtonClicked() {
        if (listener != null) {
          listener.onClick();
        }
      }
    });
  }
}
