import java.awt.*;

/**
*
* An abstract canvas object which serves the purpose of insulating the
* mathematical part of the program (Panorama object below) from the
* graphics (DrawPanel object above).  The Panorama object does its
* drawing by making calls to the KaliCanvas object, which in turn
* translates them into the appropriate graphics calls for drawing on
* the DrawPanel object.
* 
* <p>
* 
* (It might be more efficient to have the Panorama object make the
* graphics calls directly, but for now we try it this way.)
*
* <p>
* 
* <b>Coordinate systems:</b>
* 
* <p>
* 
* Kali uses 3 different coordinate systems for the image in the DrawPanel:
* 
* <dl>
*     <dt><i>Raw Screen Coordinates</i>
*         <dd>This is the original screen coordinate system of the
* 	    drawing area, which goes from (0,0) in the upper left to
* 	    (screenWidth, screenHeight) in the lower right (in
* 	    pixels).  Y increases DOWN in this system.
* 	    <p>
* 	    Mouse events happen in this coordinate system.
* 	    <p>
* 
*     <dt><i>Screen Coordinates</i>
* 	<dd>This system is the result of translating the raw screen
* 	    system so that (0,0) is at the center of the screen; so it
* 	    goes from (-screenWidth/2, -screenHeight/2) in the upper
* 	    left to (screenWidth/2, screenHeight/2) in the lower
* 	    right.  Y increases DOWN in this system too.
* 	    <p>
* 	    Drawing happens in this coordinate system; the
* 	    KaliCanvas.setGraphics() method calls g.translate() to
* 	    move the Graphics object's origin from the upper left (the
* 	    default) to the center of the screen.  Also, the
* 	    translation computations in the Panorama object happen in
* 	    this system.
* 	    <p>
* 
*       <dt><i>Internal Coordinates</i>
* 	<dd>This system goes from (-internalWidth, internalHeight) in
* 	    the upper left to (internalWidth, -internalHeight) in the
* 	    lower right; so (0,0) is at the center of the screen.  Y
* 	    increases UP in this system.
* 	    <p>
* 	    The coordinates of drawn segments are stored in this
* 	    system.  Also, the Panorama object does some of the group
* 	    action math (reflection and rotation) in this system.
* 	    <p>
* 	    Also, in the future, scaling and translation may be
* 	    implemented by transforming this system.
* 	    <p>
* </dl>
*
*
* @see Panorama
* @see DrawPanel
*/

class KaliCanvas {

  /**
   * screenWidth and screenHeight record the size, in pixels, of the
   * current screen.  We initialize these to reasonable values here so
   * that we can do the initial updateCoordinateSystems() call without
   * risk of division by zero; they get set to actual screen values
   * before any actual drawing is done, by the call to setGraphics().
   */
  int screenWidth=500, screenHeight=500;

  /**
   * internalWidth and internalHeight record the dimensions of of
   * internal coordinate system (-internalWidth,-internalHeight) to
   * (+internalWidth,+internalHeight).  In the current version these
   * numbers are fixed, but future versions might allow for scaling
   * and/or translations by modifying these values.
   */
  double internalWidth=-2.5, internalHeight=2.5;

  /**
   * position and positionInverse are used for converting
   * between the internal and screen coordinate systems:
   *    position * internal = screen;
   *    positionInverse * screen = internal
   */
  DMatrix position, positionInverse;


  /**
   * screenLeft,screenRight,screenBottom, and screenTop hold the
   * bounds of the screen coordinate system
   */
  int screenLeft	= -250;
  int screenRight	=  250;
  int screenBottom	= -250;
  int screenTop		=  250;

  /**
   * The graphics context that we use for actual drawing.
   */
  Graphics g;
  
  public KaliCanvas() {
    super();
    updateCoordinateSystems();
  }

/**
* Causes the KaliCanvas object to do the internal updating that
* is necessary after a new coordinate system has been set.
*/
  private void updateCoordinateSystems() {
    double xfactor =  2 * internalWidth / screenWidth;
    double yfactor = -2 * internalHeight / screenHeight;
    positionInverse = new DMatrix(xfactor,	0,
				     0,		yfactor);
    position = positionInverse.inverse();
  }
  
/**
* Convert raw screen coordinates to internal coordinates.
*/
  public DVector rawScreenToInternal(int x, int y) {
    return positionInverse.times(new DVector(x - screenWidth/2,
						y - screenHeight/2));
  }

/**
* Convert internal coordinates to screen coordinates.
*/
  public DVector internalToScreen(DVector v) {
    return position.times(v);
  }

/**
* Set the graphics context for future drawing; this results in
* a new screen coordinate system being computed based on the
* current size of this graphics context.
*/
  public void setGraphics(Graphics g, int w, int h) {
    this.g = g;
    Rectangle area = g.getClipRect();
    screenWidth = w;
    screenHeight = h;
    updateCoordinateSystems();
    screenLeft		= -screenWidth/2;
    screenRight		=  screenWidth/2;
    screenBottom	= -screenHeight/2;
    screenTop		=  screenHeight/2;
    g.translate(screenWidth/2, screenHeight/2);
  }
  
/**
* Draw a single line segment.
*/
  public void drawSegment(Segment s, Color c) {
    g.setColor(c);
    g.drawLine((int)(s.p[0].c[0]+0.5),
	       (int)(s.p[0].c[1]+0.5),
	       (int)(s.p[1].c[0]+0.5),
	       (int)(s.p[1].c[1]+0.5));
  }

}
