import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.net.*;

/** An instance of this class is a window that contains the menu bar,
 message, and panel of images. The menu items provide for opening and
 closing images (jpg and gif files) and for manipulating them --inverting
 the, reflecting them, and transposing them.
 This class also contains a method
 main, which simply creates an instance of the class. */
public class ImageHandler extends JFrame {
    
    /** Messages */
    public static final String LOAD_INTERRUPTED= "Image loading interrupted";
    public static final String NORMAL= "Select or load an image";
    public static final String IMAGE_LIMIT_EXCEEDED= "Image limit exceeded";
    public static final String NO_IMAGE= "No image selected";
    
    // This JFrame contains the currently selected image (null if none)
    private ImageJFrame selectedImage= new ImageJFrame(null, null);
    
    private JMenuBar menuBar= new JMenuBar();     // the menu bar
    private JLabel message= new JLabel(NORMAL);   // the message label
    
    Box topImages= new Box(BoxLayout.X_AXIS);
    Box bottomImages= new Box(BoxLayout.X_AXIS);
    Box rows= new Box(BoxLayout.Y_AXIS);
    
    // The loaded images. The images are in images[0..numberImages-1]
    // The first five images appear in topImages.
    // The second five images appear in bottomImages.
    // images[highlighted] is the highlighted window (highlighted=-1 if none)
    private ImageMaintainer[] images= new ImageMaintainer[10];
    private int numberImages= 0;
    private int highlighted= -1;
    
    private ImageHandler thisHandler;  // this object itself
    
    /** Main program: create and show one imageHandler */
    public static void main(String[] par) {  
        ImageHandler im= new ImageHandler();
        
    }
        
        
    
    /** = the highlighted ThumbnailPanel (null if none) */
    public ThumbnailPanel getHighlighted() {
        if (highlighted == -1)
            return null;
        return images[highlighted].getThumbnail();
    }
    
    /** Add image im to the loaded images, if there is room, and make it
        the highlighted one.
        Flash a message if there is no room. */
    public void addImage(ImageMaintainer im) {
        if (numberImages == 10) {
            flashMessage("There is no room to add an image");
        }
        images[numberImages]= im;
        if (numberImages <= 4) {
            topImages.add(im.getThumbnail(), "title");
        }
        else {
            bottomImages.add(im.getThumbnail(), "title");
        }
        highlighted= numberImages;
        numberImages= numberImages+1;
        // Set the preferred size of the panel of thumbnails
        setPreferredSizeOfBox();
        
        pack();
        repaint();
    }
    
