/*
 * 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.plaf;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.RoundRectangle2D;

import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicGraphicsUtils;
import javax.swing.plaf.basic.BasicTabbedPaneUI;

/**
 * The Class JNPadTabbedPaneUI.
 * 
 * @version 0.3
 * @since   jNPad v0.1
 */
public final class JNPadTabbedPaneUI extends BasicTabbedPaneUI {
  /** The Constant ROUNDED_TAB_ARC_HEIGHT. */
  private static final int               ROUNDED_TAB_ARC_HEIGHT = 20;

  /** The Constant ROUNDED_TAB_ARC_WIDTH. */
  private static final int               ROUNDED_TAB_ARC_WIDTH  = 20;

  /** The Constant DEFAULT_STROKE. */
  private static final BasicStroke       DEFAULT_STROKE         = new BasicStroke(1f);

  /** The Constant TAB_SELECTED_STROKE. */
  private static final BasicStroke       TAB_SELECTED_STROKE    = new BasicStroke(1.2f);

  /** The Constant TAB_STROKE. */
  private static final BasicStroke       TAB_STROKE             = new BasicStroke(0.75f);

  /** The Constant DASHED_STROKE. */
  private static final BasicStroke       DASHED_STROKE          = new BasicStroke(1.0f,
                                                                    BasicStroke.CAP_BUTT,
                                                                    BasicStroke.JOIN_MITER,
                                                                    10.0f, new float[] { 2.0f }, 0.0f);

  /** The Constant TABBED_PANE_MARGIN. */
  private static final Insets            TABBED_PANE_MARGIN     = new Insets(0, 0, 0, 0);

  /** The selected color. */
  private Color                          selectedColor;

  /** The border painted. */
  private boolean                        borderPainted;

  /**
   * Creates the ui.
   *
   * @param c JComponent
   * @return ComponentUI
   */
  public static ComponentUI createUI(JComponent c) {
    return new JNPadTabbedPaneUI();
  }

  /**
   *  
   * @see javax.swing.plaf.basic.BasicTabbedPaneUI#installDefaults()
   */
  @Override
  protected void installDefaults() {
    super.installDefaults();
    selectedColor = UIManager.getColor("TabbedPane.selected"); //$NON-NLS-1$
    borderPainted = UIManager.getBoolean("TabbedPane.contentBorderPainted"); //$NON-NLS-1$
  }

  /**
   * Gets the tab label shift x.
   *
   * @param tabPlacement int
   * @param tabIndex int
   * @param isSelected boolean
   * @return int
   */
  @Override
  protected int getTabLabelShiftX(int tabPlacement, int tabIndex, boolean isSelected) {
    return 0;
  }

  /**
   * Gets the tab label shift y.
   *
   * @param tabPlacement int
   * @param tabIndex int
   * @param isSelected boolean
   * @return int
   */
  @Override
  protected int getTabLabelShiftY(int tabPlacement, int tabIndex, boolean isSelected) {
    return 0;
  }

