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

/** An instance is a JFrame that contains an image with a title and
  buttons to alter the image.
  When no image is in the JFrame, it is hidden.
  When an image is placed in it, it becomes visible. */
public class ImageGUI extends JFrame implements ActionListener {
    
    // contains the original and current ImageArrays and
    // methods that manipulate them.
    private ImageProcessor processor; 
    
    // Buttons
    private JButton buttonrestore= new JButton("restore");
    private JButton buttoninvert= new JButton("invert");
    private JButton buttontranspose= new JButton("transpose");
    private JButton buttonhreflect= new JButton("hor reflect");
    private JButton buttonvreflect= new JButton("ver reflect");
    private JButton buttonfilterout= new JButton("filter out");
    private JButton buttonhide= new JButton("hide message");
    private JButton buttonreveal= new JButton("reveal message");
    private Box buttonBox= new Box(BoxLayout.Y_AXIS);
    
    // Three radio buttons
    private JRadioButton greyButton= new JRadioButton("all color", true);
    private JRadioButton redButton= new JRadioButton("red", false);
    private JRadioButton greenButton= new JRadioButton("green", false);
    private JRadioButton blueButton= new JRadioButton("blue", false);
    private Box checkboxBox= new Box(BoxLayout.X_AXIS);
    
    private ButtonGroup group= new ButtonGroup();
    
    
    /** The panel with the original image, the panel with the current image,
      and the box that contains both. */
    private ImagePanel originalPanel;
    private ImagePanel currentPanel;
    Box inputBox= new Box(BoxLayout.Y_AXIS);
    
    // The text area for the user to give a message
    private JTextArea messageArea= new JTextArea(10, 20);
    private JScrollPane scrollPane= new JScrollPane(messageArea);
    private JLabel jlabel= new JLabel("Type the message to be hidden in this text area:");
    Box areaBox= new Box(BoxLayout.Y_AXIS);
    
    // imageBox contains the original image and the current one
    Box imagebox= new Box(BoxLayout.X_AXIS);
    
    /** Constructor: an instance for Image im.
      If im is null, the window is not shown. */
    public ImageGUI(Image im) {
        super();
        setUp(im, "An image");
    }
    
    
    /** Constructor: an instance for an Image that is found using a file dialog
      window. If no Image is produced using the file dialog,
      use null as the Image. */
    public ImageGUI() {
        String f= getImageName();
        Image im= getImage(f, this);
        setUp(im, "image: " + f); 
    }
    
    /** = the current panel */
    public ImagePanel getCurrentPanel() {
        return currentPanel;
    }
    
    /** = the original panel */
    public ImagePanel getOriginalPanel() {
        return originalPanel;
    }
    
    /** = the ImageProcessor for the image. */
    public ImageProcessor getProcessor() {
        return processor;
    }
    
    /** A jframe with a picture in it and the title
      "picture". The picture is obtained from the user
      using a dialog window. */
    public static void main(String[] pars) {
        ImageGUI ij;
        ij= new ImageGUI();
    }
    
    
    