    /** Remove highlighted image from the loaded images (if one is highlighted)
        and make either the one before or after it highlighted.
      */
    public void removeImage() {
        if (numberImages == 0)
            return;
        if (highlighted == -1)
            return;
        
        // Remove the thumbnail
        if (highlighted < 5) {
            topImages.remove(images[highlighted].getThumbnail());
        }
        else {
            bottomImages.remove(images[highlighted].getThumbnail());
        }
        
        // If the highlighted thumbnail was in the top row and there is a
        // thumbnail in the bottom row, move the
        // first thumbnail of the bottom row up to the top row.
        if (highlighted < 5 && numberImages >= 6) {
            bottomImages.remove(images[5].getThumbnail());
            topImages.add(images[5].getThumbnail());
        }
        
        // Move the images down to fill the hole
        for (int i= highlighted+1; i != numberImages; i= i+1) {
            images[i-1]= images[i];
        }
        numberImages= numberImages-1;
        if (highlighted == numberImages)
            highlighted= highlighted - 1;
        
        if (highlighted != -1) {
            ImageMaintainer it= images[highlighted];
            selectedImage.placeImage(it.getImage(), it.getFileName());
        }
        else selectedImage.placeImage(null,null);
        pack();
        setVisible(true);
        repaint();
    }
    
    
    /** Constructor: A window, with a menu bar */
    public ImageHandler() {
        thisHandler= this;
        setTitle("Image Handler");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        
        setPreferredSizeOfBox();
        
        
        rows.add(topImages);
        rows.add(bottomImages);
        rows.setBorder(new EmptyBorder(0,10,0,10));
        for (int c= 0; c != images.length; c= c+1) {
            images[c]= null;
        }
        
        // set up the file menu
        JMenu file= new JMenu("File");
        JMenuItem fileOpen= new JMenuItem("Open");
        JMenuItem fileClose= new JMenuItem("Close");
        JMenuItem fileExit= new JMenuItem("Exit");
        fileOpen.addActionListener(new OpenActionListener());
        fileClose.addActionListener(new CloseActionListener());
        fileExit.addActionListener(new ExitActionListener());
        file.add(fileOpen);
        file.add(fileClose);
        file.addSeparator();
        file.add(fileExit);
        menuBar.add(file);
        
        // set up the image menu
        JMenu im= new JMenu("Image");
        JMenuItem inv= new JMenuItem("Invert");
        JMenuItem trans= new JMenuItem("Transpose");
        JMenuItem href= new JMenuItem("Horizontally Reflect");
        JMenuItem vref= new JMenuItem("Vertically Reflect");
        JMenuItem rest= new JMenuItem("Restore");
        inv.addActionListener(new InvertActionListener());
        trans.addActionListener(new TransActionListener());
        href.addActionListener(new HRefActionListener());
        vref.addActionListener(new VRefActionListener());
        rest.addActionListener(new RestActionListener());
        im.add(inv);
        im.add(trans);
        im.add(href);
        im.add(vref);
        im.addSeparator();
        im.add(rest);
        menuBar.add(im);
        
        // set up the help menu
        JMenu help= new JMenu("Help");
        JMenuItem showHelp= new JMenuItem("Show help");
        showHelp.addActionListener(new HelpActionListener());
        help.add(showHelp);
        menuBar.add(help);
        
        // add components to frame
        getContentPane().add(message,BorderLayout.NORTH);
        getContentPane().add(rows,BorderLayout.CENTER);
        setJMenuBar(menuBar);
        
        pack();
        setVisible(true);
    }
    
    /** display message m for 2 seconds */
    public void flashMessage(String m) {
        ErrorThread thread = new ErrorThread(m);
        thread.start();    
    }
    
    /** Set the message field to m */
    public void setMessage(String m) {
        message.setText(m);
        repaint();
    }
    
    
    /** the message flash must be run in a thread
     so that the pause doesn't halt the GUI */
    class ErrorThread extends Thread {
        
        String error;  // the message to be flashed
        
        /** Constructor: A thread with error er */
        public ErrorThread(String er) {
            error = er;
        }
        
        /** the run method */
        public void run() {
            setMessage(error);
            try {
                sleep(2000);
            } catch (InterruptedException e) {}
            setMessage(NORMAL);
        }
    }
    
    
    /** Process menu File item Open */
    class OpenActionListener implements ActionListener {
        /** Open an image, if possible. Make it the highlighted one */
        public void actionPerformed(ActionEvent e) {
            if (numberImages == 10) {
                flashMessage(IMAGE_LIMIT_EXCEEDED);
                return;
            }
            
            String fileName= ImageMaintainer.getImageName();
            Image im= ImageMaintainer.getImage(fileName, selectedImage);
            if (fileName == null) 
                return;
            addImage(new ImageMaintainer(im, fileName, thisHandler));
            
            selectedImage.placeImage(im, fileName);
        }
    }
    
