import java.awt.*;
import java.awt.color.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import java.awt.image.*;
import javax.swing.*;
import java.util.*;

public class Traiteur
    extends Frame {
  private Window imageWindow;
  private SplitImagePanel splitImagePanel;
  private Hashtable ops;
  
  public static void main(String[] args) {
    String imageFile = "Ethol with Roses.small.jpg";
    if (args.length > 0) imageFile = args[0];
    new Traiteur(imageFile);
  }
  
  public Traiteur(String imageFile) {
    super("Traiteur");
    createOps();
    createImageWindow(imageFile);
    createUI();
    setVisible(true);
  }
  
  private void createOps() {
    ops = new Hashtable();
    createConvolutions();
    createTransformations();
    createLookups();
    createRescales();
    createColorOps();
  }
    
  private void createConvolutions() {
    ops.put("lisser", ConvolutionFabrique.createLisseur());
    ops.put("contour", ConvolutionFabrique.createDetecteur());    
    ops.put("renforçer", ConvolutionFabrique.createAiguiseur());
  }

  private void createTransformations() {
    AffineTransform at;
    RenderingHints rh = new RenderingHints(
        RenderingHints.KEY_INTERPOLATION,
        RenderingHints.VALUE_INTERPOLATION_BILINEAR);

    at = AffineTransform.getRotateInstance(Math.PI / 6, 0, 285);
    ops.put("rotation (plus proche voisin)", new AffineTransformOp(at, null));
    ops.put("rotation (interp. bilineaire)", new AffineTransformOp(at, rh));
    
    at = AffineTransform.getRotateInstance(Math.PI / 6);
    ops.put("rotation (origine, bilineaire)", new AffineTransformOp(at, rh));

    at = AffineTransform.getScaleInstance(.5, .5);
    ops.put("réduire .5, .5", new AffineTransformOp(at, null));
  }

  private void createLookups() {
    ops.put("plus clair", LookupFabrique.createClair());
    ops.put("meilleur clair", LookupFabrique.createMeilleurClair());
    ops.put("simplfie", LookupFabrique.createSimplifie());
    ops.put("inverser", LookupFabrique.createInverser());
    ops.put("inverser rouge", LookupFabrique.createInverserRouge());
    ops.put("inverser vert", LookupFabrique.createInverserVert());
    ops.put("inverser bleu", LookupFabrique.createInverserBleu());
    ops.put("sans rouge", LookupFabrique.createSansRouge());
    ops.put("sans vert", LookupFabrique.createSansVert());
    ops.put("sans bleu", LookupFabrique.createSansBleu());
  }
  
  private void createRescales() {
    ops.put("plus sombre (.5, 0)", new RescaleOp(.5f, 0, null));
    ops.put("plus sombre (.5, 64)", new RescaleOp(.5f, 64, null));
    ops.put("plus brillant (1.2, 0)", new RescaleOp(1.2f, 0, null));
    ops.put("plus brillant (1.5, 0)", new RescaleOp(1.5f, 0, null));
  }
  
  private void createColorOps() {
    ops.put("en gris", new ColorConvertOp(
        ColorSpace.getInstance(ColorSpace.CS_GRAY), null));
  }
  
  private void createImageWindow(String imageFile) {
    // Create the image window.
    splitImagePanel = new SplitImagePanel(imageFile);
    imageWindow = new Window(this); 
    imageWindow.setLayout(new BorderLayout());
    imageWindow.add(splitImagePanel, BorderLayout.CENTER);
		imageWindow.setSize(splitImagePanel.getPreferredSize());
		Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
    Dimension d = imageWindow.getSize();
    int x = (screen.width - d.width) / 2;
    int y = (screen.height - d.height) / 2;
    imageWindow.setLocation(x, y);
    imageWindow.setVisible(true);
  }
    
  private void createUI() {
			//setFont(new Font("Serif", Font.BOLD, 12));
    setLayout(new BorderLayout());
    // Set our location to the left of the image frame.
    setSize(200, 350);
    Point pt = imageWindow.getLocation();
    setLocation(pt.x - getSize().width - 20, pt.y);

    final Checkbox accumulateCheckbox = new Checkbox("accumuler", false);
    final Label statusLabel = new Label("");

    // trier les  operations.
    Enumeration e = ops.keys();
    Vector names = new Vector();
    while (e.hasMoreElements())
      names.addElement(e.nextElement());
    Collections.sort(names);
    final java.awt.List list = new java.awt.List();
    for (int i = 0; i < names.size(); i++)
      list.add((String)names.elementAt(i));
    add(list, BorderLayout.CENTER);
    
    // effectuer l'action selectionnee
    list.addItemListener(new ItemListener() {
      public void itemStateChanged(ItemEvent ie) {
        if (ie.getStateChange() != ItemEvent.SELECTED) return;
        String key = list.getSelectedItem();
        BufferedImageOp op = (BufferedImageOp)ops.get(key);
        BufferedImage source = splitImagePanel.getTrImage();
        boolean accumulate = accumulateCheckbox.getState();
        if (source == null || !accumulate)
          source = splitImagePanel.getImage();
				statusLabel.setText("" + key + " en cours ...");
        list.setEnabled(false);
        accumulateCheckbox.setEnabled(false);
        BufferedImage destination = op.filter(source, null);
        splitImagePanel.setSecondImage(destination);
        splitImagePanel.setSize(splitImagePanel.getPreferredSize());
        imageWindow.setSize(imageWindow.getPreferredSize());
        list.setEnabled(true);
        accumulateCheckbox.setEnabled(true);
        statusLabel.setText("" + key + " terminé.");
      }
    });

    Button loadButton = new Button("charger...");
    loadButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent ae) {
        FileDialog fd = new FileDialog(Traiteur.this);
        fd.show();
        if (fd.getFile() == null) return;
        String path = fd.getDirectory() + fd.getFile();
        splitImagePanel.setImage(path);
        splitImagePanel.setSecondImage(null);
				imageWindow.setSize(splitImagePanel.getPreferredSize());
        imageWindow.validate();
        imageWindow.repaint();
      }
    });

    Panel bottom = new Panel(new GridLayout(2, 1));
    Panel topBottom = new Panel();
    topBottom.add(accumulateCheckbox);
    topBottom.add(loadButton);
    bottom.add(topBottom);
    bottom.add(statusLabel);
    add(bottom, BorderLayout.SOUTH);
    
    addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
  }

}
class ConvolutionFabrique {
		public static ConvolveOp createLisseur() {
				float[] blur = {
						1f/9f, 1f/9f, 1f/9f,
						1f/9f, 1f/9f, 1f/9f,  
						1f/9f, 1f/9f, 1f/9f 
				};
				return new ConvolveOp(new Kernel(3, 3, blur),
															ConvolveOp.EDGE_NO_OP, null);
		}
		public static ConvolveOp createDetecteur() {
				float[] edge = {
						0f, -1f, 0f,
						-1f, 4f, -1f,
						0f, -1f, 0f
				};
				return new ConvolveOp(new Kernel(3, 3, edge),
															ConvolveOp.EDGE_NO_OP, null);
		}
		public static ConvolveOp createAiguiseur() {
				float[] sharp = {
						0f, -1f, 0f,
						-1f, 5f, -1f,
						0f, -1f, 0f
				};
				return new ConvolveOp(new Kernel(3, 3, sharp));
		}
}


