import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.imageio.*;
import java.net.*;
import java.io.*;


/** An instance contains 
  (1) an original image (of class ImageArray),
  (2) a possibly altered (by methods in this instance) version of the original image,
  (3) methods to process the image
  */
public class ImageProcessor {
    
    /** DM provides methods for extracting components of an rgb pixel. */
    public static final DirectColorModel DM= (DirectColorModel) ColorModel.getRGBdefault();
    
    /** The following four constants of this class indicate a color.*/
    /** Color gray */
    public static final int GRAY= 0;
    
    /** Color red */
    public static final int RED= 1;
    
    /** Color green */
    public static final int GREEN= 2;
    
    /** Color blue */
    public static final int BLUE= 3;
    
    private ImageArray originalIm; // Original image, make it easy to restore
    private ImageArray currentIm;  // Altered image.
    
    /** Constructor: an instance for im.
      Precondition: im != null. */
    public ImageProcessor(ImageArray im) {
        originalIm= im;
        currentIm= originalIm.copy();
    }
    
    /** = current image. */
    public ImageArray getCurrentImage() {
        return currentIm;
    }
    
    /** = the original image. */
    public ImageArray getOriginalImage() {
        return originalIm;
    }
    
    /** Invert current image: replace each element with its color complement. */
    public void invert() {
        int len= currentIm.getRows() * currentIm.getCols();
        
        // invert all pixels (leave alpha/transparency value alone)
        
        // invariant: pixels 0..p-1 have been complemented.
        for (int p= 0; p < len; p= p+1) {
            int rgb= currentIm.getPixel(p);
            int red= 255 - DM.getRed(rgb);
            int blue= 255 - DM.getBlue(rgb);
            int green= 255 - DM.getGreen(rgb);
            int alpha= DM.getAlpha(rgb);
            currentIm.setPixel(p,
                               (alpha << 24) | (red << 16) | (green << 8) | blue);
        }
    }
    
    /** Transpose current image.  */
    public void transpose() {
        // Follow this plan: 
        // (1) Create a new ImageArray ia, using currentIM's row-major order array
        //     and rows and columns, but swap the roles of its numbers
        //     of rows and columns.
        // (2) Store the transpose of currentIm array in ia, using currentIm's
        //     2-parameter getPixel function and ia's 3-parameter setPixel
        //     function.
        // (3) assign ia to currentIm.
       
        
        
        
        
    }
    
    /** Reflect current image around the horizontal middle. */
    public void hreflect() {
        int rows= currentIm.getRows();
        int cols= currentIm.getCols();
        int h= 0;
        int k= rows-1;
        //invariant: rows 0..h-1 and k+1.. have been inverted
        while (h < k) {
            // Swap row h with row k
            // invariant: pixels 0..c-1 of rows h and k have been swapped
            for (int c= 0; c != cols; c= c+1) {
                currentIm.swapPixels(h, c, k, c);
            }
            
            h= h+1; k= k-1;
        }
    }
    
    /** Reflect current image around the vertical middle. */
    public void vreflect() {
        
        
        
        
        
        
        
    }
    
    /* Filter out color from current image according to parameter c.<br><br>
     Precondition: c is one of the four color constants GRAY,
     RED, GREEN, BLUE of this class.<br><br>
     
     If c is one of RED, GREEN, BLUE, then filter out that color of the image
     by setting it to 0 in all pixels. If c is GRAY, then filter all color out
     of the image by setting the three color components of each pixel to the
     average of the three.
     
     The alpha component is not changed.
     */
    public void filterOut(int c) {
        
        
        
        
        
        
    }
    

    
    /** Hide message m in this image, using the ascii representation of m's chars.
      Return true if this is possible and false if not.
      If m has more than 999999 characters or the picture doesn't have enough
      pixels, return false without storing the message.
      */
    public boolean hide(String m) {
        
        
        
        
        
        return false;
    }
    

    
    /** Extract and return the message hidden in current image.
      Return null if no message detected. */
    public String reveal() {
        
        
        
        
        return null;
    }
    
    /** Extract and return the message hidden in current image.
      Return null if no message detected. */
    public String reveal1() {
       // Note. You do not have to write this method.
       // We'll explain in class some time why this is here.
        
        
        
        
        return null;
    }
    
    /* Assuming the image is broken up into blocks, with nr rows
     and nc columns of blocks, set all pixels of the
     block at position (row, col) to pixel value pixel.
     Precondition: 0 ² row < nr  and  0 ² col < nc */
    public void setBlock(int nr, int nc, int row, int col, int pixel) {
        int height= currentIm.getRows() / nr;
        int width= currentIm.getCols() / nc;
        for (int r= 0; r < height; r= r+1) {
            for (int c=0; c < width; c= c+1) {
                currentIm.setPixel(row*height+r, col*width+c, pixel);
            }
        }
    }
    
    /* Assuming the image is broken up into blocks, with nr rows
     of blocks and nc columns of blocks, swap all pixels of
     the blocks at positions (row0, col0) and (row1, col1). */
    public void swapBlocks(int row0, int col0, int row1, int col1, int nr, int nc) {
        int rows= currentIm.getRows();
        int cols= currentIm.getCols();
        int ht= rows/nr;
        int wd= cols/nc;
        for (int r= 0; r < ht; r= r+1) {
            for (int c= 0; c < wd; c= c+1) {
                currentIm.swapPixels(row0*ht+r, col0*wd+c, row1*ht+r, col1*wd+c);
                
            }
        }
    }
    
    /** Restore the original image in current one */
    public void restore() {
        currentIm= originalIm.copy();
    }
    
    /** Provided file fname does not appear in current directory, store the
      current image in file fname in current directory, as a jpg file.
      Write a message on the console indicating whether or not the write was successful. */
    public void writeImage(String fname) throws java.io.IOException {
        File f= new File(fname);
        if (f.exists()) {
            System.out.println("File " + f.getAbsolutePath() + " exists. It was not overwritten.");
            return;
        }
        
        int r= currentIm.getRows();
        int c= currentIm.getCols();
        int roa[]= currentIm.getRmoArray();
        
        // Create an image from roa. The new Container is needed to get an
        // object that has the appropriate function, createImage, in it; that is all.
        Image image= (new Container()).createImage(new MemoryImageSource(c, r, roa, 0, c));
        
        // Obtain a buffered image and the graphics for it. It has to have the right width and height.
        // The third arg of the call below indicates that input into it will be an integer array of
        // rgb colors
        BufferedImage bimage= 
            new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
        Graphics g= bimage.createGraphics();
        
        
        // put the image into bimage so that it can be written out and dispose of the graphics.
        g.drawImage(image, 0, 0, null);
        g.dispose();
        
        // Finally, write the image onto the file and give the appropriate message
        ImageIO.write(bimage, "jpg", f);
        System.out.println("Image written to " + f.getAbsolutePath());
    }  
    
}