    /** Set up JFrame with i, with title t. This includes creating the
      ImageProcessor, adding buttons and checkboxes to the GUI, 
      performing a few other minor operations to make the GUI
      work properly, and placing the image in the JFrame.*/
    private void setUp(Image im, String t) {
        setTitle(t);
        
        // Build box buttonBox of buttons.
        buttonBox.add(buttonrestore);
        buttonBox.add(buttoninvert);
        buttonBox.add(buttontranspose);
        buttonBox.add(buttonhreflect);
        buttonBox.add(buttonvreflect);
        buttonBox.add(buttonfilterout);
        buttonBox.add(buttonhide);
        buttonBox.add(buttonreveal);
        getContentPane().add(BorderLayout.WEST, buttonBox);
        
        // Register this instance as a listener for all buttons.
        buttonrestore.addActionListener(this);
        buttoninvert.addActionListener(this);
        buttontranspose.addActionListener(this);
        buttonhreflect.addActionListener(this);
        buttonvreflect.addActionListener(this);
        buttonfilterout.addActionListener(this);
        buttonhide.addActionListener(this);
        buttonreveal.addActionListener(this);
        
        // Build box inputBox of the color buttons.
        checkboxBox.add(greyButton);
        checkboxBox.add(redButton);
        checkboxBox.add(greenButton);
        checkboxBox.add(blueButton);
        inputBox.add(checkboxBox);
        
        // Add the color buttons to group.
        group.add(greyButton);
        group.add(redButton);
        group.add(blueButton);
        group.add(greenButton);
        
        getContentPane().add(BorderLayout.NORTH, inputBox);
        
        // Add a text area with label
        areaBox.add(jlabel);
        areaBox.add(scrollPane);
        getContentPane().add(BorderLayout.SOUTH, areaBox);
        
        // If there is no image, make JFrame invisible and return
        if (im == null) {
            setVisible(false);
            return;
        }
        
        // Create the two panels and the ImageProcessor processor.
        originalPanel= new ImagePanel(im);
        currentPanel= new ImagePanel(im);
        
        // Add the panels to the GUI, in imagebox.
        imagebox= new Box(BoxLayout.X_AXIS);
        imagebox.add(originalPanel);
        imagebox.add(currentPanel);
        getContentPane().add(BorderLayout.EAST, imagebox);
        
        // Create an instance of ImageProcessor to manipulate the image.
        ImageArray intIm= new ImageArray(im,
                                       im.getHeight(originalPanel), im.getWidth(originalPanel));
        processor= new ImageProcessor(intIm);
        
        
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setLocation(200,100);
        pack();
        setVisible(true);
        repaint();
    }
    
    /** Pause the program for d microseconds */
    public void pause(int d) {
        try { Thread.currentThread().sleep(d); }
        catch (InterruptedException e) { }
    }
    
    /** = the grey check box in the GUI is selected */
    private boolean greyIsChecked() {
        return greyButton.isSelected();
    }
    /** = the red check box in the GUI is selected */
    private boolean redIsChecked() {
        return redButton.isSelected();
    }
    
    /** = the green check box in the GUI is selected */
    private boolean greenIsChecked() {
        return greenButton.isSelected();
    }
    
    /** = the blue check box in the GUI is selected */
    private boolean blueIsChecked() {
        return blueButton.isSelected();
    }
    
    /** Change current image to the map given by 
      processor.getCurrentImage(). */
    private void changeCurrentImage() {
        currentPanel.changeImageTo(processor.getCurrentImage());
        pack();
        repaint();
    }
    
    /** = the file name of an image loaded by the user using a dialog window */
    public static String getImageName() {
        FileDialog fd = new FileDialog(new Frame(), "Open File", FileDialog.LOAD);
        fd.setVisible(true);
        if (fd.getFile() == null) 
            return null;
        return fd.getDirectory() + fd.getFile();
    }
    
    /** = the image for file name f, using the toolkit for jframe */
    public static Image getImage(String f, JFrame jframe) {
        Image image= null;
        try {
            image= jframe.getToolkit().getImage(new URL("file:" + f));
            
        } 
        catch (MalformedURLException e) {
            System.err.println("Bad URL!");
            return null;
        }
        
        // set media tracker to wait for image to load
        MediaTracker tracker= new MediaTracker(jframe);
        tracker.addImage(image,0);    
        
        // wait for image to load
        try { tracker.waitForID(0); 
        }
        catch (InterruptedException e) {
            // handler.flashMessage(ImageHandler.LOAD_INTERRUPTED);
            return null;
        }
        return image;
    }
    
