/*
 * 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.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.Arc2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;

import javax.swing.Icon;
import javax.swing.ImageIcon;

import jnpad.GUIUtilities;
import jnpad.ui.ColorUtilities;

/**
 * The Class IconUtilities.
 *
 * @version 0.3
 * @since   jNPad v0.1
 */
public final class IconUtilities {
  /** no instances */
  private IconUtilities() {
    super();
  }

  /**
   * Gets the icon.
   *
   * @param picture the picture
   * @param source the source
   * @return the icon
   * @throws FileNotFoundException the file not found exception
   */
  public static ImageIcon getIcon(String picture, Class<?> source) throws FileNotFoundException {
    return GUIUtilities.getIcon(picture, source);
  }

  /**
   * Creates the image.
   *
   * @param icon Icon
   * @return Image
   */
  public static Image createImage(Icon icon) {
    if (icon instanceof ImageIcon) {
      return ((ImageIcon) icon).getImage();
    }
    BufferedImage bim = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = bim.createGraphics();
    icon.paintIcon(null, g, 0, 0);
    g.dispose();
    return bim;
  }

  /**
   * Gets the horizontal navigation icon.
   *
   * @return Icon
   */
  public static Icon getHorizontalNavigationIcon() {
    return new HArrow(8, 16, false, true, true, false, Color.BLACK);
  }

  /**
   * Gets the vertical navigation icon.
   *
   * @return Icon
   */
  public static Icon getVerticalNavigationIcon() {
    return new VArrow(16, 8, false, true, true, false, Color.BLACK);
  }

  /**
   * Gets the text file icon.
   *
   * @param w int
   * @return FileIcon
   */
  public static FileIcon getTextFileIcon(int w) {
    FileIcon fi = new FileIcon(w, false);
    fi.isText = true;
    return fi;
  }

  /**
   * Gets the diff file icon.
   *
   * @param w int
   * @return FileIcon
   */
  public static FileIcon getDiffFileIcon(int w) {
    FileIcon fi = new FileIcon(w, false);
    fi.isText = true;
    fi.isDiffFile = true;
    return fi;
  }

  /**
   * Gets the read only file icon.
   *
   * @param w int
   * @return FileIcon
   */
  public static FileIcon getReadOnlyFileIcon(int w) {
    FileIcon fi = new FileIcon(w, false);
    //fi.isText = true;
    //fi.isDiffFile = true;
    fi.color = new Color(200, 20, 20, 180);
    return fi;
  }

  /**
   * Gets the system file icon.
   *
   * @param w int
   * @param isFolder boolean
   * @return FileIcon
   */
  public static FileIcon getSystemFileIcon(int w, boolean isFolder) {
    FileIcon fi = new FileIcon(w, isFolder);
    fi.isSystem = true;
    return fi;
  }

  /**
   * Gets the project file icon.
   *
   * @param w int
   * @param isFolder boolean
   * @return FileIcon
   */
  public static FileIcon getProjectFileIcon(int w, boolean isFolder) {
    FileIcon fi = new FileIcon(w, isFolder);
    fi.isProject = true;
    return fi;
  }

  /**
   * Gets the resource file icon.
   *
   * @param w int
   * @param isFolder boolean
   * @return FileIcon
   */
  public static FileIcon getResourceFileIcon(int w, boolean isFolder) {
    FileIcon fi = new FileIcon(w, isFolder);
    fi.isProject = true;
    fi.color = Color.yellow;
    return fi;
  }

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class Icons.
   */
  public static abstract class Icons implements Icon {
    /** width. */
    protected int w;
    
    /** height. */
    protected int h;

    /**
     * Instantiates a new icons.
     *
     * @param w the width
     * @param h the height
     */
    protected Icons(int w, int h) {
      this.w = w;
      this.h = h;
    }

    /**
     *
     * @return int
     */
    public int getIconWidth() {return w;}

    /**
     *
     * @return int
     */
    public int getIconHeight() {return h;}

