package visual.statik.sampled;


//[skeleton1.
import java.awt.*;
import java.awt.geom.*;
import java.awt.image.*;

import math.*;


/**
 * A BufferedImageOp that returns a gray-scale version
 * with one color (in a particular area) left unchanged
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class GrayExceptOp extends IdentityOp
{
    private int[]             highlightColor;
    
//]skeleton1.

    private double[] x      = new double[3];
    private double[] y      = new double[3];
    private Metric   metric;

    private static final double  TOLERANCE = 80.0;


//[constructors.
    
    /**
     * Explicit Value Constructor
     *
     * @param r      The red component of the color to leave
     * @param g      The green component of the color to leave
     * @param b      The blue component of the color to leave
     */
    public GrayExceptOp(int r, int g, int b)
    {
       this(r, g, b, new RectilinearMetric());
    }

    
    /**
     * Explicit Value Constructor
     *
     * @param r      The red component of the color to leave
     * @param g      The green component of the color to leave
     * @param b      The blue component of the color to leave
     * @param metric The Metric to use to determine if two colors are similar
     */
    public GrayExceptOp(int r, int g, int b, Metric metric)
    {
       highlightColor    = new int[3];
       highlightColor[0] = r;
       highlightColor[1] = g;
       highlightColor[2] = b;

       this.metric = metric;       
    }
//]constructors.
    


//[areSimilar.
    /**
     * Determines if two colors are similar
     * 
     * Note: This method only uses the red, green, and 
     * blue components.  It does not use the alpha component.
     *
     * @param a   The components of one color
     * @param b   The components of the other color
     */
    private boolean areSimilar(int[] a, int[] b)
    {
       boolean        result;
       double         distance;
       
       for (int i=0; i<3; i++)
       {
          x[i] = a[i];
          y[i] = b[i];          
       }
       
       result   = false;
       distance = metric.distance(x, y);

       if (distance <= TOLERANCE) result = true;          

       return result;
    }
//]areSimilar.


//[filter.
    /**
     * Perform the filtering operation
     *
     * @param source      The source image
     * @param destination An empty image in which to srote the result (or null)
     */
    public BufferedImage filter(BufferedImage src, 
                                BufferedImage dest)
    {
       ColorModel       destColorModel, srcColorModel;
       int              grayRGB, highlightRGB;
       int              srcRGB, srcHeight, srcWidth;
       int[]            gray, srcColor;       
       Raster           srcRaster;
       


       srcWidth      = src.getWidth();
       srcHeight     = src.getHeight();

       srcColorModel = src.getColorModel();
       srcRaster     = src.getRaster();
       srcColor      = new int[4];
       
       gray          = new int[4];
       
       
       if (dest == null) 
          dest = createCompatibleDestImage(src, 
                                           srcColorModel);

       destColorModel = dest.getColorModel();
       highlightRGB   = destColorModel.getDataElement(
                                         highlightColor, 
                                         0);       
       
       
       for (int x=0; x<srcWidth; x++)
       {
          for (int y=0; y<srcHeight; y++)
          {
             srcRGB = src.getRGB(x, y);
             srcColorModel.getComponents(srcRGB, srcColor, 0);


             if (areSimilar(srcColor, highlightColor))
             {
                dest.setRGB(x, y, highlightRGB);                
             }
             else
             {
                gray[0]=(srcColor[0]+srcColor[1]+srcColor[2])/3;
                gray[1]=gray[0];             
                gray[2]=gray[0];
                grayRGB=destColorModel.getDataElement(gray,0);
                dest.setRGB(x, y, grayRGB);                
             }
          }
       }
       
       return dest;
    }
//]filter.

//[setMetric
    /**
     * Set the Metric to use to calculate the distance
     * between two colors
     *
     * @param metric  The Metric to use
     */
    public void setMetric(Metric metric)
    {
       this.metric = metric;       
    }
//]setMetric
//[skeleton2.
    
}
//]skeleton2.
