/**
 * Box Filtering Java Applet Interactive Demo
 * 
 * required files:
 * - BoxFilter.java
 *  
 * www.tech-algorithm.com
 * 
 */
import java.applet.Applet;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.MemoryImageSource;
import java.awt.image.PixelGrabber;

public class BoxFilterDemo extends Applet implements MouseListener, KeyListener {
    
    Image buffer ;
    Graphics graphics ;
    Dimension dim ;
    int[] pixels ;       
    
    final String targetfile = "la2-nofilter.png" ;
           
    FontMetrics fm ;
    
    // table related
    int select = -1 ; // none selected
    final int side = 30, // table width/height
            // location of table
            tablex = 0,
            tabley = 0 ;
    // table's initial entries
    String[] kernel = {
        "0", "0", "0",
        "0", "1", "0",
        "0", "0", "0"
    } ;
    
    public void init() {                                
        setBackground(Color.WHITE) ;        
        MediaTracker mt = new MediaTracker(this) ;                                
        Image img = getImage(getCodeBase(), targetfile) ;            
        mt.addImage(img, 0) ;
        try {
            mt.waitForAll() ;
        } catch (InterruptedException e) {}                
        dim = new Dimension(img.getWidth(null),img.getHeight(null)) ;                                             
        pixels = new int[dim.width*dim.height] ;
        PixelGrabber pg = new PixelGrabber(
                img,0,0,dim.width,dim.height,pixels,0,dim.width) ;
        try {
            if (!pg.grabPixels()) {                
                // failed
            }
        } catch (Exception e) {}
        buffer = createImage(dim.width, dim.height + (side*3) + 1) ;
        graphics = buffer.getGraphics() ;
        fm = graphics.getFontMetrics() ;

        addMouseListener(this) ;
        addKeyListener(this) ;
        
        repaint() ;
    }
        
    public boolean isInside(int x,int y,int w,int h, Point p) {        
        if ((p.x>=x)&&(p.y>=y)&&(p.x<=x+w)&&(p.y<=y+h))
            return true ;        
        return false ;
    }
    
    public void checkTable(int x,int y,int w,int h,int index, MouseEvent e) {        
        x += tablex ;
        y += tabley ;
        int cselect = -1 ;        
        if (isInside(x,y,w,h,e.getPoint()))
            cselect = index ;                    
        if (cselect>=0) {                        
            if (select>=0) validateTableEntry(select) ;
            select = cselect ;            
        }        
    }

    public void mouseClicked(MouseEvent e) {        
        // check if our table is clicked
        int index = 0 ;
        for (int i=0;i<3;i++)
            for (int j=0;j<3;j++)
                checkTable(j*side,i*side,side,side,index++,e) ;
        repaint() ;
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mousePressed(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
    }

    /** manage kernel-table keyboard input */
    public void keyPressed(KeyEvent e) {        
        if (select>=0) {                                    
            switch (e.getKeyCode()) {
                case KeyEvent.VK_BACK_SPACE:
                case KeyEvent.VK_DELETE:
                    kernel[select] = "0" ; break ;
                case KeyEvent.VK_PERIOD:
                    kernel[select] += "."; break ;
                case KeyEvent.VK_ENTER:
                    validateTableEntry(select) ;
                    break ;
                default:
                    char c = e.getKeyChar() ;
                    if (kernel[select].length()>3) return ;                    
                    if (((c>='0') && (c<='9'))) {
                        if (kernel[select].equals("0")) 
                            kernel[select] = "" ;
                        kernel[select] += c ;              
                    } else if (c=='-') {
                        kernel[select] = "-" ;                        
                    } else if (c=='.') {
                        kernel[select] += c ;
                    }
            }            
        }        
        repaint() ;
    }

    public void keyReleased(KeyEvent e) {
    }

    public void keyTyped(KeyEvent e) {                        
    }
    
    /** check kernel[index] validity */
    public void validateTableEntry(int index) {
        try {
            float f = Float.valueOf(kernel[index]) ;
            if ((int)f!=f) {
                kernel[index] = String.valueOf(f) ;
            } else {
                kernel[index] = String.valueOf((int)f) ;
            }
        } catch (NumberFormatException e) {
            kernel[index] = "0" ;
        }
    }
    
    /** draw a table 'field' */
    public void drawField(int x,int y,int w,int h,int index) {
        
        x+=tablex ;
        y+=tabley ;
        
        graphics.setColor(Color.BLACK) ;
        graphics.drawRect(x,y,w,h) ;
            
        String label = kernel[index] ;             
        int strw = fm.stringWidth(label) ;
        int strh = fm.getAscent() ;
        
        x += (w-strw)>>1 ;
        y += (h+strh)>>1 ;
                
        if (index==select)
            graphics.setColor(Color.RED) ;        
        
        graphics.drawString(label,x,y) ;
        
    }
    
    /** draw the matrix-table */
    public void drawTable() {        
        int index = 0 ;
        for (int i=0;i<3;i++)
            for (int j=0;j<3;j++) 
                drawField(j*side,i*side,side,side,index++) ;
    }
    
    public void paint(Graphics g) {                                
        
        if (fm == null) return ;        
        graphics.setColor(Color.WHITE) ;
        graphics.fillRect(0,0,dim.width,dim.height) ;
        
        // parse kernel from table
        float[] kernels = new float[kernel.length] ;
        for (int i=0;i<kernels.length;i++) {
            try {
                kernels[i] = Float.valueOf(kernel[i]) ;
            } catch (NumberFormatException e) {
                kernels[i] = 0.0f ;
            }
        }
        // apply filter and draw filtered image in buffer
        int[] temp = BoxFilter.BoxFiltering(pixels,dim.width,dim.height,kernels) ;        
        graphics.drawImage(
            Toolkit.getDefaultToolkit().createImage(
                new MemoryImageSource(dim.width, dim.height, temp, 0, dim.width)),0,(side*3)+1,null) ;
        
        drawTable() ;
        graphics.setColor(Color.BLACK) ;
        graphics.drawString("Interactive box filtering demo applet",(side*3)+20,20) ;
        graphics.drawString("Click the matrix and key in new values",(side*3)+20,40) ;        
        g.drawImage(buffer,0,0,null) ;
    }
    
    /** avoid flicker */
    public void update(Graphics g) {        
        paint(g) ;
    }
    
}