  /**
   * Calculate tab width.
   *
   * @param tabPlacement int
   * @param tabIndex int
   * @param metrics FontMetrics
   * @return int
   */
  @Override
  protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) {
    return super.calculateTabWidth(tabPlacement, tabIndex, metrics) + 5;
  }

  /**
   * Paint tab border.
   *
   * @param g Graphics
   * @param tabPlacement int
   * @param tabIndex int
   * @param x int
   * @param y int
   * @param w int
   * @param h int
   * @param isSelected boolean
   */
  @Override
  protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
                                int x, int y, int w, int h, boolean isSelected) {

    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);

    h = h - 2; // added
    if (isSelected) {
      h = h - 2;
      g2D.setStroke(TAB_SELECTED_STROKE);
    }
    else {
      g2D.setStroke(TAB_STROKE);
    }

    Color shadow1 = null;
    Color darkShadow1 = null;
    shadow1 = shadow;
    darkShadow1 = shadow;
    g.setColor(shadow1);
    switch (tabPlacement) {
      case LEFT:
        g.drawLine(x + 1, y + h - 2, x + 1, y + h - 2); // bottom-left highlight
        g.drawLine(x, y + 2, x, y + h - 3); // left highlight
        g.drawLine(x + 1, y + 1, x + 1, y + 1); // top-left highlight
        g.drawLine(x + 2, y, x + w - 1, y); // top highlight

        g.setColor(shadow1);
        g.drawLine(x + 2, y + h - 2, x + w - 1, y + h - 2); // bottom shadow

        g.setColor(darkShadow1);
        g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1); // bottom dark shadow
        break;
      case RIGHT:
        g.drawLine(x, y, x + w - 3, y); // top highlight
        g.setColor(shadow1);
        g.drawLine(x, y + h - 2, x + w - 3, y + h - 2); // bottom shadow
        g.drawLine(x + w - 2, y + 2, x + w - 2, y + h - 3); // right shadow

        g.setColor(darkShadow1);
        g.drawLine(x + w - 2, y + 1, x + w - 2, y + 1); // top-right dark shadow
        g.drawLine(x + w - 2, y + h - 2, x + w - 2, y + h - 2); // bottom-right dark shadow
        g.drawLine(x + w - 1, y + 2, x + w - 1, y + h - 3); // right dark shadow
        g.drawLine(x, y + h - 1, x + w - 3, y + h - 1); // bottom dark shadow
        break;
      case BOTTOM:
        g2D.draw(new RoundRectangle2D.Double(x + 2, y - 2, w - 5, h + 1, ROUNDED_TAB_ARC_WIDTH, ROUNDED_TAB_ARC_HEIGHT));
        break;
      case TOP:
      default:
        g2D.draw(new RoundRectangle2D.Double(x + 2, y, w - 5, h + 1, ROUNDED_TAB_ARC_WIDTH, ROUNDED_TAB_ARC_HEIGHT));
        break;
    }

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

  /**
   * Paint tab background.
   *
   * @param g Graphics
   * @param tabPlacement int
   * @param tabIndex int
   * @param x int
   * @param y int
   * @param w int
   * @param h int
   * @param isSelected boolean
   */
  @Override
  protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex,
                                    int x, int y, int w, int h, boolean isSelected) {

    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);

    g.setColor(!isSelected || selectedColor == null ? tabPane.getBackgroundAt(tabIndex) : selectedColor);

    h = h - 2; // added
    if (isSelected) {
      h = h - 2;
    }
    switch (tabPlacement) {
      case LEFT:
        g.fillRect(x + 1, y + 1, w - 1, h - 3);
        break;
      case RIGHT:
        g.fillRect(x, y + 1, w - 2, h - 3);
        break;
      case BOTTOM:
        g2D.draw(new RoundRectangle2D.Double(x + 2, y - 2, w - 5, h + 1, ROUNDED_TAB_ARC_WIDTH, ROUNDED_TAB_ARC_HEIGHT)); // g.fillRoundRect(x + 2, y - 2, w - 5, h + 1, ROUNDED_TAB_ARC_WIDTH, ROUNDED_TAB_ARC_HEIGHT);
        break;
      case TOP:
      default:
        g2D.fill(new RoundRectangle2D.Double(x + 2, y, w - 5, h + 1, ROUNDED_TAB_ARC_WIDTH, ROUNDED_TAB_ARC_HEIGHT)); // g.fillRoundRect(x + 2, y, w - 5, h + 1, ROUNDED_TAB_ARC_WIDTH, ROUNDED_TAB_ARC_HEIGHT);
        break;
    }

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

  /**
   * Paint focus indicator.
   *
   * @param g Graphics
   * @param tabPlacement int
   * @param rects Rectangle[]
   * @param tabIndex int
   * @param iconRect Rectangle
   * @param textRect Rectangle
   * @param isSelected boolean
   */
  @Override
  protected void paintFocusIndicator(Graphics g, int tabPlacement,
                                     Rectangle[] rects, int tabIndex,
                                     Rectangle iconRect, Rectangle textRect,
                                     boolean isSelected) {
    Rectangle tabRect = rects[tabIndex];
    if (tabPane.hasFocus() && isSelected) {
      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);

      int x, y, w, h;
      g.setColor(focus);
      switch (tabPlacement) {
        case LEFT:
        case RIGHT:
          x = tabRect.x + 3;
          y = tabRect.y + 2;
          w = tabRect.width - 6;
          h = tabRect.height - 6;
          BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
          break;
        case BOTTOM:
        case TOP:
        default:
          x = tabRect.x + 4;
          y = tabRect.y + 2;
          w = tabRect.width - 9;
          h = tabRect.height - 6;
          g2D.setStroke(DASHED_STROKE);
          g2D.draw(new RoundRectangle2D.Double(x, y, w, h, ROUNDED_TAB_ARC_WIDTH, ROUNDED_TAB_ARC_HEIGHT)); // g.drawRoundRect(x, y, w, h, ROUNDED_TAB_ARC_WIDTH, ROUNDED_TAB_ARC_HEIGHT);
          break;
      }
      g2D.setRenderingHint(RenderingHints.KEY_RENDERING, oldRendering);
      g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAntialiasing);
      g2D.setStroke(DEFAULT_STROKE);
    }
  }

  /**
   * Gets the content border insets.
   *
   * @param tabPlacement int
   * @return Insets
   */
  @Override
  protected Insets getContentBorderInsets(int tabPlacement) {
    if (borderPainted) {
      return super.getContentBorderInsets(tabPlacement);
    }
    return TABBED_PANE_MARGIN;
  }

  /**
   * Paint content border.
   *
   * @param g the g
   * @param tabPlacement the tab placement
   * @param selectedIndex the selected index
   * @see javax.swing.plaf.basic.BasicTabbedPaneUI#paintContentBorder(java.awt.Graphics, int, int)
   */
  @Override
  protected void paintContentBorder(Graphics g, int tabPlacement, int selectedIndex) {
    if (borderPainted) {
      super.paintContentBorder(g, tabPlacement, selectedIndex);
    }
  }

}
