/*
 * Licensed Materials - Property of Perforce Software, Inc. 
 * © Copyright Perforce Software, Inc. 2014, 2021 
 * © Copyright IBM Corp. 2009, 2014
 * © Copyright ILOG 1996, 2009
 * All Rights Reserved.
 *
 * Note to U.S. Government Users Restricted Rights:
 * The Software and Documentation were developed at private expense and
 * are "Commercial Items" as that term is defined at 48 CFR 2.101,
 * consisting of "Commercial Computer Software" and
 * "Commercial Computer Software Documentation", as such terms are
 * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4,
 * as applicable.
 */

import java.awt.Color;
import java.awt.Graphics;
import java.io.IOException;

import ilog.views.IlvGraphic;
import ilog.views.IlvPoint;
import ilog.views.IlvRect;
import ilog.views.IlvTransformer;
import ilog.views.graphic.IlvArcUtil;
import ilog.views.io.IlvInputStream;
import ilog.views.io.IlvOutputStream;
import ilog.views.io.IlvReadFileException;

/**
 * A shadow ellipse object. A graphic object defined by two
 * ellipses: The main ellipse and a second ellipse of the same
 * size underneath the first one that represents a shadow.
 *
 * The purpose of this sample is to illustrate how to implement your own
 * graphic objects.
 */