    /**
     * 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) {
      Graphics2D g2D = (Graphics2D) g;

      final Object oldRendering    = g2D.getRenderingHint(RenderingHints.KEY_RENDERING);
      final Object oldAntialiasing = g2D.getRenderingHint(RenderingHints.KEY_ANTIALIASING);

      g2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
      g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

      // backup
      Font oldFont     = g2D.getFont();
      Stroke oldStroke = g2D.getStroke();
      Color oldColor   = g2D.getColor();

      if (c != null) {
        g2D.setColor(c.getForeground());
      }
      g2D.translate(x, y);
      doPaintIcon(c, g2D);
      g2D.translate(-x, -y);

      // restore
      g2D.setFont(oldFont);
      g2D.setStroke(oldStroke);
      g2D.setColor(oldColor);

      g2D.setRenderingHint(RenderingHints.KEY_RENDERING, oldRendering);
      g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAntialiasing);
    }

    /**
     * Do paint icon.
     *
     * @param c the c
     * @param g the g
     */
    protected abstract void doPaintIcon(Component c, Graphics2D g);
  }
  //////////////////////////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class LetterIcon.
   */
  public static class LetterIcon extends Icons {
    boolean enabled;
    String  letter;
    boolean border;
    Color   letterColor;
    Font    font = new Font("Lucida Sans Typewriter", Font.BOLD | Font.BOLD, 12); //$NON-NLS-1$

    /**
     * Instantiates a new letter icon.
     *
     * @param letter String
     * @param border boolean
     * @param w int
     * @param h int
     * @param enabled boolean
     */
    public LetterIcon(String letter, boolean border, int w, int h, boolean enabled) {
      this(letter, border, w, h, enabled, Color.black);
    }

    /**
     * Instantiates a new letter icon.
     *
     * @param letter String
     * @param border boolean
     * @param w int
     * @param h int
     * @param enabled boolean
     * @param letterColor Color
     */
    public LetterIcon(String letter, boolean border, int w, int h, boolean enabled, Color letterColor) {
      super(w, h);
      this.enabled = enabled;
      this.letter = letter;
      this.border = border;
      this.letterColor = letterColor;

      if (font.canDisplayUpTo(letter) != -1) {
        this.font = new Font("Lucida Sans Unicode", Font.BOLD | Font.BOLD, h * 6 / 7); //$NON-NLS-1$
      }
      else {
        this.font = new Font("Lucida Sans Typewriter", Font.BOLD | Font.BOLD, h * 6 / 7); //$NON-NLS-1$
      }
    }

    /**
     * Do paint icon.
     *
     * @param c the Component
     * @param g2 the Graphics2D
     * @see jnpad.ui.icon.IconUtilities.Icons#doPaintIcon(java.awt.Component, java.awt.Graphics2D)
     */
    @Override
    protected void doPaintIcon(Component c, Graphics2D g2) {
      g2.setColor(enabled ? letterColor : ColorUtilities.toGrayscale(letterColor));
      g2.setFont(font);
      g2.drawString(letter, w / 3, h - w / 5);

      if (border) {
        g2.setColor(Color.black);
        Shape rect = new Rectangle2D.Double(1, 1, w - 2, h - 2);
        g2.draw(rect);
      }
    }
  }
  //////////////////////////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class HArrow.
   */
  public static class HArrow extends Icons {
    boolean enabled;
    Color   color = Color.blue;
    boolean showLeft;
    boolean showRight;
    boolean withStopBar;

    /**
     * Instantiates a new h arrow.
     *
     * @param w int
     * @param h int
     * @param enabled boolean
     */
    public HArrow(int w, int h, boolean enabled) {
      this(w, h, false, true, enabled);
    }

    /**
     * Instantiates a new h arrow.
     *
     * @param w int
     * @param h int
     * @param showLeft boolean
     * @param showRight boolean
     * @param enabled boolean
     */
    public HArrow(int w, int h, boolean showLeft, boolean showRight, boolean enabled) {
      this(w, h, showLeft, showRight, enabled, false, Color.blue);
    }

    /**
     * Instantiates a new h arrow.
     *
     * @param w int
     * @param h int
     * @param showLeft boolean
     * @param showRight boolean
     * @param enabled boolean
     * @param withStopBar boolean
     * @param color Color
     */
    public HArrow(int w, int h, boolean showLeft, boolean showRight, boolean enabled, boolean withStopBar, Color color) {
      super(w, h);
      this.showLeft    = showLeft;
      this.showRight   = showRight;
      this.enabled     = enabled;
      this.withStopBar = withStopBar;
      this.color       = color;
    }

