/*
 * ImageDisplay.java
 *
 * Created on 6 de Novembro de 2004, 17:43
 */

import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;

/**
 *
 * @author  Bruno
 */
public class ImageDisplay extends javax.swing.JPanel {
    
    /** Creates a new instance of ImageDisplay */
    public ImageDisplay() {
        initComponents();
        dim = getSize();
        zero = new Point();
        at = new AffineTransform();
    }
    
    private void initComponents() {
        setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
        setBackground(new java.awt.Color(255, 255, 255));
        
        addMouseListener(new java.awt.event.MouseAdapter() {
            public void mousePressed(java.awt.event.MouseEvent evt) {
                setZero(evt.getX(), evt.getY());
            }
        });
        addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
            public void mouseDragged(java.awt.event.MouseEvent evt) {
                translate(evt.getX(), evt.getY());
            }
        });
    }
    
    public void paint(Graphics g) {
        Graphics2D g2D = (Graphics2D) g;
    
        // Use antialiasing.
        g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON); 
        
        if((!dim.equals(getSize())) || (offImage == null)) {
            dim = getSize();
            offImage = (BufferedImage) createImage(dim.width, dim.height);
            offGrfx = offImage.createGraphics();
        }

        int flag = checkImage(image, this);
        if((flag & ALLBITS) != 0 ) {
            if(shrink)
                drawShrinkedPicture();
            else if(zoom != 1)
                drawZoomPicture();
            else
                drawPicture();
        }
        else if((flag & ABORT) != 0)
            drawAbort();
        else if((flag & ERROR) != 0)
            drawError();
        else
            drawLoading();

        g2D.drawImage(offImage, 0, 0, this);
    } 
    
    public void update(Graphics g) {
        paint(g);
    }
    
    /** Apanha as varias imagens a serem carregadas e efectua o repaint 
     * quando for a imagem pretendida */
    public boolean imageUpdate( Image image, int flags, int x, int y, 
                                int width, int height) { 
        if ( (flags & HEIGHT) !=0 ) 
            ; 
        else if ( (flags & WIDTH ) !=0 ) 
            ; 
        else if ( (flags & FRAMEBITS) != 0 ) 
            ;         
        else if ( (flags & SOMEBITS) != 0 ) 
            ;
        
        else if ( (flags & ALLBITS) != 0 ) { 
            System.out.println("Image complete " + k); 
            if(image.equals(this.image)) {
                repaint();
            }
            k++;
            return false;  
        } 

        else if ( (flags & ABORT) != 0 )  { 
            System.out.println("Image load aborted..."); 
            repaint();
            return false;  
        } 
        
        else if ( (flags & ERROR) != 0 )  { 
            System.out.println("Image error..."); 
            repaint();
            return false;  
        } 
        
        return true;  
    } 
    
    
    /** Troca de imagem */
    public void setImage(Image img) { 
        image = img;   
        at.setToTranslation(0,0);
        repaint();
    }   
    
    /** */
    private void drawError() {
        String str = "Image error.";
        drawText(str);
    }
    
    /** */
    private void drawAbort() {
        String str = "Image load aborted.";
        drawText(str);
    }
    
    /** */
    private void drawLoading() {
        String str = "Loading...";
        drawText(str);
    }
    
    private void drawText(String str) {
        Font font = new Font("Helvetica", Font.PLAIN, 14);
        FontMetrics fm = offGrfx.getFontMetrics(font); 
        
        int x = (dim.width - fm.stringWidth(str))/2;
        int y = (dim.height - fm.getHeight())/2 + fm.getAscent();
        
        offGrfx.setColor(Color.WHITE);
        offGrfx.fillRect(0, 0, dim.width, dim.height);
        offGrfx.setFont(font); 
        offGrfx.setColor(Color.BLACK);
        offGrfx.drawString(str, x, y);
    }
    
    /** */
    private void drawPicture() {
        double width = image.getWidth(this);
        double height = image.getHeight(this);
        
        double x = at.getTranslateX();
        double y = at.getTranslateY();
        
        if(x > 0)
            x = 0;
        else if(x+width < dim.width)
            x = - (width - dim.width);
     
        if(y > 0)
            y = 0;
        else if(y+height < dim.height)
            y = - (height - dim.height);
        
        // Se a imagem for menor que o display em largura e altura
        // coloco a imagem no centro do display
        if((width < dim.width) && (height < dim.height)) {
            x = (dim.width - width) / 2;
            y = (dim.height - height) / 2;
        }
        // Se a imagem for maior que o display em largura
        else if((width > dim.width) && (height < dim.height)) {
            y = (dim.height - height) / 2;
        }
        // Se a imagem for maior que o display em altura
        else if((width < dim.width) && (height > dim.height)) {
            x = (dim.width - width) / 2;
        }     
        at.setToTranslation(x, y);

        offGrfx.setColor(Color.WHITE);
        offGrfx.fillRect(0, 0, dim.width, dim.height);
        offGrfx.drawImage(image, at, this);   
    }
    
    /** Metodo para reduzir uma imagem ate' caber no display */
    private void drawShrinkedPicture() {
        double width = image.getWidth(this);
        double height = image.getHeight(this);
        
        double scale = 1.0;
        
        // Se a imagem for maior que o display em largura e altura
        // reduzo a largura ate caber no display
        if((width > dim.width) && (height > dim.height)) {
            scale = dim.width / width;
            // Calcula a nova altura da imagem reduzida
            double new_height = height * scale;
            // Se a altura continua a ser maior que o display
            // reduzo outra vez a imagem
            if(new_height > dim.height) 
                scale = scale * (dim.height / new_height);
        }
        // Se a imagem for maior que o display em largura
        else if((width > dim.width) && (height < dim.height)) {
            scale = dim.width / width;
        }
        // Se a imagem for maior que o display em altura
        else if((width < dim.width) && (height > dim.height)) {
            scale = dim.height / height;
        }
        
        // Calculo as novas dimensoes da imagem
        width = width * scale;
        height = height * scale;
       
        // Centro a imagem reduzida no ecran
        double tx = (dim.width - width) / 2;
        double ty = (dim.height - height) / 2;
        at.setToTranslation(tx, ty);
        
        // Reduz a imagem para caber no display
        at.scale(scale, scale);
        
        offGrfx.setColor(Color.WHITE);
        offGrfx.fillRect(0, 0, dim.width, dim.height);
        offGrfx.drawImage(image, at, this);   
    }
    
    /** Desenha a imagem com o zoom */
    private void drawZoomPicture() {
        double width = image.getWidth(this)*zoom;
        double height = image.getHeight(this)*zoom;
        
        double x = at.getTranslateX();
        double y = at.getTranslateY();
        
        if(x > 0)
            x = 0;
        else if(x+width < dim.width)
            x = - (width - dim.width);
     
        if(y > 0)
            y = 0;
        else if(y+height < dim.height)
            y = - (height - dim.height);
        
        // Se a imagem for menor que o display em largura e altura
        // coloco a imagem no centro do display
        if((width < dim.width) && (height < dim.height)) {
            x = (dim.width - width) / 2;
            y = (dim.height - height) / 2;
        }
        // Se a imagem for maior que o display em largura
        else if((width > dim.width) && (height < dim.height)) {
            y = (dim.height - height) / 2;
        }
        // Se a imagem for maior que o display em altura
        else if((width < dim.width) && (height > dim.height)) {
            x = (dim.width - width) / 2;
        }     
        at.setToTranslation(x, y);  
        at.scale(zoom, zoom);
        
        offGrfx.setColor(Color.WHITE);
        offGrfx.fillRect(0, 0, dim.width, dim.height);
        offGrfx.drawImage(image, at, this);   
    }
    
    /** */
    public void setZero(int x, int y) {
        zero.move(x, y);
    }
    
    /** */
    public void setShrink(boolean b) {
        shrink = b;
        repaint();
    }
    
    /** */
    public void translate(int xx, int yy) {
        int dx = xx - zero.x;
        int dy = yy - zero.y;    
        
        int x = (int) at.getTranslateX() + dx;
        int y = (int) at.getTranslateY() + dy;

        at.setToTranslation(x, y);
        zero.move(xx, yy);
        repaint();
    }
    
    /** */
    public void setZoom(double z) {
        zoom = z;
        repaint();
    } 
    
    private Image image;
    private Point zero;
    private Dimension dim;    
    private BufferedImage offImage; 
    private Graphics2D offGrfx;
    private AffineTransform at;
    private boolean shrink;
    private double zoom = 1.0;
    private int k = 0;
}
