/**
 * @author Derek Babb <derek.babb@bpsne.net>
 * @version 1.1
 * @since 7-2-2013
 *
 * The PictureEdit class allows you to easily open and modify an image
 * at the pixel level.
 * 
 * New in version 1.1, ability to create a new file of given width and height
 *
 */

 import java.awt.image.BufferedImage;
 import javax.swing.filechooser.FileNameExtensionFilter;
 import javax.swing.JFileChooser;
 import javax.swing.JFrame;
 import javax.swing.JPanel;
 import javax.imageio.ImageIO;
 import java.io.File;
 import java.awt.Image;
 import java.util.Date;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.awt.Graphics;
 import java.awt.Color;

 public class PictureEdit extends JPanel
 {
  private BufferedImage image;
  private BufferedImage revertImage;
  private File file;

  /**
   * Constructor will use the JFileChooser to open a file
   */

  public PictureEdit()
  {
   image = null;
   this.open();
  }

  /**
   * Constructor will use the JFileChooser to open a file
   *
   * @param fileName is the String file name including the directory path
   */

  public PictureEdit(String fileName)
  {
   image = null;
   this.open(fileName);
  }

  /**
   * Constructor will use the JFileChooser to open a file
   *
   * @param file is the File object of the picture that will be opened.
   */

  public PictureEdit(File file)
  {
   image = null;
   this.open(file);
  }
  
  /**
   * Constructor will use the JFileChooser to open a file
   *
   * @param the width and height of the desired blank picture.
   */

  public PictureEdit(int width, int height)
  {
    image = buildImage(width, height);
    revertImage = copyImage(image);
  }
  

  /**
   * Opens a file by opening a JFileChooser which allows the user to select
   * the file they would like to open.
   *
   * @return true if the file successfully opened, false otherwise.
   */

  public boolean open()
  {
  JFileChooser chooser = new JFileChooser(".");
     FileNameExtensionFilter filter = new FileNameExtensionFilter("PNG Images", "png");
     chooser.setFileFilter(filter);
     int returnVal = chooser.showOpenDialog(null);
     if(returnVal == JFileChooser.APPROVE_OPTION) {
   try{
    file = chooser.getSelectedFile();
    image = ImageIO.read(file);
    revertImage = copyImage(image);
   }
   catch(Exception e){
    return false;
   }

   return true;
     }
     else
     {
      return false;
     }
  }

  /**
   * Opens an image file based on the file name in String format
   *
   * @param fileName the name of the file including file path.
   * @return true if the file is successfully opened, false otherwise.
   */

   public boolean open(String fileName)
   {
   try{
    file = new File(fileName);
   image = ImageIO.read(file);
   revertImage = copyImage(image);
  }
  catch(Exception e){
   return false;
  }
  return true;
   }

  /**
   * Opens an image file given a File object.
   *
   * @param file the File object of the image file.
   * @return true if the file is successfully opened, false otherwise.
   */

 public boolean open(File file)
 {
  try{
   image = ImageIO.read(file);
   revertImage = copyImage(image);
  }
  catch(Exception e){
   return false;
  }
  return true;
  }

  /**
   * Reverts to the original file, all unsaved changes are lost.
   */

  public void revert()
  {
   image = copyImage(revertImage);
  }




  /**
   * Saves a file by opening a JFileChooser which allows the user to select
   * the file they would like to save over or create a new file to save onto.
   *
   * @return true if the file successfully opened, false otherwise.
   */

  public boolean saveAs()
  {
   JFileChooser chooser = new JFileChooser(".");
     int returnVal = chooser.showSaveDialog(null);
     if(returnVal == JFileChooser.APPROVE_OPTION) {
   try{
   ImageIO.write(image, "png",chooser.getSelectedFile());
   }
   catch(Exception e){
    return false;
   }
   return true;
     }
     return false;

 }

 /**
   * Saves a file by writing a new file with the given fileName String.
   * CAUTION: This will overwrite any file with the name provided.  No confirmation
   * dialog.
   *
   * @param fileName is a String with the file name and path directory.
   *
   * @return true if the file successfully opened, false otherwise.
   */

 public boolean saveAs(String fileName)
  {

  try{
  ImageIO.write(image, "png",new File(fileName));
  }
  catch(Exception e){
   return false;
  }
  return true;


 }

 /**
   * Returns the width of the image file
   *
   * @return integer value of the width.  Will return 0 if there is no file currently open.
   */
  public int getWidth()
  {
   if(image == null)
   {
    return 0;
   }
   else
   {
    return image.getWidth();
   }
  }

 /**
   * Returns the height of the image file
   *
   * @return integer value of the height.  Will return 0 if there is no file currently open.
   */

  public int getHeight()
  {
   if(image == null)
   {
    return 0;
   }
   else
   {
    return image.getHeight();
   }
  }

 /**
   * Returns the image file as an Image object.  This might be used to easily display the open image.
   *
   * @return Image object of the currently open image.
   */

  public Image getImage()
  {
   return (Image)image;
  }

 /**
   * Displays the current image in a new JFrame.
   * The title of the JFrame will be the timestamp of when it was displayed.
   * The timestamp allows the user to see multiple revisions and know which came first.
   *
   */

   public void displayImage()
   {
    DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
  Date date = new Date();
   displayImage(dateFormat.format(date));
   }

   /**
   * Displays the current image in a new JFrame.
   * The title of the window is given as a paramater.
   *
   * @param title Title of the window.
   *
   */

   public void displayImage(String title)
   {
    new Window(image, title);
   }


  /**
   * Returns the integer value of the color at a given point.
   * Will return a -1 if the coordinate is outside the width/height of the open image.
   *
   * @param x x-coordinate of the selected pixel
   * @param y y-coordinate of the selected pixel
   *
   * @return integer value of the color at (x,y) coordinate.
   */

   public int getRGBColor(int x, int y)
   {
    if(x < image.getWidth() && y < image.getHeight())
    {
     return image.getRGB(x,y);
    }
    else
    {
     return -1;
    }
   }


 /**
   * Returns a Color object of the color at a given point.
   * Will return null if the coordinate is outside the width/height of the open image.
   *
   * @param x x-coordinate of the selected pixel
   * @param y y-coordinate of the selected pixel
   *
   * @return Color object of the color at (x,y) coordinate.
   */

   public Color getColor(int x, int y)
   {
    if(x < image.getWidth() && y < image.getHeight())
    {
     return new Color(getRGBColor(x,y));
    }
    else
    {
     return null;
    }
   }

  /**
   * Sets the color at a given x,y coordinate.
   * The color is given in an integer RGB model.
   *
   * If the x,y is outside the image, the method returns false, true otherwise.
   *
   * @param x x-coordinate of the selected pixel
   * @param y y-coordinate of the selected pixel
   * @param color the color in integer RGB form
   *
   * @return Color object of the color at (x,y) coordinate.
   */

   public boolean setColor(int x, int y, int color)
   {
    if(x < image.getWidth() && y < image.getHeight())
    {
     image.setRGB(x, y, color);
     return true;
    }
    return false;
   }

  /**
   * Sets the color at a given x,y coordinate.
   * The color is given as a Color object.
   *
   * If the x,y is outside the image, the method returns false, true otherwise.
   *
   * @param x x-coordinate of the selected pixel
   * @param y y-coordinate of the selected pixel
   * @param color Color object of the color to change to
   *
   * @return Color object of the color at (x,y) coordinate.
   */

   public boolean setColor(int x, int y, Color color)
   {
    if(x < image.getWidth() && y < image.getHeight() && color != null)
    {
     image.setRGB(x, y, color.getRGB());
     return true;
    }
    return false;
   }
   
   private BufferedImage buildImage(int width, int height)
   {
     BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
     return img;
   }

   private BufferedImage copyImage(BufferedImage i)
   {
    BufferedImage img = new BufferedImage(i.getWidth(), i.getHeight(), i.getType());
   for (int y = 0; y < i.getHeight(); y++)
   {
    for (int x = 0; x < i.getWidth(); x++)
    {
     int rgb = i.getRGB(x, y);
     img.setRGB(x, y, rgb);
    }
   }
  return img;
   }
   
  /**
  * Allows the user to get the currently open file as an object.
  * 
  * @Return returns the File object of the current open file.
  * 
  */
 
 public File getFile()
 {
   return file;
 }


 private class Window extends JPanel
 {
  private BufferedImage img;


  public Window(BufferedImage i, String title)
  {
   img = copyImage(image);
     JFrame jf = new JFrame(title);

   jf.setSize(img.getWidth() + 16, img.getHeight() + 38);
   jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
   jf.getContentPane().add(this);
   jf.setVisible(true);
  }


   /**
     * @Override to display image
     */
    public void paintComponent(Graphics g)
    {
     super.paintComponent(g);
     g.drawImage(img,0,0,this);
    }
 }


 }