/*
 * ImageViewer.java
 *
 * Created on April 7, 2006, 7:22 PM
 *
 * To change this template, choose Tools | Options and locate the template under
 * the Source Creation and Management node. Right-click the template and choose
 * Open. You can then make changes to the template in the Source Editor.
 */

import java.lang.Thread;
// Java Abstract Windowing Toolkit packages
import java.awt.*;
import java.awt.event.*;
// Java I/O packages
import java.io.FileNotFoundException;
// Java network packages
import java.net.MalformedURLException;
import java.net.URL;
// Java Advanced Imaging packages
import javax.swing.*;
import javax.media.jai.Interpolation;
import javax.media.jai.JAI;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.TiledImage;
import com.sun.media.jai.widget.DisplayJAI;

/**
 *
 * @author Willard C. Smith
 */
public class ImageViewer extends javax.swing.JApplet {
    // Points for start and end values of rubber band selection area
    private Point startPoint = null;
    private Point endPoint = null;
    // Image and Window height and width for computing center
    private int imageHeight = 0;
    private int imageWidth = 0;
    private int windowHeight = 0;
    private int windowWidth = 0;
    // Location of window X and Y relative to the image
    private int windowX = 0;
    private int windowY = 0;
    // The dimensions of the Applet window
    private Dimension dimension = null;
    // Image filename
    private String filename = "";
    // Image URL
    private URL imageURL = null;
    // Image relate
    private PlanarImage image = null;
    private TiledImage tiledImage = null;
    private DisplayJAI display = null;
    private RubberBandSelection rubberBand;
    // Applet background color set to light blue
    private static final Color BACKGROUND = new Color( 227,237,247 );
    // Default image to load
    private static final String DEFAULT_FILE = "images/faa.jpg";
    // Applet Parameter Names
    private static final String IMAGESOURCE = "imagesource";
    private static final String IMAGESTARTUP = "startup";
    private static final String IMAGEBACKGROUND = "background";
    private static final String WINDOWHEIGHT = "windowheight";
    private static final String WINDOWWIDTH = "windowwidth";
    // Image border distance for panning
    private static final double PAN_BORDER = 25.0;
    
    // Print debug messages: Enabled = true; Disabled = false
    private static final boolean DEBUG = false;
    
    /**
     * Creates a new instance of ImageViewer
     */
    public ImageViewer() {
        // Everything is done in the init() and start() methods
    }
    
    // Applet specific methods
    
    /**
     * The Applet method init() contains the code that you would normally put
     * into a constructor and is used for one-time initialization.
     */
    public void init() {
        // Get the content pane of the JApplet
        Container content_pane = getContentPane();
        // Set the background color of the Applet window
        setBackground( BACKGROUND );
        // Create the JPanel
        display = new DisplayJAI();
        // Add the JPanel to the JApplet
        content_pane.add( display );
        // Listen for mouse button selections in the JPanel
        display.addMouseListener( new InnerMouseAdapter() );
        // Listen for mouse motions in the JPanel
        display.addMouseMotionListener( new InnerMouseMotionAdapter() );
        // Get the dimensions, width and height, of the Applet
        dimension = getSize();
        // Set the windowHeight and windowWidth
        setWindowDimensions();
        // Get the filename from the web browser applet parameter imagesource
        filename = getParameter( IMAGESOURCE );
        // Check if image source file name is not set in web browser
        if( filename == null ) {
            filename = DEFAULT_FILE;
        }
        // Set the image URL
        setImageURL( filename );
        // Load the image
        imageLoad();
    }
    
    /**
     * The Applet method start() performs the applet's work or starts up one or
     * more threads to perform the work.
     */
    public void start() {
        update( display.getGraphics() );
    }
    
    /**
     * The Applet method stop() suspends the applet's execution, so that it doesn't
     * take up system resources when the user isn't viewing the applet's page.
     */
    public void stop() {
        // 
    }
    
    /**
     * The Applet method destroy() is available for applets that need to release
     * additional resources.
     */
    public void destroy() {
        //
    }
    
    /**
     * The Applet method paint() is used to draw the applet's representation
     * with in a web browser page.
     */
    public void paint( Graphics graphics ) {
        update( graphics );
    }
    
    /**
     * The Applet method update()
     */
    public void update( Graphics graphics ) {
        if( DEBUG ) {
            System.out.println( "Updating Applet Graphics: " + graphics.toString() );
        }
        //
        if( image == null ) {
            imageLoad();
            imageCenter();
         }
        // Display the image
        imageDisplay();
    }
    
