package mosaiccreator;

import javax.swing.JPanel;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.event.MouseListener;
import java.awt.event.ComponentListener;
import java.awt.event.MouseMotionAdapter;
import java.util.Vector;
import java.util.Hashtable;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.awt.Image;

/**
 * <p>Title: Mosaic Viewer </p>
 * <p>Description: diplays the mosaic created </p>
 * @author Lance Finfrock
 * @version 1.0
 */
public class Viewer extends JPanel {
  private int subWidth;
  private int subHeight;
  private int[] imageArrayIndex;
  private Vector pictures;
  private int XPostionToDrawEnlargePic;
  private int YPostionToDrawEnlargePic;
  private Image EnlargedImage;
  private int borderWidth = 0;
  private int borderHeight = 0;
  private int enlargeBy;
  private boolean MouseIsOnASubPic;

  /**
   * true - the mouse is on a mosaic piece
   * false - the mouse is not on a mosaic piece
   */
  public boolean isMouseOverViewOn;

  private Image DisplayedMosaic;

  /**
   *
   */
  public Viewer() {
    this.addMouseMotionListener(new MouseMotionHandler());
    this.addMouseListener(new MouseClickListener());
    this.addComponentListener(new ComponentResizeListener());

    //this uses the log function to give a zoom that is not to large
    this.enlargeBy = (int)Math.log(MainWindow.currentMosaic.DivionsHigh * MainWindow.currentMosaic.DivionsWide);
    this.MouseIsOnASubPic = false;
    this.isMouseOverViewOn = true;
    this.setBackground(new Color(245, 245, 245));
    this.createQuickIndexToMosaicPieces();

  }

  /**
   * changes the zoom on the mouse over viewer
   * @param zoom int
   */
  public void setZoom(int zoom) {
    this.enlargeBy = zoom;
  }

  /**
   * redraws the mosaic
   */
  public void redrawPictuer() {
    if(this.subWidth <= 0 || this.subHeight <= 0){
      this.resizeMosaic();
    }

    this.DisplayedMosaic = MainWindow.currentMosaic.image.getScaledInstance(this.subWidth *
          MainWindow.currentMosaic.DivionsWide, this.subHeight * MainWindow.currentMosaic.DivionsHigh, Image.SCALE_FAST);

    this.repaint();
  }

  public void paint(Graphics g) {
    //clearing the screen
    g.clearRect(0, 0, this.getWidth(), this.getHeight());

    //displaying the image to the screen
    if(this.DisplayedMosaic != null)
      g.drawImage(this.DisplayedMosaic, this.borderWidth, this.borderHeight, this);

    //if the mouse is on the displayed image and the Mouse over view is on
    if (this.MouseIsOnASubPic && this.isMouseOverViewOn) {
      g.drawImage(this.EnlargedImage, this.XPostionToDrawEnlargePic,
                  this.YPostionToDrawEnlargePic, this);

      //creating a black box around the image in the mouse over view
      g.drawRect(this.XPostionToDrawEnlargePic, this.YPostionToDrawEnlargePic,
                 this.subWidth * this.enlargeBy, this.subHeight * this.enlargeBy);
    }
  }




  /**
   * this quick index is only usefull if the Mosaic is made from multable of the same picture
   * this allow a dublicat picture to only be read in once
   */
  private void createQuickIndexToMosaicPieces() {
    this.imageArrayIndex = new int[MainWindow.currentMosaic.subPiecesPixels.length];
    Hashtable hash = new Hashtable();
    this.pictures = new Vector();
    int count = 0;
    for (int i = 0; i < imageArrayIndex.length; i++) {
      if (!hash.containsKey(MainWindow.currentMosaic.subPiecesPixels[i].filePath)) {
        hash.put(MainWindow.currentMosaic.subPiecesPixels[i].filePath, new Integer(count));
        this.imageArrayIndex[i] = count;
        count++;
        this.pictures.add(MainWindow.currentMosaic.subPiecesPixels[i].filePath);
      }
      else {
        this.imageArrayIndex[i] = ( (Integer) hash.get(MainWindow.currentMosaic.subPiecesPixels[i].
                                                  filePath)).
            intValue();
      }
    }
  }

  private class MouseClickListener implements MouseListener {

    public void mouseClicked(MouseEvent mouseEvent) {
      int x = mouseEvent.getX();
      int y = mouseEvent.getY();
      if (x > borderWidth && x < getWidth() - borderWidth &&
          y > borderHeight && y < getHeight() - borderHeight) {
        new pictureViewer( (String) pictures.get(imageArrayIndex[
                                                 findIndexOfPictureMouseIsOn(x,
            y)]));

      }
    }

    public void mousePressed(MouseEvent mouseEvent) {}

    public void mouseReleased(MouseEvent mouseEvent) {}

    public void mouseEntered(MouseEvent mouseEvent) {}

    public void mouseExited(MouseEvent mouseEvent) {
      MouseIsOnASubPic = false;
      sendMousePosition( -1, -1);
    }
  }