    /** Process a click of one of the buttons */
    public void actionPerformed(ActionEvent e) {
        
        if (e.getSource() == buttonrestore) {
            processor.restore();
            changeCurrentImage();
            return;
        }
        if (e.getSource() == buttoninvert) {
            processor.invert();
            changeCurrentImage();
            return;
        }
        if (e.getSource() == buttontranspose) {
            processor.transpose();
            changeCurrentImage();
            return;
        }
        if (e.getSource() == buttonhreflect) {
            processor.hreflect();
            changeCurrentImage();
            return;
        }
        if (e.getSource() == buttonvreflect) {
            processor.vreflect();
            changeCurrentImage();
            return;
        }
        
        if (e.getSource() == buttonfilterout) {
            if (greyIsChecked()) {
                processor.filterOut(ImageProcessor.GRAY);
                changeCurrentImage();
                return;
            }
            if (redIsChecked()) {
                processor.filterOut(ImageProcessor.RED);
                changeCurrentImage();
                return;
            }
            if (greenIsChecked()) {
                processor.filterOut(ImageProcessor.GREEN);
                changeCurrentImage();
                return;
            }
            if (blueIsChecked()) {
                processor.filterOut(ImageProcessor.BLUE);
                changeCurrentImage();
                return;
            }
        }
        if (e.getSource() == buttonhide) {
            String m= messageArea.getText();
            boolean b= processor.hide(m);
            if (b) {
                messageArea.setText("Message of length " + m.length() + " was hidden.\n");
                changeCurrentImage();
            } else {
                messageArea.setText("Message too long to be hidden:\n" + m);
            }
            
            return;
        }
        if (e.getSource() == buttonreveal) {
            String m= processor.reveal();
            if (m == null) {
                messageArea.setText("No message found to reveal.");
            } else {
                messageArea.setText(m);
            }
            return;
        }
        
    }
    
    
    /** Invert the image */
    public void invert() {
        processor.invert();
        changeCurrentImage();
    }
    
    /** Reveal the message that is hidden in this picture --give
      a message stating that there is none if there is none.
      The output is in the interactions pane as well as the GUI. */
    public void reveal() {
        String m= processor.reveal();
        if (m == null) {
            messageArea.setText("No message found to reveal.");
            System.out.println("No message found to reveal.");
        } else {
            messageArea.setText(m);
        }
    }
    
    /** Reveal the message that is hidden in this picture --give
      a message stating that there is none if there is none.
      The output is in the interactions pane as well as the GUI.
      Using an algorithm that takes time proportional to the square
      of the length of the message being revealed. */ 
    public void reveal1() {
        String m= processor.reveal1();
        if (m == null) {
            messageArea.setText("No message found to reveal.");
            System.out.println("No message found to reveal.");
        } else {
            messageArea.setText(m);
        }
    }
     
    /** Hide message m in the image. Give message if not possible. */
    public void hide(String m) {
        boolean successful= processor.hide(m);
        if (successful) {
            System.out.println("Message was hidden.");
            changeCurrentImage();
        } else {
            messageArea.setText("Message too long to be hidden: " + m.length() + " characters");
            System.out.println("Message too long to be hidden: " + m.length() + " characters"); 
        }
    }
    
    /** Transpose the image */
    public void transpose() {
        processor.transpose();
        changeCurrentImage();
    }
    
    /** Reflect the image around the horizontal middle */
    public void hreflect() {
        processor.hreflect();
        changeCurrentImage();
    }
    
    /** Reflect the image around the vertical middle */
    public void vreflect() {
        processor.vreflect();
        changeCurrentImage();
    }
    
    /** Restore the original image */
    public void restore() {
        processor.restore();
        changeCurrentImage();
    }
    
    /** Filter the image according to the grey-red-blue-green buttons */
    public void filter() {
        if (greyIsChecked()) {
            processor.filterOut(ImageProcessor.GRAY);
            changeCurrentImage(); 
            return;
        }
        if (redIsChecked()) {
            processor.filterOut(ImageProcessor.RED);
            changeCurrentImage();
            return;
        }
        if (greenIsChecked()) {
            processor.filterOut(ImageProcessor.GREEN);
            changeCurrentImage();
            return;
        }
        if (blueIsChecked()) {
            processor.filterOut(ImageProcessor.BLUE);
            changeCurrentImage();
            return;
        }
    }
    
    /** Provided the file does not yet exist, store the image as a jpg file in 
      the current directory with name with file-name fileName. 
      Put a message on the console indicating whether or not it has been written.
      Note: It is a good idea to include the extension ".jpg" as part of the file name.
      */
    public void writeImage(String fileName) throws java.io.IOException {
        processor.writeImage(fileName);
    }
    
}
