package yi.iu.ui;

import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;

/**
 * <p>Title: Image Canvas</p>
 * <p>Description: This a panel-like component used to display a image</p>
 * <p>Copyright: Copyright (c) 2002</p>
 * <p>Company: </p>
 * @author Yi Li
 * @version 1.0
 */

public class ImageCanvas extends Canvas {
  /**
   * Constants used to decide how to paint the image on the ImageCanvas
   * SCALE_TO_FIT means the image will be scaled to display on the canvas
   */
  public static final int SCALE_TO_FIT = 0;
  /**
   * Constants used to decide how to paint the image on the ImageCanvas
   * RESIZE_CANVAS means the canvas will be resized to put the image
   */
  public static final int RESIZE_CANVAS = 1;
  /**
   * The image
   */
  private Image m_image;
  /**
   * The real size of the image, not the size displayed
   */
  private Dimension m_imageSize = null;
  /**
   * The image size on the canvas
   */
  private Dimension m_imageScreenSize;
  /**
   * The size of the canvas
   */
  private Dimension m_canvasSize = null;
  /**
   * How to fit the image to the canvas
   */
  private int m_fitMethod = SCALE_TO_FIT;

  /**
   * Initialize the canvas rescaled to fit the image.  The image size may be not
   * available now, so the set of the size will be delayed.
   *
   * @param image the image to be displayed
   */
  public ImageCanvas(Image image) {
    m_image = image;
    m_fitMethod = RESIZE_CANVAS;
  }

  /**
   * Initialize the canvas with the image scaled to fit in
   *
   * @param image the image size to be displayed
   * @param width the width of the canvas
   * @param height the height of the canvas
   */
  public ImageCanvas(Image image, int width, int height) {
    m_image = image;
    m_fitMethod = SCALE_TO_FIT;
    m_canvasSize = new Dimension(width, height);
  }

  /**
   * Load the image and Init the size's
   */
  public void init() {
    // wait until the image is loaded
    MediaTracker mt = new MediaTracker(this);
    mt.addImage(m_image, 0);
    try {
      mt.waitForAll();
      if (mt.isErrorAny()) {
        System.err.println("Error Loading Image");
        m_image = createImage(1, 1);
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
      m_image = createImage(1, 1);
    }
    // set the size's
    m_imageSize = new Dimension(m_image.getWidth(this), m_image.getHeight(this));
    System.out.println("imagesize = " + m_imageSize);
    if (m_fitMethod == RESIZE_CANVAS) {
      m_canvasSize = new Dimension(m_imageSize);
      m_imageScreenSize = new Dimension(m_imageSize);
    }
    else if (m_fitMethod == SCALE_TO_FIT) {
      int iw = m_imageSize.width;
      int ih = m_imageSize.height;
      int cw = m_canvasSize.width;
      int ch = m_canvasSize.height;
      if (iw * ch / cw > ih) {
        // if the image can only fit the width of the canvas
        m_imageScreenSize = new Dimension(cw, ih * cw / iw);
      }
      else {
        m_imageScreenSize = new Dimension(ch * iw / ih , ch);
      }
      System.out.println("screensize = " + m_imageScreenSize);
    }
    enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK);
    setSize(m_canvasSize);
  }

  /**
   * Save the registered mouse listeners so that when a mouse event comes, we
   * can change the mouse point position and forward the new event to the
   * listeners
   */
  protected MouseListener m_mouseLisenser = null;

  public void addMouseListener(MouseListener l){
    super.addMouseListener(l);
    m_mouseLisenser = AWTEventMulticaster.add(m_mouseLisenser, l);
  }

  public void removeMouseListener(MouseListener l){
    super.removeMouseListener(l);
    m_mouseLisenser = AWTEventMulticaster.remove(m_mouseLisenser, l);
  }

  /**
   * Save the registered mouse motion listeners so that when a mouse event
   * comes, we can change the mouse point position and forward the new event to
   * the motion listeners
   */
  protected MouseMotionListener m_mouseMotionLisenser = null;

  public void addMouseMotionListener(MouseMotionListener l){
    super.addMouseMotionListener(l);
    m_mouseMotionLisenser = AWTEventMulticaster.add(m_mouseMotionLisenser, l);
  }

  public void removeMouseMotionListener(MouseMotionListener l){
    super.removeMouseMotionListener(l);
    m_mouseMotionLisenser = AWTEventMulticaster.remove(m_mouseMotionLisenser, l);
  }

