/*-
 * @(#)ExtendedJBorder.java
 *
 * Copyright: Yokogawa,  All rights reserved.
 * YOKOGAWA PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 * Author:    C. Horevoorts
 *
 * ----------------------------------------------------------------------------
 *  Changes ....
 * ----------------------------------------------------------------------------
 *  Who When    Change  What
 * ----------------------------------------------------------------------------
 *  HVS Feb-08  e9435   First version documented
 *  BNL Apr-09  i10199  Correct hit detection for border top, bottom and text
 *  BNL Mar-11  e10468  732 Correct hit detection for empty border with text
 *  HVS Jun-20  e12272  Port to JViews 2017.2
 *
 */

package fasttools.jwui.ilv.graphic;

import fasttools.jwui.common.property.JBorderType;
import fasttools.jwui.ilv.swing.INonRectangularJComponent;
import fasttools.jwui.ilv.swing.IJComponentTagger;
import ilog.views.IlvPoint;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;

/**
 * Extends the ExtendedJBorder to be usable in the {@link ExtendedJComponent} container.
 */
public class ExtendedJBorder extends JPanel implements IJComponentTagger, INonRectangularJComponent {
  private String cLabel = "";
  private Border cBorder = JBorderType.NATIVE_BORDER;

  public ExtendedJBorder() {
    setOpaque(false);
    setBorder(cBorder);
  }

  /**
   * Sets the label.
   *
   * @param label The label
   */
  public void setLabelText(String label) {
    if (label == null) {
      label = "";
    }

    if (!cLabel.equals(label)) {
      cLabel = label;
      createBorder();
    }
  }

  /**
   * Returns the label.
   *
   * @return The label
   */
  public String getLabelText() {
    return cLabel;
  }

  /**
   * Sets the border type.
   *
   * @param borderType The border type
   */
  public void setBorderType(JBorderType borderType) {
    Border mBorder = borderType.getBorder();
    if (mBorder != cBorder) {
      cBorder = mBorder;
      createBorder();
    }
  }

  /**
   * Returns the border type.
   *
   * @return The border type
   */
  public JBorderType getBorderType() {
    return new JBorderType(cBorder);
  }

  /**
   * Override to update the border.
   *
   * @param color The color
   */
  public void setForeground(Color color) {
    super.setForeground(color);
    createBorder();
  }

  /**
   * Override to update the border.
   *
   * @param font The font
   */
  public void setFont(Font font) {
    super.setFont(font);
    createBorder();
  }

  /**
   * Recreate the border.
   */
  private void createBorder() {
    if (cLabel != null) {
      Border mBorder = cBorder;
      if (mBorder instanceof LineBorder) {
        mBorder = new LineBorder(getForeground());
      }
      if (cLabel.length() > 0) {
        mBorder = new TitledBorder(mBorder, cLabel, TitledBorder.LEADING, TitledBorder.TOP, getFont(), getForeground());
      }
      setBorder(mBorder);
    }
  }

  public boolean contains(Point2D p, AffineTransform t, GraphicHelper graphicHelper, float zoomFactor, boolean zoomEnabled) {
    // TODO: Shearing is not taken into consideration, so sheared borders won't hit-detect correctly.
    Shape mShape = graphicHelper.getShape();
    if (isOpaque()) {
      return mShape.contains(p);
    }
    if (!mShape.contains(p)) {
      return false;
    }
    Point2D mTopLeft = graphicHelper.getXYNull();
    Point2D mTopRight = graphicHelper.getXYWidth();
    double mRotation = GraphicHelper.getRotationAngleInDegrees((IlvPoint) mTopLeft, (IlvPoint) mTopRight);

    String mLabel = getLabelText();
    if (mLabel != null && !mLabel.equals("")) {
      Graphics2D g = (Graphics2D) getGraphics();
      FontRenderContext mFontRenderingContext = g.getFontRenderContext();
      Font mFont = getFont();
      TextLayout mActualText = new TextLayout(mLabel, mFont, mFontRenderingContext);
      TextLayout mHeightText = new TextLayout("abcdefghijklmnopqrstuvwxyz", mFont, mFontRenderingContext);
      Rectangle2D mBounds = mActualText.getBounds();
      Rectangle2D mHeightBounds = mHeightText.getBounds();
      double mZoomFactor = zoomFactor / (zoomEnabled ? 1.0 : t.getScaleX());
      double mTextHeight = mHeightBounds.getHeight() * mZoomFactor;
      // NOTE: The width in mBounds is too long for reasons that I've given up trying to figure out.  Multiplying by 0.75 gives us a value that's good enough for now.
      double mTextWidth = mBounds.getWidth() * mZoomFactor * 0.75;
      double mLeftInset = 5.0 * mZoomFactor;
      Point2D mTextTopLeft = getPointFrom((IlvPoint) mTopLeft, mLeftInset, mRotation);
      Point2D mTextTopRight = getPointFrom((IlvPoint) mTextTopLeft, mTextWidth, mRotation);
      Point2D mTextBottomLeft = getPointFrom((IlvPoint) mTextTopLeft, mTextHeight, mRotation + 90.0);
      Point2D mTextBottomRight = getPointFrom((IlvPoint) mTextBottomLeft, mTextWidth, mRotation);
      Path2D.Double mTextShapeRectangle = new Path2D.Double(Path2D.WIND_NON_ZERO, 4);
      mTextShapeRectangle.moveTo(mTextTopLeft.getX(), mTextTopLeft.getY());
      mTextShapeRectangle.lineTo(mTextTopRight.getX(), mTextTopRight.getY());
      mTextShapeRectangle.lineTo(mTextBottomRight.getX(), mTextBottomRight.getY());
      mTextShapeRectangle.lineTo(mTextBottomLeft.getX(), mTextBottomLeft.getY());
      if (mTextShapeRectangle.contains(p)) {
        return true;
      }
    }

    if (!getBorderType().equals(new JBorderType(JBorderType.EMPTY))) {
      double mBorderWidth = 6.0 * zoomFactor;
      if (!zoomEnabled && t != null) {
        mBorderWidth /= t.getScaleX();
      }
      Point2D mBottomLeft = graphicHelper.getXYHeight();
      Point2D mBottomRight = graphicHelper.getXYWidthHeight();
      mTopLeft = getPointFrom((IlvPoint) mTopLeft, mBorderWidth, mRotation + 45.0);
      mTopRight = getPointFrom((IlvPoint) mTopRight, mBorderWidth, mRotation + 135.0);
      mBottomRight = getPointFrom((IlvPoint) mBottomRight, mBorderWidth, mRotation + 225.0);
      mBottomLeft = getPointFrom((IlvPoint) mBottomLeft, mBorderWidth, mRotation + 315.0);
      Path2D.Double mInsideShape = new Path2D.Double(Path2D.WIND_NON_ZERO, 4);
      mInsideShape.moveTo(mTopLeft.getX(), mTopLeft.getY());
      mInsideShape.lineTo(mTopRight.getX(), mTopRight.getY());
      mInsideShape.lineTo(mBottomRight.getX(), mBottomRight.getY());
      mInsideShape.lineTo(mBottomLeft.getX(), mBottomLeft.getY());
      if (!mInsideShape.contains(p)) {
        return true;
      }
    }
    return false;
  }

  private Point2D getPointFrom(IlvPoint point, double distance, double angle) {
    IlvPoint mMidPoint = GraphicHelper.getPointRotatedFrom(point, angle - 45.0, distance);
    return GraphicHelper.getPointRotatedFrom(mMidPoint, angle + 45.0, distance);
  }
}
