/*
 * @(#)DitherTest.java	1.5 98/03/23
 *
 * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */

import java.applet.Applet;
import java.awt.event.*;
import java.awt.*;
import java.awt.image.ColorModel;
import java.awt.image.MemoryImageSource;
import java.lang.InterruptedException;

public class DitherTest extends Applet implements Runnable {
    final static int NOOP = 0;
    final static int RED = 1;
    final static int GREEN = 2;
    final static int BLUE = 3;
    final static int ALPHA = 4;
    final static int SATURATION = 5;

    ThreadGroup appletThreadGroup;
    Thread runner;

    DitherControls XControls;
    DitherControls YControls;
    DitherCanvas canvas;

    public void init() {
	String xspec, yspec;
	int xvals[] = new int[2];
	int yvals[] = new int[2];

	try {
	    xspec = getParameter("xaxis");
	} catch (Exception e) {
	    xspec = null;
	}
	try {
	    yspec = getParameter("yaxis");
	} catch (Exception e) {
	    yspec = null;
	}
	if (xspec == null) xspec = "red";
	if (yspec == null) yspec = "blue";
	int xmethod = colormethod(xspec, xvals);
	int ymethod = colormethod(yspec, yvals);

	setLayout(new BorderLayout());
	XControls = new DitherControls(this, xvals[0], xvals[1],
				       xmethod, false);
	YControls = new DitherControls(this, yvals[0], yvals[1],
				       ymethod, true);
	YControls.addRenderButton();
	add("North", XControls);
	add("South", YControls);
	add("Center", canvas = new DitherCanvas());

	appletThreadGroup = Thread.currentThread().getThreadGroup();
    }

    public void destroy() {
        remove(XControls);
        remove(YControls);
        remove(canvas);
    }

    public void start() {
        runner = new Thread(this);
	runner.start();
    }

    public void stop() {
	runner = null;
    }

    public static void main(String args[]) {
	Frame f = new Frame("DitherTest");
	DitherTest	ditherTest = new DitherTest();

	ditherTest.init();

	f.add("Center", ditherTest);
	f.pack();
	f.show();

	ditherTest.start();
    }

    int colormethod(String s, int vals[]) {
	int method = NOOP;

	if (s == null)
	    s = "";

	String lower = s.toLowerCase();
	int len = 0;
	if (lower.startsWith("red")) {
	    method = RED;
	    lower = lower.substring(3);
	} else if (lower.startsWith("green")) {
	    method = GREEN;
	    lower = lower.substring(5);
	} else if (lower.startsWith("blue")) {
	    method = BLUE;
	    lower = lower.substring(4);
	} else if (lower.startsWith("alpha")) {
	    method = ALPHA;
	    lower = lower.substring(4);
	} else if (lower.startsWith("saturation")) {
	    method = SATURATION;
	    lower = lower.substring(10);
	}

	if (method == NOOP) {
	    vals[0] = 0;
	    vals[1] = 0;
	    return method;
	}

	int begval = 0;
	int endval = 255;

	try {
	    int dash = lower.indexOf('-');
	    if (dash < 0) {
		begval = endval = Integer.parseInt(lower);
	    } else {
		begval = Integer.parseInt(lower.substring(0, dash));
		endval = Integer.parseInt(lower.substring(dash+1));
	    }
	} catch (Exception e) {
	}

	if (begval < 0) begval = 0;
	if (endval < 0) endval = 0;
	if (begval > 255) begval = 255;
	if (endval > 255) endval = 255;

	vals[0] = begval;
	vals[1] = endval;

	return method;
    }

    void applymethod(int c[], int method, int step, int total, int vals[]) {
	if (method == NOOP)
	    return;
	int val = ((total < 2)
		   ? vals[0]
		   : vals[0] + ((vals[1] - vals[0]) * step / (total - 1)));
	switch (method) {
	case RED:
	    c[0] = val;
	    break;
	case GREEN:
	    c[1] = val;
	    break;
	case BLUE:
	    c[2] = val;
	    break;
	case ALPHA:
	    c[3] = val;
	    break;
	case SATURATION:
	    int max = Math.max(Math.max(c[0], c[1]), c[2]);
	    int min = max * (255 - val) / 255;
	    if (c[0] == 0) c[0] = min;
	    if (c[1] == 0) c[1] = min;
	    if (c[2] == 0) c[2] = min;
	    break;
	}
    }

