/*
 * @(#) ROI2D.java 1.0 2000/10/3
 * Copyright (c) 2000 Larry Rodrigues
 */
package com.vistech.roi;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import java.awt.color.*;

/** Represents an ROI. Properties in this class represent attributes
   * of the ROI. The properties include shape of the ROI, mask value
   * for thresholding, and statistics of the ROI. Whenever a new shape is set,
   * this class computes the threshold values and statistics.
   *
   * Statistics computed in this class are: minimum, maximumn mean, standard deviation, and area.
   * If your application needs to compute other types of statistics, extend this class.
   * and override relevant methods.  
   * @version 1.0  3 Oct 2000
   * @author Lawrence Rodrigues
   */
public class ROI2D implements java.io.Serializable{
   protected Shape currentShape = null;
   protected transient ROIContext roiContext = null;
   protected String[] roiColNames = new String[] {"Num","Sample", "Min", "Max", "Mean","Std Dev", "Area"};
   protected int stats[][] = new int[roiColNames.length][];
   protected String[][] roiData = null;
   protected int roiNumber =0;
   protected ROIStatistics roiStats[];
   protected int threshold;
   protected int maskValue = 255;
   protected transient BufferedImage origImage, displayImage;
   protected Point[] curPoints;

   protected Color drawingColor= Color.white, fillColor=Color.white;

   public ROI2D (){ }

   /** Sets the roi context property.
     * @param roiContext the ROI context.
     */
   public void setROIContext(ROIContext roiContext){
      this.roiContext = roiContext;
      origImage = roiContext.getOriginalImage();
      displayImage = roiContext.getDisplayImage();
   }

   /** Gets the roi context property.
     * @return the ROI context.
     */
   public ROIContext getROIContext(){
      return roiContext;
   }

   /** Sets the shape property. Invocation of this method results in computation
     * of ROI statistics.
     * @param sh the ROI shape.
     */
   public void setShape(Shape sh){
      currentShape = sh;
      computeStats();
      if(roiStats == null) return;
      double mean = roiStats[0].getMean();
      setThreshold((int)mean);
   }

   /** Draws the ROI shape over the iamge.
     * @param sh the ROI shape.
     * @param color the color of the ROI outline.
     */
   public void drawShape(Shape shape, Color color){
      displayImage = roiContext.getDisplayImage();
      if(displayImage == null) return;
      Graphics2D g2d = displayImage.createGraphics();
      g2d.setColor(color);
      g2d.draw(shape);
   }

   /** Reurns the shape of the ROI.
     * @return the ROI shape.
   */
   public Shape getShape(){return currentShape;}

   /** Sets the identification number for this ROI.
     * This property is useful for distinguishing ROIs
     * when multiple ROIs  deposited on an image.
     * It is tge responsibity of the calling object to ensure that
     * no duplicate numbers exist.
     * @param number the identification number of the ROI.
     */
   public void setID(int number){roiNumber = number;}

   /** Reurns the identification number of this ROI.
     * @return the identification number.
     */
   public int getID(){ return roiNumber; }

   /** Sets the mask value.
     * @param number the value of the mask.
     */
   public void setMaskValue(int number){maskValue= number;}

   /** Reurns the mask value.
     * @return the mask value.
     */
   public int getMaskValue(){ return maskValue; }

   /** Sets the color of the thresholded pixels.
     * @param the color of the thresholded pixels.
     */
   public void setFillColor(Color color){fillColor = color;}

   /** Reurns the color of the thresholded pixels.
     * @return the color of the thresholded pixels.
     */
   public Color getFillColor(){ return fillColor; }

   /** Sets the color of the ROI outline.
     * @param the color of ROI outlne.
     */
   public void setDrawingColor(Color color){drawingColor = color;}

   /** Reurns the color of the ROI outline.
     * @return the color of the ROI outline.
     */
   public Color getDrawingColor(){ return drawingColor; }