public class ShadowEllipse
extends IlvGraphic
{ 
  /**
   * The definition rectangle of the ellipse.
   * This rectangle is the bounding rectangle of the
   * graphic object.
   */
  protected final IlvRect drawrect = new IlvRect();
  /**
   * The color of the ellipse.
   */
  private Color  color = Color.blue;
  /** 
   * The color of the shadow.
   */
  private Color  shadowColor = Color.black;
  /**
   * The thickness of the shadow.
   */
  private int thickness = 5;

  /**
   * Creates a new shadow ellipse.
   * @param rect the bounding rectangle of the shadow ellipse.
   */
  public ShadowEllipse(IlvRect rect)
  {
    super();
    // Stores the bounding rectangle of the object.
    drawrect.reshape(rect.x, rect.y, rect.width, rect.height);
  }
  
  /**
   * Creates an ellipse by copying another one.
   * @param source the object to copy.
   */
  public ShadowEllipse(ShadowEllipse source) 
  {
    // We first call the super class constructor 
    // that will copy the informations of the super class.
    super(source);

    // Copies the bounding rectangle.
    drawrect.reshape(source.drawrect.x, source.drawrect.y, 
                     source.drawrect.width, source.drawrect.height);
    // Copies the color and the color of the shadow.
    setColor(source.getColor());
    setShadowColor(source.getShadowColor());
    // Copies the thickness
    setThickness(source.getThickness());
  }
  
  /**
   * Reads the object from an IlvInputStream
   * @param stream the input stream.
   * @exception IlvReadFileException an error occurs when reading.
   */
  public ShadowEllipse(IlvInputStream stream) throws IlvReadFileException
  {
    // Calls the super class constructor that reads the 
    // the informations for the super class in the file.
    super(stream);
    // Reads the color.
    setColor(stream.readColor("color"));
    // Reads the shadow color.
    setShadowColor(stream.readColor("shadowColor"));
    // Reads the thickness
    setThickness(stream.readInt("thickness"));
    // reads the definition rectangle.
    IlvRect rect = stream.readRect("rectangle");
    drawrect.reshape(rect.x, rect.y, rect.width, rect.height);
  }
  
  /**
   * Copies the objects.
   */
  Override
  public IlvGraphic copy()
  {
    // We simply call the copy constructor that is defined above.
    return new ShadowEllipse(this);
  }

  
  /**
   * Draws the object.
   * We overwrite the draw method to define the way the object will appear.
   * @param dst The AWT object that will perform the
   * drawing operations. 
   * @param t This parameter is the transformer used to draw the object.
   * This parameter may be a translation, a zoom or a rotation.
   * When the graphic object is drawn in a view (IlvManagerView),
   * this transformer is the transformer of the view.
   */
  Override
  public void draw(Graphics dst, IlvTransformer t)
  {
    // We first copy the rectangle that defines the bounding
    // rectangle of the object because we do not want to modify it.
    
    IlvRect r = new IlvRect(drawrect);
    
    // To compute the bounding rectangle of the object in 
    // the view coordinate system, we apply the transformer 't'
    // to the definition rectangle.
    // The transformer may define a zoom, a translation or a rotation.
    
    // We are using apllyFloor so the resulting rectangle
    // is correctly projected for drawing in the view.
    // The object's coordinate system is defined by 'double' values
    // and we need 'int' values to be able to draw. applyFloor will
    // apply the transformation to 'r' ans then calls Math.floor to
    // translate 'double' values to 'int' values.
    if (t != null) 
      t.applyFloor(r);
    else
      r.floor();
    
    // The variable 'r' now contains the bounding rectangle
    // of the object in the view's coordinate system and we will
    // draw in this rectangle. In this rectangle, we will draw 
    // two ellispes. We first draw the shadow ellipse on the 
    // bottom right corner of the definition rectangle, then the main
    // ellipse on the top-left corner. Each ellipse will be of size
    // (r.width-thickness, r.height-thickness).

    int thick = thickness;

    // Computes a correct value for thickness.
    // Since we want the size of the ellipses
    // to be (r.width-thickness, r.height-thickness), we
    // must check that the thickness is not too big.

    if ((r.width <= thick) || (r.height <= thick))
      thick = (int)Math.min(r.width, r.height);

    // Sets the size of the ellipses.
    r.width  -= thick;
    r.height -= thick;
    
    // 'r' now contains the bounding area of the main ellipse.

    // Computes a rectangle to draw the shadow.
    // We copy the variable 'r', we will need it for the
    // second ellipse.
    IlvRect shadowRect = new IlvRect(r);
    shadowRect.translate(thick, thick);

    // Draws the shadow ellipse
    dst.setColor(getShadowColor());
    dst.fillArc((int)shadowRect.x, 
                (int)shadowRect.y, 
                (int)shadowRect.width, 
                (int)shadowRect.height,
                0, 360);

    // Draws the main ellipse.
    dst.setColor(getColor());
    dst.fillArc((int)r.x, 
                (int)r.y, 
                (int)r.width, 
                (int)r.height,
                0, 360);
  }

  /**
   * Tests if a point lies within the shape of the object.
   * This method will be called when you click on the object.
   * @param p The point where you in the object's coordinate system.
   * @param tp Same point as 'p' but transformed by transformer 't'
   * @param t The transformer used to draw the object.
   */
  Override
  public boolean contains(IlvPoint p, IlvPoint tp,
                          IlvTransformer t)
  {
    // We want to allow the use to click on the main ellipse
    // but not on the shadow ellipse. 
    // This method will return true when the clicked point is
    // on the main ellipse.
    // We first compute the bounding rectangle of the main
    // ellipse in the view coordinate system, just like in the
    // method draw.
    IlvRect r = new IlvRect(drawrect);
      
    if (t != null) 
      t.apply(r);
        
    int thick = thickness;
  
    if ((r.width <= thick) || (r.height <= thick))
      thick = (int)Math.min(r.width, r.height);

    r.width  -= thick;
    r.height -= thick;

    // Then we call PointInFilledArc that will return true
    // if the point is in the ellipse. 'r' and 'tp' are both
    // in the view coordinate system.
    return IlvArcUtil.PointInFilledArc(tp, r, 0, 360);
  }

  /**
   * Computes the bounding rectangle of the graphic 
   * object when drawn with the specified transformer.
   */
  Override
  public IlvRect boundingBox(IlvTransformer t)
  {
    // We first copy the definition rectangle
    // because we do not want to modify it.
    IlvRect rect = new IlvRect(drawrect);
    // Apply the transformer on the rectangle to
    // translate to the correct coordinate system.
    if (t != null) t.apply(rect);
    return rect;
  }
  
  /**
   * Applies a transformation to the shape of the object.
   * @param t The transformer to apply
   */
  Override
  public void applyTransform(IlvTransformer t)
  {
    // This method is called by method such as IlvGraphic.move
    // IlvGraphic.rotate or IlvGraphic.scale to modify the
    // shape of the object. For example, when this method
    // is called from IlvGraphic.move, the parameter 't' is the
    // corresponding translation.
    // We simply need to apply the transformer to
    // the definition rectangle of the object.
    t.apply(drawrect);
  }
    
  /**
   * Changes the thickness of the shadow ellipse
   * @param thickness the new thickness
   */
  public void setThickness(int thickness)
  {
    this.thickness = thickness;
  }
  
  /**
   * Returns the thickness of the shadow ellipse
   * @return the thickness of the object.
   */
  public int getThickness()
  {
    return thickness;
  }
  
  /**
   * Changes the color of the ellipse.
   * @param color the new color.
   */
  public void setColor(Color color)
  {
    this.color = color;
  }
  
  /**
   * Returns the color of the shadow ellipse
   * @return the color of the object.
   */
  public Color getColor()
  {
    return color;
  }

  /**
   * Changes the color of the shadow
   * @param color the new color
   */
  public void setShadowColor(Color color)
  {
    this.shadowColor = color;
  }
  
  /**
   * Returns the color of the shadow
   * @return the color of the shadow
   */
  public Color getShadowColor()
  {
    return shadowColor;
  }

  /**
   * Writes the object to an output stream.
   */ 
  Override
  public  void write(IlvOutputStream stream) 
    throws IOException
  {
    // Calls the super class method that will write
    // the fields specific to the super class.
    super.write(stream);
    // Writes the colors.
    stream.write("color", getColor());
    stream.write("shadowColor", getShadowColor());
    // Writes the thickness.
    stream.write("thickness", getThickness());
    // Writes the definition rectangle.
    stream.write("rectangle", drawrect);
  }
}