class LookupFabrique {
    static short[] clair = new short[256];
    static short[] meilleurClair = new short[256];
    static short[] simplifie = new short[256];
    static short[] inverser = new short[256];
    static short[] ident = new short[256];
    static short[] zero = new short[256];
		static {
    for (int i = 0; i < 256; i++) {
      clair[i] = (short)(128 + i / 2);
      meilleurClair[i] = (short)(Math.sqrt((double)i / 255.0) * 255.0);
      simplifie[i] = (short)(i - (i % 32));
      inverser[i] = (short)(255 - i);
      ident[i] = (short)i;
      zero[i] = (short)0;
    }
		}
		static LookupOp createClair() {
				return new LookupOp(new ShortLookupTable(0, clair), null);
		}
    static LookupOp createMeilleurClair() {
				return new LookupOp(
						new ShortLookupTable(0, meilleurClair), null);
		}
		static LookupOp createSimplifie() {
				return new LookupOp(
					  new ShortLookupTable(0, simplifie), null);
		}
		static LookupOp createInverser() {
				return new LookupOp(new ShortLookupTable(0, inverser), null);}
    
    static short[][] rougeSeul = { inverser, ident, ident };
    static short[][] vertSeul = { ident, inverser, ident };
    static short[][] bleuSeul = { ident, ident, inverser };

		static LookupOp createInverserRouge() {
				return new LookupOp(
										new ShortLookupTable(0, rougeSeul), null);}
		static LookupOp createInverserVert() {
				return new LookupOp(
										new ShortLookupTable(0, vertSeul), null);}
		static LookupOp createInverserBleu() {
				return new LookupOp(
										new ShortLookupTable(0, bleuSeul), null);}
    
    static short[][] sansRouge= { zero, ident, ident };
    static short[][] sansVert = { ident, zero, ident };
    static short[][] sansBleu = { ident, ident, zero };
		static LookupOp createSansRouge() {
    return new LookupOp(
												new ShortLookupTable(0, sansRouge), null);}
		static LookupOp createSansVert() {
    return new LookupOp(
												new ShortLookupTable(0, sansVert), null);}
		static LookupOp createSansBleu() {
    return new LookupOp(
												new ShortLookupTable(0, sansBleu), null);}
}