    /**
     * Do paint icon.
     *
     * @param c the Component
     * @param g2 the Graphics2D
     * @see jnpad.ui.icon.IconUtilities.Icons#doPaintIcon(java.awt.Component, java.awt.Graphics2D)
     */
    @Override
    protected void doPaintIcon(Component c, Graphics2D g2) {
      if (enabled) {
        g2.setColor(color);
        g2.setStroke(new BasicStroke(h / 15.0f));
      }
      else {
        g2.setColor(Color.gray);
      }

      g2.draw(new Line2D.Double(w / 8.0, h / 2.0, w - w / 8.0, h / 2.0));
      if (showRight) {
        g2.draw(new Line2D.Double(w - w / 8.0, h / 2.0, 3 * w / 4.0 - w / 8.0, h / 2.0 - h / 5.0));
        g2.draw(new Line2D.Double(w - w / 8.0, h / 2.0, 3 * w / 4.0 - w / 8.0, h / 2.0 + h / 5.0));

        if (withStopBar) {
          g2.draw(new Line2D.Double(w - w / 8.0, h / 2.0 - h / 3.0, w - w / 8.0, h / 2.0 + h / 3.0));
        }
      }

      if (showLeft) {
        g2.draw(new Line2D.Double(w / 8.0, h / 2.0, w / 4.0 + w / 8.0, h / 2.0 - h / 5.0));
        g2.draw(new Line2D.Double(w / 8.0, h / 2.0, w / 4.0 + w / 8.0, h / 2.0 + h / 5.0));

        if (withStopBar) {
          g2.draw(new Line2D.Double(w / 8.0, h / 2.0 - h / 3.0, w / 8.0, h / 2.0 + h / 3.0));
        }
      }
    }
  }
  //////////////////////////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class VArrow.
   */
  public static class VArrow extends Icons {
    boolean enabled;
    Color   color    = Color.blue;
    boolean showUp;
    boolean showDown;
    boolean showRoot = true;
    boolean withStopBar;

    /**
     * Instantiates a new v arrow.
     *
     * @param w int
     * @param h int
     * @param enabled boolean
     */
    public VArrow(int w, int h, boolean enabled) {
      this(w, h, true, false, enabled);
    }

    /**
     * Instantiates a new v arrow.
     *
     * @param w int
     * @param h int
     * @param showUp boolean
     * @param showDown boolean
     * @param enabled boolean
     */
    public VArrow(int w, int h, boolean showUp, boolean showDown, boolean enabled) {
      this(w, h, showUp, showDown, enabled, false, Color.blue);
    }

    /**
     * Instantiates a new v arrow.
     *
     * @param w int
     * @param h int
     * @param showUp boolean
     * @param showDown boolean
     * @param enabled boolean
     * @param withStopBar boolean
     * @param color Color
     */
    public VArrow(int w, int h, boolean showUp, boolean showDown, boolean enabled, boolean withStopBar, Color color) {
      super(w, h);
      this.enabled     = enabled;
      this.showUp      = showUp;
      this.showDown    = showDown;
      this.withStopBar = withStopBar;
      this.color       = color;
    }

    /**
     * Do paint icon.
     *
     * @param c the Component
     * @param g2 the Graphics2D
     * @see jnpad.ui.icon.IconUtilities.Icons#doPaintIcon(java.awt.Component, java.awt.Graphics2D)
     */
    @Override
    protected void doPaintIcon(Component c, Graphics2D g2) {
      if (enabled) {
        g2.setColor(color);
        g2.setStroke(new BasicStroke(h / 15.0f));
      }
      else {
        g2.setColor(Color.gray);
      }

      g2.draw(new Line2D.Double(w / 2.0, h / 8.0, w / 2.0, h - h / 8.0));
      if (showUp) {
        g2.draw(new Line2D.Double(w / 2.0, h / 8.0, w / 2.0 - w / 5.0, h / 4.0 + h / 8.0));
        g2.draw(new Line2D.Double(w / 2.0, h / 8.0, w / 2.0 + w / 5.0, h / 4.0 + h / 8.0));
        if (withStopBar) {
          g2.draw(new Line2D.Double(w / 2.0 - w / 3.0, h / 8.0, w / 2.0 + w / 3.0, h / 8.0));
        }
      }

      if (showDown) {
        g2.draw(new Line2D.Double(w / 2.0, h - h / 8.0, w / 2.0 - w / 5.0, 3 * h / 4.0 - h / 8.0));
        g2.draw(new Line2D.Double(w / 2.0, h - h / 8.0, w / 2.0 + w / 5.0, 3 * h / 4.0 - h / 8.0));
        if (withStopBar) {
          g2.draw(new Line2D.Double(w / 2.0 - w / 3.0, h - h / 8.0, w / 2.0 + w / 3.0, h - h / 8.0));
        }
      }

      if (showRoot) {
        if (!showUp) {
          // on the upper part
          g2.draw(new Line2D.Double(w / 2.0, h / 8.0, w / 2.0 - w / 5.0, h / 8.0));
        }

        if (!showDown) {
          g2.draw(new Line2D.Double(w / 2.0, h - h / 8.0, w / 2.0 - w / 5.0, h - h / 8.0));
        }
      }
    }
  }
  //////////////////////////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class FileIcon.
   */
  public static class FileIcon extends Icons {
    boolean enabled;
    String  letter;
    boolean directory;
    boolean isText;
    boolean isDiffFile = false;
    Font    letterFont;
    Color   color      = Color.white;
    boolean isSystem   = false;
    boolean isProject  = false;