  private class ComponentResizeListener implements ComponentListener {
    public void componentResized(ComponentEvent componentEvent) {
      resizeMosaic();
    }

    public void componentMoved(ComponentEvent componentEvent) {}

    public void componentShown(ComponentEvent componentEvent) {}

    public void componentHidden(ComponentEvent componentEvent) {}
  }

  private void resizeMosaic() {
    this.findSubPiecesWidthHeight();
    this.borderHeight = (this.getHeight() - (this.subHeight * MainWindow.currentMosaic.DivionsHigh)) / 2;
    this.borderWidth = (this.getWidth() - (this.subWidth * MainWindow.currentMosaic.DivionsWide)) / 2;

    this.DisplayedMosaic = MainWindow.currentMosaic.image.getScaledInstance(this.subWidth *
    MainWindow.currentMosaic.DivionsWide, this.subHeight * MainWindow.currentMosaic.DivionsHigh, Image.SCALE_AREA_AVERAGING);
  }

  private void findSubPiecesWidthHeight() {
    int windowWide = this.getWidth();
    int windowHeight = this.getHeight();

    int MosaicPictureHeight, MosaicPictureWidth;
    if ( ( (double) MainWindow.currentMosaic.OriginalPicture.width) / MainWindow.currentMosaic.OriginalPicture.height >
        ( (double) windowWide) / windowHeight) {
      MosaicPictureWidth = windowWide;
      MosaicPictureHeight = (MainWindow.currentMosaic.OriginalPicture.height * MosaicPictureWidth) /
          MainWindow.currentMosaic.OriginalPicture.width;
    }
    else {
      MosaicPictureHeight = windowHeight;
      MosaicPictureWidth = (MainWindow.currentMosaic.OriginalPicture.width * MosaicPictureHeight) /
          MainWindow.currentMosaic.OriginalPicture.height;
    }
    this.subWidth = Math.round(MosaicPictureWidth / MainWindow.currentMosaic.DivionsWide);
    this.subHeight = Math.round(MosaicPictureHeight / MainWindow.currentMosaic.DivionsHigh);
  }

  private class MouseMotionHandler extends MouseMotionAdapter {

    public void mouseMoved(MouseEvent event) {
      int x = event.getX();
      int y = event.getY();

      if (isMouseOverViewOn && x > borderWidth && x < getWidth() - borderWidth &&
          y > borderHeight && y < getHeight() - borderHeight &&
          subWidth > 0 && subHeight > 0 && pictures.size() > 0) {
        MouseIsOnASubPic = true;
        sendMousePosition(x, y);
      }
      else {
        MouseIsOnASubPic = false;
        sendMousePosition( -1, -1);
      }
    }
  }

  private int findIndexOfPictureMouseIsOn(int x, int y) {
    int wide = (x - this.borderWidth) / this.subWidth;
    int high = (y - this.borderHeight) / this.subHeight;
    int index = high * MainWindow.currentMosaic.DivionsWide + wide;
    if (index >= this.imageArrayIndex.length)
      index -= MainWindow.currentMosaic.DivionsWide;

    return index;
  }

  public void sendMousePosition(int x, int y) {
    int PictureMouseIsOn;

    if (this.MouseIsOnASubPic) {
      this.MouseIsOnASubPic = true;
      PictureMouseIsOn = this.findIndexOfPictureMouseIsOn(x, y);

      //if the rounder error over estimtes the index of the picture

      this.XPostionToDrawEnlargePic = ( (x - this.borderWidth) / this.subWidth) * this.subWidth +
          this.borderWidth - (this.enlargeBy * this.subWidth) / 2;
      if (this.XPostionToDrawEnlargePic + this.subWidth * this.enlargeBy > this.getWidth()) {
        this.XPostionToDrawEnlargePic = this.getWidth() - (this.subWidth * this.enlargeBy);
      }
      else if (this.XPostionToDrawEnlargePic < 0) {
        this.XPostionToDrawEnlargePic = 0;
      }
      this.YPostionToDrawEnlargePic = ( (y - this.borderHeight) / this.subHeight) * this.subHeight +
          this.borderHeight + this.subHeight * 4;
      if (this.YPostionToDrawEnlargePic + this.subHeight * this.enlargeBy > this.getHeight()) {
        if(this.getHeight() - y > (this.subHeight * this.enlargeBy )){
          this.YPostionToDrawEnlargePic = this.getHeight() - (this.subHeight * this.enlargeBy);
        }
        else {
          this.YPostionToDrawEnlargePic = ( (y - this.borderHeight) / this.subHeight) *
              this.subHeight - ( (this.enlargeBy * (this.subHeight)) + (this.subHeight * 2));
        }
      }
      this.EnlargedImage = PictureTools.readImage( (String) this.pictures.get(
          this.imageArrayIndex[PictureMouseIsOn]), this.subWidth * this.enlargeBy,
                                             this.subHeight * this.enlargeBy, this);
    }
    this.repaint();
  }

}
