package mosaiccreator;

import java.awt.image.BufferedImage;
import java.awt.Color;
import java.awt.Frame;

/**
 *
 * <p>Title: Mosaic Image </p>
 * <p>Description: the information of a mosaic image </p>
 * @author Lance Finfrock
 * @version 1.0
 */
public class Mosaic {
  public PicPixels[] subPiecesPixels;
  public PicPixels OriginalPicture;
  public int DivionsWide;
  public int DivionsHigh;
  public double BackGroundIntensity;
  public BufferedImage image;

  /**
   *
   * @param subPieces PicPixels[] the mosaic pieces
   * @param Original PicPixels the mosaic picture
   * @param divW int the divions wide
   * @param divH int the divions high
   */
  public Mosaic(PicPixels[] subPieces, PicPixels Original, int divW, int divH) {
    subPiecesPixels = subPieces;
    OriginalPicture = Original;
    DivionsWide = divW;
    DivionsHigh = divH;
  }

  /**
   *
   * @return int the width of the mosaic image
   */
  public int getWidth() {
    return this.getSubWidth() * this.DivionsWide;
  }

  /**
   *
   * @return int the width of the mosaic pieces
   */
  public int getSubWidth() {
    return this.subPiecesPixels[0].width;
  }

  /**
   *
   * @return int the height of the mosaic image
   */
  public int getHeight() {
    return this.getSubHeight() * this.DivionsHigh;
  }

  /**
   *
   * @return int the height of the mosaic pieces
   */
  public int getSubHeight() {
    return this.subPiecesPixels[0].height;
  }

  /**
   *
   * @param maxWidth int
   * @param maxHeight int
   * @param windowCameFrom Frame
   */
  public void resizeMosaic(int maxWidth, int maxHeight, Frame windowCameFrom) {

    int subWidth, subHeight;

    //don't allow the user to use the window that called this method
    windowCameFrom.setEnabled(false);

    //cretat the wait window
    WaitWindow waitWindow = new WaitWindow();
    waitWindow.setVisible(true);
    waitWindow.Label.setText("Resizing the Mosaic");
    waitWindow.setIconImage(PictureTools.readImage("mosaicImage.gif",
        windowCameFrom));

    int MosaicPictureHeight, MosaicPictureWidth;

    //if the width is the greatest proportion
    if ( ( (double)this.OriginalPicture.width) / this.OriginalPicture.height >
        ( (double) maxWidth) / maxHeight) {

      //set the width the the greatest width
      MosaicPictureWidth = maxWidth;

      //find the correct proportion for the hieght
      MosaicPictureHeight = (this.OriginalPicture.height *
                             MosaicPictureWidth) / this.OriginalPicture.width;

    }
    else { //height is the greatest proportion

      MosaicPictureHeight = maxHeight;
      MosaicPictureWidth = (this.OriginalPicture.width * MosaicPictureHeight) /
          this.OriginalPicture.height;

    }
    //find the mosaic pieces width and height
    subWidth = Math.round(MosaicPictureWidth /
                          MainWindow.currentMosaic.DivionsWide);
    subHeight = Math.round(MosaicPictureHeight /
                           MainWindow.currentMosaic.DivionsHigh);

    String picturePath;

    //Creating sub pieces pixels
    for (int subPiecesIndex = 0; subPiecesIndex < this.subPiecesPixels.length;
         subPiecesIndex++) {

      //save the path to the image
      picturePath = this.subPiecesPixels[subPiecesIndex].filePath;

      //updating the progress bar
      waitWindow.ProgressBar.setValue( (int) (subPiecesIndex /
                                              (double)this.subPiecesPixels.
                                              length * 100.0));

      //set to null to save memory
      this.subPiecesPixels[subPiecesIndex] = null;
      this.subPiecesPixels[subPiecesIndex] = new PicPixels(picturePath,
          subWidth, subHeight);
    }

    //Creating orginal pixels
    picturePath = this.OriginalPicture.filePath;

    //set to null to save memory
    this.OriginalPicture = null;

    this.OriginalPicture = new PicPixels(picturePath,
                                         subWidth *
                                         MainWindow.currentMosaic.DivionsWide,
                                         subHeight *
                                         MainWindow.currentMosaic.DivionsHigh);

    //allow the user to use the window this came from
    windowCameFrom.setEnabled(true);
    windowCameFrom.toFront();

    //get rid of the wait window
    waitWindow.dispose();
  }