    /**
     * Instantiates a new file icon.
     *
     * @param w int
     * @param directory boolean
     */
    public FileIcon(int w, boolean directory) {
      this(w, w, directory, true, null);
    }

    /**
     * Instantiates a new file icon.
     *
     * @param w int
     * @param h int
     * @param directory boolean
     * @param enabled boolean
     * @param letter String
     */
    public FileIcon(int w, int h, boolean directory, boolean enabled, String letter) {
      super(w, h);
      this.directory = directory;
      this.enabled   = enabled;
      this.letter    = letter;
      if (letter != null) {
        letterFont = new Font("Lucida Sans Typewriter", Font.PLAIN, w * 5 / 8); //$NON-NLS-1$
      }
    }

    /**
     * Do paint icon.
     *
     * @param c Component
     * @param g2 Graphics2D
     */
    @Override
    public void doPaintIcon(final Component c, final Graphics2D g2) {
      GeneralPath poly = new GeneralPath();

      if (directory) {
        poly.moveTo( (float) (w * 0.10), (float) (h * 0.16));
        poly.lineTo( (float) (w * 0.45), (float) (h * 0.16));
        poly.lineTo( (float) (w * 0.50), (float) (h * 0.04));
        poly.lineTo( (float) (w * 0.88), (float) (h * 0.04));
        poly.lineTo( (w - 1)           , (float) (h * 0.18));
        poly.lineTo( (w - 1)           , (float) (h * 0.85));
        poly.lineTo( (float) (w * 0.1) , (float) (h * 0.85));
      }
      else {
        poly.moveTo( (float) (w * 0.55), (float) (h * 0.1));
        poly.lineTo( (float) (w * 0.55), (float) (h * 0.34));
        poly.lineTo( (float) (w * 0.8) , (float) (h * 0.34));

        poly.moveTo( (float) (w * 0.15), (float) (h * 0.1));
        poly.lineTo( (float) (w * 0.55), (float) (h * 0.1));
        poly.lineTo( (float) (w * 0.8) , (float) (h * 0.34));

        poly.lineTo( (float) (w * 0.8) , h);
        poly.lineTo( (float) (w * 0.15), h);

      }
      poly.closePath();

      if (enabled) {
        if (isSystem) {
          Color color1 = new Color(180, 180, 10);
          Color color2 = new Color(220, 220, 10);
          g2.setPaint(new GradientPaint(0, 0, color1, w, w, color2, false));
        }
        else {
          g2.setColor(color);
        }

        g2.fill(poly);
      }
      g2.setColor(Color.black);

      if (isProject) {
        g2.setColor(new Color(200, 20, 20, 120));
        if (directory) {
          g2.fillRect(w - 5, w - 7, 5, 5);
        }
        else {
          g2.fillRect(w - 8, w - 4, 5, 5);
        }
        g2.setColor(Color.black);
      }

      if (letter != null) {
        Font oldFont = g2.getFont();

        g2.setFont(letterFont);
        if (this.directory) {
          g2.drawString(letter, w / 3 + 1, h * 6 / 8 + 1);
        }
        else {
          g2.drawString(letter, w / 4, h * 7 / 8);
        }

        g2.setFont(oldFont);
      }

      g2.draw(poly);

      if (isText) {
        for (int i = 0; i < 3; i++) {
          if (isDiffFile) {
            switch (i) {
              case 0:
                g2.setColor(Color.BLACK);
                break;
              case 1:
                g2.setColor(Color.RED);
                break;
              case 2:
                g2.setColor(Color.GREEN);
                break;
              default: //Keep FindBugs happy
                break;
            }
          }
          double hh = h / 3 + h / 8 + i * h / 6;
          double d = i == 1 ? 2 : 0;
          g2.drawLine((w / 2 - w / 5), (int) hh, (int) (w / 2 + w / 7 - d), (int) hh);
        }
      }
    }
  }
  //////////////////////////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class MultilineIcon.
   */
  public static class MultilineIcon extends Icons {
    boolean     enabled;
    Color       color = Color.black;
    final float lineWidth;