    /** Process menu File item Close */
    public class CloseActionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            removeImage();
        }
    }
    
   /** highlight thumbnail tp.
       Precondition: tp should be in array images
       */
    public void highlightThumbnail(ThumbnailPanel tp) {
        highlighted= 0;
        while (images[highlighted].getThumbnail() != tp) {
             highlighted= highlighted+1;
        }
        highlightThumbnail();
    }
   
      /** Highlight images[highlighted]-- but if highlighted = -1,
          don't highlight anything
       */
    public void highlightThumbnail() {
        if (highlighted != -1) {
            ImageMaintainer it= images[highlighted];
            selectedImage.placeImage(it.getImage(), it.getFileName());
        }
        else {
            selectedImage.placeImage(null,null);
        }
        repaint();
    }
    
    /** Set the preferred size of the Box of rows of thumbnails to 
        hold all the thumbnails */
    public void setPreferredSizeOfBox() {
        if (numberImages == 0) {
            int w= ThumbnailPanel.SIZE + ThumbnailPanel.WIDTH_PADDING;
            rows.setPreferredSize(new Dimension(2*w, 100));
            return;
        }
        // Calculate the width w of the Box
        int w= (ThumbnailPanel.SIZE+ThumbnailPanel.WIDTH_PADDING) *
                   Math.min(numberImages,5);
        
        // Calculate the height h1 of the first row. The thumnails may have different heights
        int h1= images[0].getThumbnail().newGetHeight();
        //int h1= 60;
        
        // inv: 1<=i<=5 and h1 is the max height of images images[0..i-1]
        int m= Math.min(numberImages, 5);
        for (int i= 1; i != m; i= i+1) {
            h1= Math.max(h1, images[i].getThumbnail().newGetHeight());
        }
        h1= h1 + 12; // Add in height of title
        
        // Calculate the height h2 of the second row. If it's empty, the height is 0
        int h2= 0;
        // inv: 5<=i<=numberImages and h2 is the max height of images images[1..i-1]
        for (int i= 5; i < Math.max(numberImages,5); i= i+1) {
            h2= Math.max(h2, images[i].getThumbnail().newGetHeight());
        }
        if (h2 != 0) {
            h2= h2 + 12;   // Add in height of title
        }
        int minWidth= 2 * (ThumbnailPanel.SIZE + ThumbnailPanel.WIDTH_PADDING);
            
        rows.setPreferredSize(new Dimension(Math.max(minWidth, w), h1 + 15 + h2));
    }
    
    /** Process menu item Invert */
    class InvertActionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if (highlighted != -1) {
                images[highlighted].invert();
                selectedImage.setImage(images[highlighted].getImage());
                repaint();
            }
            else flashMessage(NO_IMAGE);
        }
    } 
    
    /** Process menu item Transpose */
    class TransActionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if (highlighted != -1) {
                images[highlighted].transpose();
                selectedImage.setImage(images[highlighted].getImage());
                repaint();
            }
            else flashMessage(NO_IMAGE);
        }
    }
 
    
    /** Process menu item Horizontally reflect */
    class HRefActionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if (highlighted != -1) {
                images[highlighted].hreflect();
                selectedImage.setImage(images[highlighted].getImage());
                repaint();
            }
            else flashMessage(NO_IMAGE);
        }
    }
    
    /** Process menu item Vertically reflect */
    class VRefActionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if (highlighted != -1) {
                images[highlighted].vreflect();
                selectedImage.setImage(images[highlighted].getImage());
                repaint();
            }
            else flashMessage(NO_IMAGE);
        }
    }
    
    /** Process menu item Restore */
    class RestActionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if (highlighted != -1) {
                images[highlighted].restore();
                selectedImage.setImage(images[highlighted].getImage());
                repaint();
            }
            else flashMessage(NO_IMAGE);
        }
    }
    
       /** Process menu File item Exit */
    class ExitActionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }
    }

    
    /** Process menu item Help */
    class HelpActionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            String helps= "To load a new picture, use menu File item open. You can " +
                "load .jpeg files and .gif files.\n" +
                "\nTo close a picture, highlight it and use menu File item close.\n" +
                "\nTo terminate the application, use either the close box or " +
                "menu File item Exit.\n" +
                "\nIn menu Image, you can use the following items, " +
                "on the currently highlighted picture." +
                "\n    Invert \n    Transpose \n    Horizontally Reflect, \n    Vertically Reflect " +
                "\nwhich perform obvious transformations on the highlighted picture." +
                "\nThis item restores the original picture:" +
                "\n    Restore";
            
            TextWindow w= new TextWindow(helps,"Image Help", 60);
        }
    } 
    
    
    
}