  /**
   * draws a picPixels and the sector given by the width and height to the buffered Image
   * @param picPixels PicPixels the mosiac pieces
   * @param w int the width of the sector to be draw
   * @param h int the height of the sector to be draw
   * @param MosaicPicture BufferedImage the image to be draw to
   */
  private void drawPieces(PicPixels picPixels, int w, int h) {
    Color colorpiece, colorOriginal;

    //if the picPixels was found on the computer
    if (picPixels.pixel != null) {

      for (int i = 0; i < picPixels.width; i++) {
        for (int j = 0; j < picPixels.height; j++) {
          colorpiece = picPixels.getColor(i, j);
          colorOriginal = this.OriginalPicture.getColor(i + w, j + h);
          this.image.setRGB(i + w, j + h,
                               findColor(colorpiece, colorOriginal).getRGB());
        }
      }

    }
    else { //if the picPixels was not found on the computer just draw the original image

      for (int i = 0; i < picPixels.width; i++) {
        for (int j = 0; j < picPixels.height; j++) {
          this.image.setRGB(i + w, j + h,
                               this.OriginalPicture.getColor(i + w, j + h).
                               getRGB());
        }
      }

    }

  }

  /**
   * draws the orginal picpixels sector and the mosaic piece to the buffered image
   * @param Original PicPixels the mosiac image
   * @param picPixels PicPixels the mosaic piece
   * @param w int the width of the mosiac section
   * @param h int the heigth of the mosaic section
   * @param MosaicPicture BufferedImage the image to be draw to
   */
  private void drawPieces(PicPixels Original, PicPixels picPixels, int w, int h,
                          BufferedImage MosaicPicture) {
    Color colorpiece, colorOriginal;

    //if the picPixels was found on the computer
    if (picPixels.pixel != null) {

      for (int i = 0; i < picPixels.width; i++) {
        for (int j = 0; j < picPixels.height; j++) {
          colorpiece = picPixels.getColor(i, j);
          colorOriginal = Original.getColor(i + w, j + h);
          MosaicPicture.setRGB(i + w, j + h,
                               findColor(colorpiece, colorOriginal).getRGB());
        }
      }

    }
    else {//if the picPixels was not found on the computer just draw the original image

      for (int i = 0; i < picPixels.width; i++) {
        for (int j = 0; j < picPixels.height; j++) {
          MosaicPicture.setRGB(i + w, j + h,
                               Original.getColor(i + w, j + h).getRGB());
        }
      }

    }

  }

  /**
   * finds the color value from the with the blending
   * @param colorpiece Color the color of the mosaic piece pixel
   * @param colorOriginal Color the color of the mosaic image pixel
   * @return Color the color to set the pixel to
   */
  private Color findColor(Color colorpiece, Color colorOriginal) {

    int Red = colorOriginal.getRed() -
        (int) ( (colorOriginal.getRed() - colorpiece.getRed()) *
               BackGroundIntensity);

    int Blue = colorOriginal.getBlue() -
        (int) ( (colorOriginal.getBlue() - colorpiece.getBlue()) *
               BackGroundIntensity);

    int Green = colorOriginal.getGreen() -
        (int) ( (colorOriginal.getGreen() - colorpiece.getGreen()) *
               BackGroundIntensity);

    return new Color(Red, Green, Blue);
  }