    /**
     * Instantiates a new multiline icon.
     *
     * @param w int
     * @param h int
     * @param lineWidth float
     * @param enabled boolean
     * @param color Color
     */
    public MultilineIcon(int w, int h, float lineWidth, boolean enabled, Color color) {
      super(w, h);
      this.enabled = enabled;
      this.lineWidth = lineWidth;
      this.color = color;
    }

    /**
     * Do paint icon.
     *
     * @param c the Component
     * @param g2 the Graphics2D
     * @see jnpad.ui.icon.IconUtilities.Icons#doPaintIcon(java.awt.Component, java.awt.Graphics2D)
     */
    @Override
    public void doPaintIcon(Component c, Graphics2D g2) {
      Shape line1 = new Line2D.Double(w / 8.0, h / 2.0, w - w / 3.0, h / 2.0);
      Shape line2 = new Line2D.Double(w / 8.0, h / 2.0 - h / 4.0, w - w / 8.0, h / 2.0 - h / 4.0);
      Shape line3 = new Line2D.Double(w / 8.0, h / 2.0 + h / 4.0, w - w / 8.0, h / 2.0 + h / 4.0);
      if (enabled) {
        g2.setColor(color);
        g2.setStroke(new BasicStroke(lineWidth));
        g2.draw(line1);
        g2.draw(line2);
        g2.draw(line3);
      }
      else {
        g2.setColor(Color.gray);
        g2.draw(line1);
        g2.draw(line2);
        g2.draw(line3);
      }
    }
  }
  //////////////////////////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class EyeIcon.
   */
  public static class EyeIcon extends Icons {
    boolean enabled;
    Color   color;

    /**
     * Instantiates a new eye icon.
     *
     * @param w int
     * @param h int
     * @param enabled boolean
     */
    public EyeIcon(int w, int h, boolean enabled) {
      this(w, h, enabled, new Color(160, 180, 250));
    }

    /**
     * Instantiates a new eye icon.
     *
     * @param w int
     * @param h int
     * @param enabled boolean
     * @param color Color
     */
    public EyeIcon(int w, int h, boolean enabled, Color color) {
      super(w, h);
      this.enabled = enabled;
      this.color = color;
    }
    
    /**
     * Sets the color.
     *
     * @param color the new color
     * @since 0.2
     */
    public void setColor(Color color) {
      this.color = color;
    }

    /**
     * Do paint icon.
     *
     * @param c the Component
     * @param g2 the Graphics2D
     * @see jnpad.ui.icon.IconUtilities.Icons#doPaintIcon(java.awt.Component, java.awt.Graphics2D)
     */
    @Override
    public void doPaintIcon(Component c, Graphics2D g2) {
      Shape disk = new Arc2D.Double(0, w / 4.0, w, w / 2.0, 0, 180, Arc2D.OPEN);
      Shape disk2 = new Arc2D.Double(0, w / 4.0, w, w / 2.0, 180, 360, Arc2D.OPEN);
      Shape disk3 = new Arc2D.Double(w / 2.0 - w / 4.0, w / 2.0 - w / 8.0, w / 4.0, w / 4.0, 0, 360, Arc2D.PIE); // the eye

      g2.setColor(enabled ? color : Color.lightGray);
      g2.fill(disk);
      g2.fill(disk2);
      g2.setColor(Color.black);
      g2.fill(disk3);

      g2.setStroke(new BasicStroke(h / 15.0f));
      g2.setColor(Color.black);
      g2.draw(disk);
      g2.draw(disk2);
    }
  }
  //////////////////////////////////////////////////////////////////////////////

}