   /** This is a convenient method that returns the names of the
     * statistics that is computed in this class.
     * @return an array with each element representing a
     * type of statistics (e.g., min, max, mean).
     */
   public String[] getColumnNames() {return roiColNames;}

   /** This is a convenient method that returns the statistics values as
     * text so that they can be immediately displayed on a table.
     * @return a two dimensional array with first subscript representing
     * the pixel sample, and the second the correpsonding statistics.
     */
    public String[][] getStatsAsString() {
      computeStats();
      return roiData;
   }

   /** Returns the statistics values of each sample in an ROIStatistics object.
     * @return an array of ROIStatiscs objects. Each element of this array is populated
     * with statistics of a sample (i.e., pixel component).
     */
   public ROIStatistics[] getStats() {
      computeStats();
      return roiStats;
   }

   /** Returns the threshold value.
     * @return the threshold value.
     */
   public int getThreshold(){ return threshold; }

   /** Sets the threshold value. This value is the same for all the bands.
     * Calls setMask(points, maskValue) to highlight the thresholded pixels.
     * @param thresh the threshold value.
     * @see com.vistech.roi.ROIUtil.
     */
    public void setThreshold(int thresh){
      threshold = thresh;
      Point[] thPoints = ROIUtil.getThresholdPoints(origImage, currentShape, thresh);
      if(thPoints == null) return;
      setMask(thPoints, maskValue);
   }

   /** Sets the mask value of the thresholded pixels and highlights them.
     * @param points an arry of points within the ROI.
     * @param maskValue the mask value.
   */

   public void setMask(Point[] points, int maskValue){
      if(points == null) return;
      clearMask();
      displayImage = roiContext.getDisplayImage();
      WritableRaster ra = displayImage.getRaster();
      int num =  ra.getNumBands();
      int mask[] = new int[num];
      for(int i=0;i<num;i++) mask[i] = maskValue;
      for(int i=0; i<points.length;i++){
          ra.setPixel(points[i].x, points[i].y,mask);
      }
      curPoints = points;
      drawShape(currentShape,drawingColor);
   }

   /** Clear the ROI. This is done evertime a new threshold is set.*/
   public void clearMask(){
      WritableRaster origRaster = origImage.getRaster();
      WritableRaster displayRaster = displayImage.getRaster();
      int num =  displayRaster.getNumBands();
      if(curPoints == null) return;
      for(int i=0; i<curPoints.length;i++) {
          int pix1[] = new int[num];
          int[] pix = origRaster.getPixel(curPoints[i].x, curPoints[i].y, pix1);
          displayRaster.setPixel(curPoints[i].x, curPoints[i].y,pix);
      }
   }

   /** Computes default statistics */

   private void computeStats(){
      SampleModel sampleModel = origImage.getSampleModel();
      DataBuffer dataBuffer = origImage.getRaster().getDataBuffer();
      if((sampleModel == null)|| (dataBuffer == null) || (currentShape == null)) return;
      double[][] roist = ROIUtil.getROIStats(sampleModel, dataBuffer,currentShape);
      //2.0 changes
      if( roist == null) return;
      int numSamples = roist.length;
      roiStats = new ROIStatistics[numSamples];
      for(int i=0;i<numSamples;i++) {
          roiStats[i] = new ROIStatistics(roist[i][0],
                                          roist[i][1],
                                          roist[i][2],
                                          roist[i][3],
                                          roist[i][4]);
      }
      statsToString(roist);
   }

   /** Converts the statistics values to their string representation */
   private void statsToString( double roistats[][]){
      roiData = new String[roistats.length][roiColNames.length];
      String roinum =  Integer.toString(getID()-1);
      String componentName[] = roiContext.getComponentNames();
      for(int i=0; i<roistats.length;i++){
          roiData[i][0] =  roinum;
          if((componentName != null) && (componentName[i] != null))
              roiData[i][1]= componentName[i];
          else roiData[i][1] = "Unknown";
          for(int j=0;j<roistats[0].length;j++){
              roiData[i][j+2] = Double.toString(roistats[i][j]);
          }
      }
   }
}