    /**
     * The Applet method getParameterInfo() returns an array of Strings, that
     * contains information about the parameters that can be set by the web
     * browser interface.
     */
    public String[][] getParameterInfo() {
        String[][] info = {
            // Parameter Name     Kind of Value   Description
            {"imagesource",     "URL",          "a directory"},
            {"startup",         "URL",          "image displayed at startup"},
            {"background",      "URL",          "image displayed as background"},
            {"windowheight",    "URL",          "window height"},
            {"windowwidth",     "URL",          "window width"},
        };
        return info;
    }
    
    // Accessors
    
    /**
     * The accessor method getStartPoint() returns an instance of Point, the
     * start point of a rectangle to send to the Rubber Band Selection class.
     */
    public Point getStartPoint() {
        return this.startPoint;
    }
    
    /**
     * The accessor method getEndPoint() returns an instance of Point, the end
     * point of a rectangle to send to the Rubber Band Selection class.
     */
    public Point getEndPoint() {
        return this.endPoint;
    }
    
    /**
     * The accessor method getImage() returns an instance of the PlanarImage,
     * the image loaded.
     */
    public PlanarImage getImage() {
        return image;
    }
    
    /**
     * The accessor method getImageURL() returns an instance of URL, the URL of
     * the image loaded.
     */
    public URL getImageURL() {
        return imageURL;
    }
    
    /**
     * The accessor method getImageHeight()
     */
    public int getImageHeight() {
        return imageHeight;
    }
    
    /**
     * The accessor method getImageWidth()
     */
    public int getImageWidth() {
        return imageWidth;
    }
    
    /**
     * The accessor method getWindowHeight()
     */
    public int getWindowHeight() {
        return windowHeight;
    }
    
    /**
     * The accessor method getWindowWidth()
     */
    public int getWindowWidth() {
        return windowWidth;
    }
    
    // Mutators
    
    /**
     * The mutator method setStartPoint() accepts one input parameter, an instance
     * of Point, the start poing of a rectangle to send to the Rubber Band
     * Selection class.
     */
    public void setStartPoint( Point inputPoint ) {
        this.startPoint = inputPoint;
    }
    
    /**
     * The mutator method setEndPoint() accepts one input parameter, an instance
     * of Point, the end point of a rectangle to send to the Rubber Band Selection
     * class.
     */
    public void setEndPoint( Point inputPoint ) {
        this.endPoint = inputPoint;
    }
    
    /**
     * The mutator method setImage() accepts one input parameter, an instance of
     * PlanarImage, the image to display in the Applet.
     */
    public void setImage( PlanarImage inputImage ) {
        this.image = inputImage;
    }
    
    /**
     * The mutator method setImageHeight()
     */
    public void setImageHeight( int inputHeight ) {
        imageHeight = inputHeight;
    }
    
    /**
     * The mutator method setImageWidth()
     */
    public void setImageWidth( int inputWidth ) {
        imageWidth = inputWidth;
    }
    
    /**
     * The mutator method setWindowHeight()
     */
    public void setWindowHeight( int inputHeight ) {
        windowHeight = inputHeight;
    }
    
    /**
     * The mutator method setWindowWidth()
     */
    public void setWindowWidth( int inputWidth ) {
        windowWidth = inputWidth;
    }
    
    /**
     * The mutator method setImageURL() accepts one input parameter, a String
     * primitive data type, the path of the image to load.
     */
    public void setImageURL( String imagePath ) {
        // Try to load the image using the URL
        try {
            // Image URL
            imageURL = new URL( getDocumentBase(), imagePath );
            showStatus( "Getting image: " + imageURL );
        } catch( MalformedURLException urlException ) {
            if( DEBUG ) {
                System.err.println( "Unable to load image: " + imagePath + " " + urlException );
            }
            // Load the default image
            setImageURL( DEFAULT_FILE );
            // Display exception on Applet status line
            showStatus( "ImageDisplay: Unable to load image: " + imageURL );
        }
    }
    
    /**
     * The mutator method setWindowDimensions() is used to set the dimensions,
     * height and width, of the JApplet window.
     */
    public void setWindowDimensions() {
        setWindowHeight( dimension.height );
        setWindowWidth( dimension.width );
        if( DEBUG ) {
            System.out.println( "Window Height Set: " + getWindowHeight() );
            System.out.println( "Window Width Set: " + getWindowWidth() );
        }
    }
    
    // Helper Methods
    
    /**
     * The helper method imageCenter() is used to display the center of the image
     * in the JApplet window.
     */
    public void imageCenter() {
        windowX = windowWidth/2 - imageWidth/2;
        windowY = windowHeight/2 - imageHeight/2;
        if( DEBUG ) {
            System.out.println( "Image Center Set: " + windowX + " , " + windowY );
        }
        display.set( image, windowX, windowY );
    }
    