    public void run() {
        canvas.setImage(null);	// Wipe previous image
        Image img = calculateImage();
        synchronized(this) {
            if (img != null && runner == Thread.currentThread())
                canvas.setImage(img);
        }
    }
  
    /**
     * Calculates and returns the image.  Halts the calculation and returns
     * null if the Applet is stopped during the calculation.
     */
    Image calculateImage() {
        Thread me = Thread.currentThread();

        int width = canvas.getSize().width;
	int height = canvas.getSize().height;
	int xvals[] = new int[2];
	int yvals[] = new int[2];
	int xmethod = XControls.getParams(xvals);
	int ymethod = YControls.getParams(yvals);
	int pixels[] = new int[width * height];
	int c[] = new int[4];
	int index = 0;
	for (int j = 0; j < height; j++) {
	    for (int i = 0; i < width; i++) {
		c[0] = c[1] = c[2] = 0;
		c[3] = 255;
		if (xmethod < ymethod) {
		    applymethod(c, xmethod, i, width, xvals);
		    applymethod(c, ymethod, j, height, yvals);
		} else {
		    applymethod(c, ymethod, j, height, yvals);
		    applymethod(c, xmethod, i, width, xvals);
		}
		pixels[index++] = ((c[3] << 24) |
				   (c[0] << 16) |
				   (c[1] << 8) |
				   (c[2] << 0));
	    }

            // Poll once per row to see if we've been told to stop.
            if (runner != me)
                return null;
	}

        return createImage(new MemoryImageSource(width, height,
		      ColorModel.getRGBdefault(), pixels, 0, width));
    }

    public String getAppletInfo() {
        return "An interactive demonstration of dithering.";
    }
  
    public String[][] getParameterInfo() {
        String[][] info = {
            {"xaxis", "{RED, GREEN, BLUE, PINK, ORANGE, MAGENTA, CYAN, WHITE, YELLOW, GRAY, DARKGRAY}", "The color of the Y axis.  Default is RED."}, 
            {"yaxis", "{RED, GREEN, BLUE, PINK, ORANGE, MAGENTA, CYAN, WHITE, YELLOW, GRAY, DARKGRAY}", "The color of the X axis.  Default is BLUE."}
        };
        return info;
    }
}

class DitherCanvas extends Canvas {
    Image img;
    static String calcString = "Calculating...";

    public void paint(Graphics g) {
	int w = getSize().width;
	int h = getSize().height;
	if (img == null) {
	    super.paint(g);
	    g.setColor(Color.black);
	    FontMetrics fm = g.getFontMetrics();
	    int x = (w - fm.stringWidth(calcString))/2;
	    int y = h/2;
	    g.drawString(calcString, x, y);
	} else {
            g.drawImage(img, 0, 0, w, h, this);
        }
    }

    public void update(Graphics g) {
	paint(g);
    }

    public Dimension getMinimumSize() {
	return new Dimension(20, 20);
    }

    public Dimension getPreferredSize() {
	return new Dimension(200, 200);
    }

    public Image getImage() {
	return img;
    }

    public void setImage(Image img) {
	this.img = img;
        paint(getGraphics());
    }
}

class DitherControls extends Panel implements ActionListener {
    TextField start;
    TextField end;
    Button button;
    Choice choice;
    DitherTest applet;

    static LayoutManager dcLayout = new FlowLayout(FlowLayout.CENTER, 10, 5);

    public DitherControls(DitherTest app, int s, int e, int type,
			  boolean vertical) {
	applet = app;
	setLayout(dcLayout);
	add(new Label(vertical ? "Vertical" : "Horizontal"));
	add(choice = new Choice());
	choice.addItem("Noop");
	choice.addItem("Red");
	choice.addItem("Green");
	choice.addItem("Blue");
	choice.addItem("Alpha");
	choice.addItem("Saturation");
	choice.select(type);
	add(start = new TextField(Integer.toString(s), 4));
	add(end = new TextField(Integer.toString(e), 4));
    }

    public void addRenderButton() {
	add(button = new Button("New Image"));
	button.addActionListener(this);
    }

    public int getParams(int vals[]) {
	vals[0] = Integer.parseInt(start.getText());
	vals[1] = Integer.parseInt(end.getText());
	return choice.getSelectedIndex();
    }

    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == button) {
            applet.start();
        }
    }
}