  /**
   * use to create the viewable moaic with the size of the current screen
   * @param backgroundIntensity double the blending level
   * @param windowCameFrom Frame what window called this function
   */
  public void createMosaicPicture(double backgroundIntensity,
                                  Frame windowCameFrom) {

    this.BackGroundIntensity = backgroundIntensity;

    //create a wait window to show the user the progress
    WaitWindow waitWindow = new WaitWindow();
    waitWindow.setVisible(true);
    waitWindow.Label.setText("Creating Mosaic Picture...");
    waitWindow.setIconImage(PictureTools.readImage("mosaicImage.gif",
        windowCameFrom));

    //disable the window
    windowCameFrom.setEnabled(false);

    //set the image to null to save on memory
    this.image = null;
    this.image = new BufferedImage(getWidth(), getHeight(),
                                   BufferedImage.TYPE_INT_RGB);

    int w = 0;
    int h = ( -1) * (subPiecesPixels[0].height);

    for (int i = 0; i < subPiecesPixels.length; i++) {
      if ( (i % DivionsWide) == 0)
        h += subPiecesPixels[0].height;
      w = (i % DivionsWide) * subPiecesPixels[0].width;

      //show the user the progress
      waitWindow.ProgressBar.setValue( (int) (i /
                                              (double) MainWindow.currentMosaic.
                                              subPiecesPixels.length * 100.0));
      // Paint the image onto the buffered image
      drawPieces(subPiecesPixels[i], w, h);
    }

    //set the window to be used
    windowCameFrom.setEnabled(true);
    windowCameFrom.toFront();

    //get rid of the wait window
    waitWindow.dispose();

  }

  /**
   * use to create the printable or saveable mosaic with the size that is passed in
   * @param width int the width of the mosaic wanted to be created
   * @param height int the height of the mosaic wanted to be created
   * @param backgroundIntensity double the blending level
   * @param windowCameFrom Frame the window that called this function
   * @return BufferedImage the mosaic image created
   */
  public BufferedImage createMosaicPicture(int width, int height,
                                           double backgroundIntensity,
                                           Frame windowCameFrom) {

    //create a copy of the orginal mosaic to not destory the original
    PicPixels Orginalcopy = new PicPixels(this.OriginalPicture.filePath, width,
                                          height);

    BufferedImage tempImage;

    this.BackGroundIntensity = backgroundIntensity;


    //create a wait window to show the user the progress
    WaitWindow waitWindow = new WaitWindow();
    waitWindow.Label.setText("Creating Mosaic Picture...");
    waitWindow.setIconImage(PictureTools.readImage("mosaicImage.gif",
        windowCameFrom));
    waitWindow.setVisible(true);

    //disable the window
    windowCameFrom.setEnabled(false);


    int MosaicPictureHeight, MosaicPictureWidth;

    //find the correct propotions
    if ( ( (double) this.OriginalPicture.width) / this.OriginalPicture.height > ( (double) width) / height) {
      MosaicPictureWidth = width;
      MosaicPictureHeight = (this.OriginalPicture.height *
                             MosaicPictureWidth) / this.OriginalPicture.width;
    }
    else {
      MosaicPictureHeight = height;
      MosaicPictureWidth = (this.OriginalPicture.width * MosaicPictureHeight) /this.OriginalPicture.height;
    }
    int subWidth = Math.round(MosaicPictureWidth / this.DivionsWide);
    int subHeight = Math.round(MosaicPictureHeight / this.DivionsHigh);



    tempImage = new BufferedImage(subWidth * this.DivionsWide,
                              subHeight * this.DivionsHigh,
                              BufferedImage.TYPE_INT_RGB);

    int w = 0;
    int h = ( -1) * (subHeight);
    for (int i = 0; i < subPiecesPixels.length; i++) {
      if ( (i % DivionsWide) == 0)
        h += subHeight;
      w = (i % DivionsWide) * subWidth;
      waitWindow.ProgressBar.setValue( (int) (i /
                                              (double) MainWindow.currentMosaic.
                                              subPiecesPixels.length * 100.0));
      // Paint the image onto the temp image
      drawPieces(Orginalcopy,
                 new PicPixels(subPiecesPixels[i].filePath, subWidth, subHeight),
                 w, h, tempImage);
    }

    //set the window to be used
    windowCameFrom.setEnabled(true);
    windowCameFrom.toFront();

    //get rid of the wait window
    waitWindow.dispose();

    return image;
  }

}
