/*
 * jNPad v0.3 - jNPad's an Simple Text Editor written in Java
 *
 * Copyright (C) 2014-2017  rgs
 *
 * Require JDK 1.6 (or later)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 *
 * Info, Questions, Suggestions & Bugs Report to rgsevero@gmail.com
 */

package jnpad.ui.icon;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;

import javax.swing.Icon;
import javax.swing.SwingConstants;
import javax.swing.UIManager;

/**
 * The Class ArrowIcon.
 *
 * @version 0.3
 * @since   jNPad v0.1
 */
public class ArrowIcon implements Icon, SwingConstants {
  private static final float DB = -.06f;
  private int                direction;
  private int                size;
  private Color              color;
  private BufferedImage      arrowImage;

  /**
   * Instantiates a new arrow icon.
   *
   * @param direction int
   */
  public ArrowIcon(int direction) {
    this(direction, 10, null);
  }

  /**
   * Instantiates a new arrow icon.
   *
   * @param direction int
   * @param color Color
   */
  public ArrowIcon(int direction, Color color) {
    this(direction, 10, color);
  }

  /**
   * Instantiates a new arrow icon.
   *
   * @param direction int
   * @param size int
   * @param color Color
   */
  public ArrowIcon(int direction, int size, Color color) {
    this.size = size;
    this.direction = direction;
    this.color = color;
  }

  /**
   * Gets the icon height.
   *
   * @return the icon height
   * @see javax.swing.Icon#getIconHeight()
   */
  @Override
  public int getIconHeight() {
    return size;
  }

  /**
   * Gets the icon width.
   *
   * @return the icon width
   * @see javax.swing.Icon#getIconWidth()
   */
  @Override
  public int getIconWidth() {
    return size;
  }

  /**
   * Paint icon.
   *
   * @param c the Component
   * @param g the Graphics
   * @param x the x
   * @param y the y
   * @see javax.swing.Icon#paintIcon(java.awt.Component, java.awt.Graphics, int, int)
   */
  @Override
  public void paintIcon(Component c, Graphics g, int x, int y) {
    g.drawImage(getArrowImage(), x, y, c);
  }

  /**
   * Creates the translucent image.
   *
   * @param width the width
   * @param height the height
   * @return the buffered image
   */
  public static BufferedImage createTranslucentImage(int width, int height) {
    return GraphicsEnvironment.getLocalGraphicsEnvironment().
        getDefaultScreenDevice().getDefaultConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
  }

  /**
   * Gets the arrow image.
   *
   * @return the arrow image
   */
  protected Image getArrowImage() {
    if (arrowImage == null) {
      arrowImage = createTranslucentImage(size, size);
      AffineTransform atx = direction != SOUTH ? new AffineTransform() : null;
      switch (direction) {
        case NORTH:
          atx.setToRotation(Math.PI, size / 2.0, size / 2.0);
          break;
        case EAST:
          atx.setToRotation(-(Math.PI / 2), size / 2.0, size / 2.0);
          break;
        case WEST:
          atx.setToRotation(Math.PI / 2, size / 2.0, size / 2.0);
          break;
        case SOUTH:
        default: 
          /* no xform */
          break;
      }
      Graphics2D ig = (Graphics2D) arrowImage.getGraphics();
      if (atx != null) {
        ig.setTransform(atx);
      }
      int width = size;
      int height = size / 2 + 1;
      int xx = (size - width) / 2;
      int yy = (size - height + 1) / 2;

      Color base = color != null ? color : UIManager.getColor("controlDkShadow").darker(); //$NON-NLS-1$

      paintArrow(ig, base, xx, yy);
      paintArrowBevel(ig, base, xx, yy);
      paintArrowBevel(ig, deriveColorHSB(base, 0f, 0f, .20f), xx, yy + 1);
    }
    return arrowImage;
  }

  /**
   * Paint arrow.
   *
   * @param g the Graphics2D
   * @param base the base color
   * @param x the x
   * @param y the y
   */
  protected void paintArrow(Graphics2D g, Color base, int x, int y) {
    g.setColor(base);
    int len = size - 2;
    int xx = x;
    int yy = y - 1;
    while (len >= 2) {
      xx++;
      yy++;
      g.fillRect(xx, yy, len, 1);
      len -= 2;
    }
  }

  /**
   * Paint arrow bevel.
   *
   * @param g the Graphics
   * @param base the base color
   * @param x the x
   * @param y the y
   */
  protected void paintArrowBevel(Graphics g, Color base, int x, int y) {
    int len = size;
    int xx = x;
    int yy = y;
    Color c2 = deriveColorHSB(base, 0f, 0f, (-DB) * (size / 2.0f));
    while (len >= 2) {
      c2 = deriveColorHSB(c2, 0f, 0f, DB);
      g.setColor(c2);
      g.fillRect(xx, yy, 1, 1);
      g.fillRect(xx + len - 1, yy, 1, 1);
      len -= 2;
      xx++;
      yy++;
    }

  }

  /**
   * Derives a color by adding the specified offsets to the base color's hue,
   * saturation, and brightness values. The resulting hue, saturation, and
   * brightness values will be contrained to be between 0 and 1.
   * 
   * @param base the color to which the HSV offsets will be added
   * @param dH the offset for hue
   * @param dS the offset for saturation
   * @param dB the offset for brightness
   * @return Color with modified HSV values
   */
  public static Color deriveColorHSB(Color base, float dH, float dS, float dB) {
    float hsb[] = Color.RGBtoHSB(
        base.getRed(), base.getGreen(), base.getBlue(), null);

    hsb[0] += dH;
    hsb[1] += dS;
    hsb[2] += dB;
    return Color.getHSBColor(
        hsb[0] < 0 ? 0 : (hsb[0] > 1 ? 1 : hsb[0]),
        hsb[1] < 0 ? 0 : (hsb[1] > 1 ? 1 : hsb[1]),
        hsb[2] < 0 ? 0 : (hsb[2] > 1 ? 1 : hsb[2]));

  }

}