    /**
     * The helper method imageDisplay() is used to display the image in the 
     * JApplet window.
     */
    public void imageDisplay() {
        display.set( image );
    }
    
    /**
     * The helper method imageLoad() loads the selected image and displays it in
     * the Applet. The image is loaded using the image's URL and stored in an
     * instance of BufferedImage.
     */
    public void imageLoad() {
        // Display loading status on applet status line.
        showStatus( "Loading image " + imageURL );
        // Load image from URL
        image =  JAI.create("url", imageURL );
        // Get the image height
        setImageHeight( image.getHeight() );
        // Get the image width
        setImageWidth( image.getWidth() );
        // Center the image in the Applet window
        imageCenter();
        // Display the image
        imageDisplay();
    }
    
    /**
     * The helper method imageZoomIn() is used to zoom in to the image when the
     * left mouse button in pressed.
     */
    public void imageZoomIn() {
        // The left mouse button was pressed
        showStatus( "Image Scale: zooming in.");
        float scaleFactor = 2.0F;
        // Adjust scale of image by the scaleFactor
        imageScale( scaleFactor, scaleFactor );
        // Display scale status on the applet status line
        showStatus( "Image Scale: complete.");
    }
    
    /**
     * The helper method imageZoomOut() is used to zoom out of the image when the
     * right mouse button in pressed.
     */
    public void imageZoomOut() {
        // The right mouse button was pressed
        showStatus( "Image Scale: zooming out.");
        float scaleFactor = 0.5F;
        // Adjust scale of image by the scaleFactor
        imageScale( scaleFactor, scaleFactor );
        // Display scale status on the applet status line
        showStatus( "Image Scale: complete.");
    }
    
    /**
     * The helper method imagePanLeft() is used to pan the image to the left
     * when the mouse is in the left quadrant of the image.
     */
    public void imagePanLeft() {
        // The mouse entered the left image border
        showStatus( "Image Translate: panning left.");
        float translateFactorX = 2.0f;
        float translateFactorY = 0.0f;
//        while( this.getMousePosition().getX() <= PAN_BORDER ) {
            // Adjust image left by the translateFactorX
            imageTranslate( translateFactorX, translateFactorY );
            pause();
//        }
    }
    
    /**
     * The helper method imagePanRight() is used to pan the image to the right
     * when the mouse is in the right quadrant of the image.
     */
    public void imagePanRight() {
        // The mouse entered the right image border
        showStatus( "Image Translate: panning right.");
        float translateFactorX = -2.0f;
        float translateFactorY = 0.0f;
//        while( this.getMousePosition().getX() >= dimension.getWidth() - PAN_BORDER ) {
            // Adjust image right by the translateFactorX
            imageTranslate( translateFactorX, translateFactorY );
            pause();
//        }
    }
    
    /**
     * The helper method imagePanUp() is used to pan the image up when the mouse
     * is in the upper quadrant of the image.
     */
    public void imagePanUp() {
        // The mouse entered the upper image border
        showStatus( "Image Translate: panning up.");
        float translateFactorX = 0.0f;
        float translateFactorY = 2.0f;
//        while( this.getMousePosition().getY() <= PAN_BORDER ) {
            // Adjust image up by the translateFactorY
            imageTranslate( translateFactorX, translateFactorY );
            pause();
//        }
    }
    
    /**
     * The helper method imagePanDown() is used to pan the image down when the
     * mouse in the lower quadrant of the image.
     */
    public void imagePanDown() {
        // The mouse entered the lower image border
        showStatus( "Image Translate: panning down.");
        float translateFactorX = 0.0f;
        float translateFactorY = -2.0f;
//        while( this.getMousePosition().getY() >= dimension.getHeight() - PAN_BORDER ) {
            // Adjust image down by the translateFactorY
            imageTranslate( translateFactorX, translateFactorY );
            pause();
//        }
    }
    
    /**
     * The helper method imageScale() accepts four input parameters 
     */
    public void imageScale( float inputScaleFactorX, float inputScaleFactorY ) {
        // The interpolation method for resampling the image
        Interpolation interpolation = Interpolation.getInstance( Interpolation.INTERP_BICUBIC );
        // Create a parameter block to apply to new image creation
        ParameterBlockJAI imageParameters = new ParameterBlockJAI( "scale" );
        imageParameters.addSource( image );
        imageParameters.setParameter( "xscale", inputScaleFactorX );
        imageParameters.setParameter( "yscale", inputScaleFactorY );
        imageParameters.setParameter( "interpolation", interpolation );
        // Create the new image, applying the scale factors
        image = JAI.create( "scale", imageParameters );
        // Display the image
        imageDisplay();
    }
    
