package visual.statik.sampled;

import java.awt.color.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.util.*;


/**
 * A class that can be used to construct BufferedImageOp objects that
 * can then be used to operate on static sampled visual content
 *
 * This (partial) implementation is difficult to maintain because it
 * contains a lot of repetitive code
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class RepetitiveBufferedImageOpFactory
{
    private Hashtable<Integer,ConvolveOp>  blurOps, edgeOps;


    private static RepetitiveBufferedImageOpFactory  instance = 
                    new RepetitiveBufferedImageOpFactory();
    

    /**
     * Default Constructor
     */
    private RepetitiveBufferedImageOpFactory()
    {
       blurOps   = new Hashtable<Integer,ConvolveOp>();
       edgeOps   = new Hashtable<Integer,ConvolveOp>();
    }


    /**
     * Create a blur operation
     *
     * @param size   The size of the convolution kernel
     */
    public ConvolveOp createBlurOp(int size)
    {
       ConvolveOp    op;
       float         denom;      
       float[]       kernelValues;
       Integer       key;
       
       key = new Integer(size);
       op  = blurOps.get(key);
       if (op == null)
       {
          kernelValues = getBlurValues(size);

          op = new ConvolveOp(new Kernel(size,size,kernelValues),
                              ConvolveOp.EDGE_NO_OP,
                              null);

          blurOps.put(key, op);          
       }
       
       return op;
    }



    /**
     * Create an edge detection operation
     *
     * @param size   The size of the convolution kernel
     */
    public ConvolveOp createEdgeDetectionOp(int size)
    {
       ConvolveOp    op;
       float         denom;      
       float[]       kernelValues;
       int           center;       
       Integer       key;
       
       key = new Integer(size);
       op  = edgeOps.get(key);
       if (op == null)
       {
          kernelValues = getEdgeValues(size);

          op = new ConvolveOp(new Kernel(size,size,kernelValues),
                              ConvolveOp.EDGE_NO_OP,
                              null);

          edgeOps.put(key, op);          
       }
       
       return op;
    }


    /**
     * Create a RepetitiveBufferedImageOpFactory object
     */
    public static RepetitiveBufferedImageOpFactory createFactory()
    {
       return instance;       
    }


   /**
    * Get the kernel values for a blurring convolution
    *
    * @param size   The size of the kernel
    * @return       The array of kernel values
    */
   private float[] getBlurValues(int size)
   {
      float       denom;      
      float[]     result;
      
      denom  = (float)(size*size);      
      result = new float[size*size];

      for (int row=0; row<size; row++)
         for (int col=0; col<size; col++)
            result[indexFor(row,col,size)] = 1.0f/denom;      

      return result;      
   }


   /**
    * Get the kernel values for an edge detecting convolution
    *
    * @param size   The size of the kernel
    * @return       The array of kernel values
    */
   private float[] getEdgeValues(int size)
   {
      float[]     result;
      int         center;
      
      
      center = size/2;
      result = new float[size*size];

      result[indexFor(center-1, center  , size)] = -1.0f;      
      result[indexFor(center  , center-1, size)] = -1.0f;      
      result[indexFor(center  , center  , size)] =  4.0f;      
      result[indexFor(center  , center+1, size)] = -1.0f;      
      result[indexFor(center+1, center  , size)] = -1.0f;      

      return result;      
   }
   

   /**
    * Convert row and column indexes (i.e., matrix indices)
    * into a linear index (i.e., vector index)
    *
    * @param row   The row index
    * @param col   The column index
    * @param size  The size of the square matrix
    */
   private int indexFor(int row, int col, int size)
   {
      return row*size + col;      
   }

}
