/* BEGIN_HEADER                                              Java TreeView
 *
 * $Author: rqluk $
 * $RCSfile: KnnArrayDrawer.java,v $
 * $Revision: 1.1 $
 * $Date: 2006-08-16 19:13:45 $
 * $Name:  $
 *
 * This file is part of Java TreeView
 * Copyright (C) 2001-2014 Alok Saldanha, All Rights Reserved. Modifications Copyright (C) Lawrence Berkeley Lab.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 * END_HEADER 
 */
package edu.stanford.genetics.treeview.plugin.dendroview;

import java.awt.Rectangle;

import edu.stanford.genetics.treeview.DataModel;
/**
 *  Class for Drawing PixelViews.
 *
 * @author     Alok Saldanha <alok@genome.stanford.edu>
 * @version $Revision: 1.1 $ $Date: 2006-08-16 19:13:45 $
 *      A PixelView is a view of an array. Each cell in the view
 *      corresponds to an element in the array. The color of the pixels is
 *      determined by the value in the array according to a certain contrast. If
 *      it the value is negative, the color is green, otherwise it is red. There
 *      is also a special nodata value, draw grey. <p>
 *
 *      replacing this by a more general mapping from array value to color may
 *      be desirable in the future. <p>
 *
 *      The KnnArrayDrawer is Observable. It setsChanged() whenever the data array
 *      or color mapping (contrast) is changed, but you have to call
 *      notifyObservers() yourself. <p>
 *
 *      Upon setting a data array, KnnArrayDrawer may set a reference to the data
 *      array, and may refer to it when it asked to draw things. Of course, it
 *      may form some kind of internal buffer- you're advised to call setData()
 *      if you change the data, and not to change the data unless you call
 *      setData() too. <p>
 *
 *      The KnnArrayDrawer can draw on a Graphics object. It requires a source
 *      rectangle in units of array indexes, to determine which array values to
 *      render, and a destination rectangle to draw them to. <p>
 *
 *      At some point, we many want to allow arrays of ints to specify source
 *      rows and columns to grab data from for non-contiguous views.
 */
public class KnnArrayDrawer extends DoubleArrayDrawer {

	
	public void recalculateContrast() {
		double mean  = 0.0;
		int count    = 0;
		int nRow = dataMatrix.getNumRow();
		int nCol = dataMatrix.getNumCol();
		for (int row = 0; row < nRow; row++) {
			for (int col = 0; col < nCol; col++) {
				double val = dataMatrix.getValue(row, col);
				if (val == DataModel.NODATA) continue;
				if (val == DataModel.EMPTY) continue;
				mean += Math.abs(val);
				count++;
			}
		}
	  
	  mean /= count;

	  colorExtractor.setContrast(mean * 4);
	  colorExtractor.notifyObservers();
	}

		/**
	 *  Paint the array values onto pixels. This method will do averaging if multiple
	 *  values map to the same pixel.
	 *
	 * @param  pixels    The pixel buffer to draw to.
	 * @param  source    Specifies Rectangle of values to draw from
	 * @param  dest      Specifies Rectangle of pixels to draw to
	 * @param  scanSize  The scansize for the pixels array (in other words, the width of the image)
	 * @param  geneOrder the order of the genes. The source rect y values are taken to mean indexes into this array. If the gene order is null, the indexes from the source rect are used as indexes into the data matrix.
	 */
	public void paint(int[] pixels, Rectangle source, Rectangle dest, int scanSize, int [] geneOrder) {
		if (dataMatrix == null) {
			System.out.println("data matrix wasn't set");
		}
		// ynext will hold the first pixel of the next block.
		int ynext       = dest.y;
		// geneFirst holds first gene which contributes to this pixel.
		int geneFirst  = 0;
		// gene will hold the last gene to contribute to this pixel.
		for (int gene = 0; gene < source.height; gene++) {
			int ystart     = ynext;
			ynext = dest.y + (dest.height + gene * dest.height) / source.height;
			// keep incrementing until block is at least one pixel high
			if (ynext == ystart) {
				continue;
			}
			// xnext will hold the first pixel of the next block.
			int xnext      = dest.x;

			// arrayFirst holds first gene which contributes to this pixel.
			int arrayFirst  = 0;
			for (int array = 0; array < source.width; array++) {
				int xstart  = xnext;
				xnext = dest.x + (dest.width + array * dest.width) / source.width;
				if (xnext == xstart) {
					continue;
				}
				try {
					double val   = 0;
					int count    = 0;
					for (int i = geneFirst; i <= gene; i++) {
						for (int j = arrayFirst; j <= array; j++) {
							int actualGene = source.y + i;
							if (geneOrder != null) actualGene = geneOrder[actualGene];
							double thisVal  = dataMatrix.getValue(j + source.x, actualGene);
							if (thisVal == DataModel.EMPTY) {
								val = DataModel.EMPTY;
								count =1;
								break;
							}
							if (thisVal != DataModel.NODATA) {
								count++;
								val += thisVal;
							}
						}
						if (val == DataModel.EMPTY) break;
					}
					if (count == 0) {
						val = DataModel.NODATA;
					} else {
						val /= count;
					}
					int t_color  = colorExtractor.getARGBColor(val);
					for (int x = xstart; x < xnext; x++) {
						for (int y = ystart; y < ynext; y++) {
							pixels[x + y * scanSize] = t_color;
						}
					}
				} catch (java.lang.ArrayIndexOutOfBoundsException e) {
					//			System.out.println("out of bounds, " + (i + source.x) + ", " + (array + source.y));
				}
				arrayFirst = array + 1;
			}
			geneFirst = gene + 1;
		}
	}

}