    /**
     * The helper method imageTranslate() accepts two parameters 
     */
    public void imageTranslate( float inputTranslateFactorX, float inputTranslateFactorY ) {
        // The interpolation method for resampling the image
        Interpolation interpolation = Interpolation.getInstance( Interpolation.INTERP_NEAREST );
        // Create a parameter block to apply to new image creation
        ParameterBlockJAI imageParameters = new ParameterBlockJAI( "translate" );
        imageParameters.addSource( image );
        imageParameters.setParameter( "xtrans", inputTranslateFactorX );
        imageParameters.setParameter( "ytrans", inputTranslateFactorY );
        imageParameters.setParameter( "interpolation", interpolation );
        // Create the new image, applying the translation factors
        image = JAI.create( "translate", imageParameters );
        // Display the image
        imageDisplay();
    }
    
    /**
     *
     */
    private void pause() {
        try {
            Thread.sleep( 100 );
        }
        catch( InterruptedException exception ) {
            
        }
    }
    
    // Inner Classes
    
    /**
     * The inner class InnerMouseAdapter() implements the MouseListener interface
     * by extending the MouseAdapter event adapter class.
     * This inner class listens for mouse button selections.
     */
    private class InnerMouseAdapter extends MouseAdapter {
        // Mouse entered applet
        public void mouseEntered( MouseEvent eventMouseEntered ) {
            if( DEBUG ) {
                System.out.println( eventMouseEntered.toString() );
            }
        }
        // Mouse button pressed and released
        public void mouseClicked( MouseEvent eventMouseClicked ) {
            // Don't do anything when a button is pressed and released
            if( DEBUG ) {
                System.out.println( eventMouseClicked.toString() );
            }
        }
        // Mouse button pressed
        public void mousePressed( MouseEvent eventMousePressed ) {
            switch( eventMousePressed.getButton() ) {
                case 1:
                    // Left Mouse Button Pressed - Zoom In
                    imageZoomIn();
                    break;
                case 2:
                    // Middle Mouse Button Pressed - Start Rubber Band Selection
                    setStartPoint( eventMousePressed.getPoint() );
                    break;
                case 3:
                    // Right Mouse Button Pressed - Zoom Out
                    imageZoomOut();
                    break;
            }
            if( DEBUG ) {
                System.out.println( eventMousePressed.toString() );
            }
        }
        // Mouse button released
        public void mouseReleased( MouseEvent eventMouseReleased ) {
            Point tempPoint = eventMouseReleased.getPoint();
            if( DEBUG ) {
                System.out.println( eventMouseReleased.toString() );
                
            }
            switch( eventMouseReleased.getButton() ) {
                case 1:
                    // Left Mouse Button Released
                    break;
                case 2:
                    // Middle Mouse Button Released - Stop Rubber Band Selection
                    if( (getStartPoint().getX() < tempPoint.getX()) || (getStartPoint().getY() < tempPoint.getY()) ) {
                        setEndPoint( getStartPoint() );
                        setStartPoint( tempPoint );
                    } else {
                        // Set the end point for the Rubber Band Selection
                        setEndPoint( tempPoint );
                    }
                    break;
                case 3:
                    // Right Mouse Button Released
                    break;
            }
        }
        // Mouse exited applet
        public void mouseExited( MouseEvent eventMouseExited ) {
            if( DEBUG ) {
                System.out.println( eventMouseExited.toString() );
            }
        }
    }
    
    /**
     * The inner class InnerMouseMotionAdapter implements the MouseMotionListener
     * interface by extending the MouseMotionAdapter event adapter class.
     * This class listens for mouse motion events.
     */
    private class InnerMouseMotionAdapter extends MouseMotionAdapter {
        // Mouse moved while mouse button pressed
        public void mouseDragged( MouseEvent eventMouseDragged ) {
            //
        }
        // Mouse moved and no mouse buttons pressed
        public void mouseMoved( MouseEvent eventMouseMoved ) {
            Point tempPoint = eventMouseMoved.getPoint();
            double tempPointX = tempPoint.getX();
            double tempPointY = tempPoint.getY();
            if( tempPointX <= PAN_BORDER ) {
                imagePanLeft();
            }
            else if( tempPointX >= dimension.getWidth() - PAN_BORDER ) {
                imagePanRight();
            }
            //
            if( tempPointY <= PAN_BORDER ) {
                imagePanUp();
            }
            else if( tempPointY >= dimension.getHeight() - PAN_BORDER ) {
                imagePanDown();
            }
        }
    }
}

