package de.lessvoid.nifty.effects.impl;

import de.lessvoid.nifty.EndNotify;
import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.NiftyIdCreator;
import de.lessvoid.nifty.builder.ControlBuilder;
import de.lessvoid.nifty.builder.ElementBuilder;
import de.lessvoid.nifty.builder.LayerBuilder;
import de.lessvoid.nifty.effects.EffectEventId;
import de.lessvoid.nifty.effects.EffectImpl;
import de.lessvoid.nifty.effects.EffectProperties;
import de.lessvoid.nifty.effects.Falloff;
import de.lessvoid.nifty.elements.Element;
import de.lessvoid.nifty.render.NiftyRenderEngine;
import de.lessvoid.nifty.screen.Screen;
import de.lessvoid.nifty.tools.SizeValue;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.logging.Logger;

/**
 * Hint - show a hint, a nifty hint!
 * <p/>
 * This is the new, even more nifty version of the hint. It maintains only a single layer and does not delete this
 * layer once its created. All hints are stored on this single layer. Thanks to the render order system of Nifty the
 * hints are still always shown at the very top of the layer stack.
 *
 * @author void
 * @author Martin Karing &lt;nitram@illarion.org&gt;
 */
public class Hint implements EffectImpl {
  @Nonnull
  private static final Logger log = Logger.getLogger(Hint.class.getName());
  @Nonnull
  private static final String HINT_LAYER_ID = "niftyHintLayer";
  @Nullable
  private Nifty nifty;
  @Nullable
  private Screen screen;
  @Nullable
  private Element hintLayer;
  @Nullable
  private Element hintPanel;
  private int hintDelay;
  @Nullable
  private String offsetX;
  @Nullable
  private String offsetY;

  @Override
  public void activate(
      @Nonnull final Nifty nifty,
      @Nonnull final Element element,
      @Nonnull final EffectProperties parameter) {
    this.nifty = nifty;
    screen = nifty.getCurrentScreen();

    if (screen == null) {
      log.severe("Can't activate hint effect without a screen that is currently active.");
      return;
    }

    final String hintControl = parameter.getProperty("hintControl", "nifty-default-hint");
    final String hintStyle = parameter.getProperty("hintStyle", null);
    final String hintText = parameter.getProperty("hintText", "hint: add a 'hintText' attribute to the hint effect :)");
    hintDelay = Integer.valueOf(parameter.getProperty("hintDelay", "0"));
    offsetX = parameter.getProperty("offsetX", "0");
    offsetY = parameter.getProperty("offsetY", "0");

    Element hintLayer = getHintLayer();

    ControlBuilder builder = new ControlBuilder(NiftyIdCreator.generate(), hintControl);
    builder.parameter("hintText", hintText);
    if (hintStyle != null) {
      builder.style(hintStyle);
    }
    builder.visible(false);
    hintPanel = builder.build(nifty, screen, hintLayer);
  }

  @Nonnull
  private Element getHintLayer() {
    if (hintLayer != null) {
      return hintLayer;
    }
    if (screen == null || nifty == null) {
      throw new IllegalStateException("Can't create or retrieve hint layer while there is no active screen.");
    }
    hintLayer = screen.findElementById(HINT_LAYER_ID);
    if (hintLayer != null) {
      return hintLayer;
    }
    LayerBuilder builder = new LayerBuilder(HINT_LAYER_ID);
    builder.childLayout(ElementBuilder.ChildLayoutType.Absolute);
    builder.visible(false);
    hintLayer = builder.build(nifty, screen, screen.getRootElement());
    hintLayer.setRenderOrder(Integer.MAX_VALUE);
    return hintLayer;
  }

  @Override
  public void execute(
      @Nonnull final Element element,
      final float normalizedTime,
      @Nullable final Falloff falloff,
      @Nonnull final NiftyRenderEngine r) {
    if (nifty == null || screen == null || hintLayer == null || hintPanel == null) {
      log.severe("Can't execute effect. Activation not done or failed.");
      return;
    }
    if (normalizedTime > 0.0) {
      if (!hintPanel.isVisible()) {
        // decide if we can already show the hint
        if (nifty.getNiftyMouse().getNoMouseMovementTime() > hintDelay) {
          hintPanel.setConstraintX(SizeValue.px(getPosX(element, hintPanel, r.getWidth())));
          hintPanel.setConstraintY(SizeValue.px(getPosY(element, hintPanel, r.getHeight())));

          // hard remove all old hints, else they only mess with the new ones
          List<Element> hints = hintLayer.getChildren();
          for (int i = 0; i < hints.size() - 1; i++) {
            nifty.removeElement(screen, hints.get(i));
          }

          hintLayer.layoutElements();
          hintLayer.show();
        }
      }
    }
  }

  @Override
  public void deactivate() {
    if (hintPanel != null) {
      if (hintPanel.isVisible()) {
        hintPanel.startEffect(EffectEventId.onCustom, new EndNotify() {
          @Override
          public void perform() {
            removePanel();
          }
        });
      } else {
        removePanel();
      }
    }
  }

  private void removePanel() {
    if (hintPanel != null && hintLayer != null) {
      hintPanel.markForRemoval(new EndNotify() {
        @Override
        public void perform() {
          if (hintLayer.getChildrenCount() == 1) {
            hintLayer.hide();
          }
        }
      });
    }
  }

  private int getPosX(@Nonnull final Element element, @Nonnull final Element hintPanel, final int screenWidth) {
    int pos;
    if ("center".equals(offsetX)) {
      pos = element.getX() + element.getWidth() / 2 - hintPanel.getWidth() / 2;
    } else if ("left".equals(offsetX)) {
      pos = element.getX();
    } else if ("right".equals(offsetX)) {
      pos = element.getX() + element.getWidth() - hintPanel.getWidth();
    } else {
      pos = Integer.valueOf(offsetX);
    }
    if (pos < 0) {
      pos = 0;
    }
    if (pos + hintPanel.getWidth() > screenWidth) {
      pos = screenWidth - hintPanel.getWidth();
    }
    return pos;
  }

  private int getPosY(@Nonnull final Element element, @Nonnull final Element hintPanel, final int screenHeight) {
    int pos;
    if ("center".equals(offsetY)) {
      pos = element.getY() + element.getHeight() / 2 - hintPanel.getHeight() / 2;
    } else if ("top".equals(offsetY)) {
      pos = element.getY();
    } else if ("bottom".equals(offsetY)) {
      pos = element.getY() + element.getHeight() - hintPanel.getHeight();
    } else {
      pos = Integer.valueOf(offsetY);
    }
    if (pos < 0) {
      pos = 0;
    }
    if (pos + hintPanel.getHeight() > screenHeight) {
      pos = screenHeight - hintPanel.getHeight();
    }
    return pos;
  }

}

@@ -32,24 +32,155 @@
 ts.impl;%0D%0A%0D%0A
+import java.util.List;%0D%0Aimport java.util.logging.Logger;%0D%0A%0D%0Aimport javax.annotation.Nonnull;%0D%0Aimport javax.annotation.Nullable;%0D%0A%0D%0A
 import de.le
@@ -803,137 +803,8 @@
 %0D%0A%0D%0A
-import javax.annotation.Nonnull;%0D%0Aimport javax.annotation.Nullable;%0D%0Aimport java.util.List;%0D%0Aimport java.util.logging.Logger;%0D%0A%0D%0A
 /**%0D
@@ -2811,37 +2811,8 @@
  %7D%0D%0A
-    builder.visible(false);%0D%0A
     
@@ -3995,29 +3995,29 @@
    if (!hint
-Panel
+Layer
 .isVisible()

fix hint effect, closes #246