  /**
   * Deal with mouse event.  Mainly change the coordinator system
   * @param e
   */
  public void processEvent(AWTEvent e){
    if (e.getID() == MouseEvent.MOUSE_MOVED) {
      if (m_mouseMotionLisenser != null) {
        MouseEvent me = (MouseEvent)e;
        Point p = getMousePointInImage(me.getPoint());
        if (p == null) return;
        m_mouseMotionLisenser.mouseMoved(new MouseEvent(this,
          MouseEvent.MOUSE_MOVED, me.getWhen(), me.getModifiers(), p.x, p.y,
          me.getClickCount(), me.isPopupTrigger()));
      }
    }
    else if (e.getID() == MouseEvent.MOUSE_CLICKED) {
      if (m_mouseLisenser != null) {
        MouseEvent me = (MouseEvent)e;
        Point p = getMousePointInImage(me.getPoint());
        if (p == null) return;
        m_mouseLisenser.mouseClicked(new MouseEvent(this,
          MouseEvent.MOUSE_CLICKED, me.getWhen(), me.getModifiers(), p.x, p.y,
          me.getClickCount(), me.isPopupTrigger()));
      }
    }
    else if (e.getID() == MouseEvent.MOUSE_PRESSED) {
      if (m_mouseLisenser != null) {
        MouseEvent me = (MouseEvent)e;
        Point p = getMousePointInImage(me.getPoint());
        if (p == null) return;
        m_mouseLisenser.mousePressed(new MouseEvent(this,
          MouseEvent.MOUSE_PRESSED, me.getWhen(), me.getModifiers(), p.x, p.y,
          me.getClickCount(), me.isPopupTrigger()));
      }
    }
    // leave other events to be dealt with the super class
    else super.processEvent(e);
  }

  /**
   * Given the mouse position in the canvas coordinator system, returns the
   * position in the image coordinator system.
   *
   * @param p position in the canvas coordinator system
   * @return p position in the image coordinator system.  Return null if p is
   *   not in the image at all
   */
  private Point getMousePointInImage(Point p) {
    int x = p.x;
    int y = p.y;
    // consider the only image part
    x -= (m_canvasSize.width - m_imageScreenSize.width)/2;
    y -= (m_canvasSize.height - m_imageScreenSize.height)/2;
    // convert to image position
    x = x * m_imageSize.width / m_imageScreenSize.width;
    y = y * m_imageSize.height / m_imageScreenSize.height;
    if (x < 0 || x > m_imageSize.width || y < 0 || y > m_imageSize.height)
      return null;
    return new Point(x, y);
  }

  /**
   * First must wait the peer is started up (which can be done though calling
   * super.addNotify()), then can call init
   */
  public void addNotify() {
    super.addNotify();
    init();
  }

  public Dimension preferredSize() {
    return m_canvasSize;
  }

  public Dimension minimumSize() {
    return m_canvasSize;
  }

  public void paint(Graphics g) {
    int left = (m_canvasSize.width - m_imageScreenSize.width) / 2;
    int top = (m_canvasSize.height - m_imageScreenSize.height) / 2;
    g.drawImage(m_image, left, top,
                m_imageScreenSize.width,
                m_imageScreenSize.height, this);
  }

  public static void main(String[] args) {
    Frame f = new Frame("image canvas");
    Image image = Toolkit.getDefaultToolkit().getImage("t.gif");
    final PixelGrabber pg = new PixelGrabber(image, 0, 0, -1, -1, false);
    pg.startGrabbing();
    ImageCanvas ic1 = new ImageCanvas(image);
    ImageCanvas ic2 = new ImageCanvas(image, 200, 100);
    ic2.addMouseMotionListener(new MouseMotionAdapter() {
      public void mouseMoved(MouseEvent e) {
        System.out.println("point = " + e.getPoint());

      }
    });
    ic2.addMouseListener(new MouseAdapter() {
      public void mousePressed(MouseEvent e) {
        int x = e.getX(), y = e.getY();
        int count = y * pg.getWidth() + x;
        if (pg.getPixels() instanceof byte[]) {
          byte[] data = (byte[])pg.getPixels();
          System.out.println("byte="+data[count]);
        }
        else {
          int[] data = (int[])pg.getPixels();
          System.out.println(data[count]);
        }
      }
      public void mouseEntered(MouseEvent e) {
        System.out.println("Entered: " + e.getPoint());
      }
    });

    f.add(ic1, BorderLayout.CENTER);
    f.add(ic2, BorderLayout.SOUTH);
    f.setSize(800, 600);
    f.show();
  }

}