/* BEGIN_HEADER                                              Java TreeView
 *
 * $Author: rqluk $
 * $RCSfile: ATRZoomView.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.*;
import java.awt.event.*;
import java.util.Observable;

import javax.swing.JPanel;
import javax.swing.JScrollBar;

import edu.stanford.genetics.treeview.*;

/**
 *  Draws an array tree to show the relations between arrays This object requires
 *  a MapContainer to figure out the offsets for the arrays. Furthermore, it sets
 *  up a scrollbar to scroll the tree, although there is currently no way to specify
 *  how large you would like the scrollable area to be, so the height is just set
 *  to match the available space.
 *
 * @author     Alok Saldanha <alok@genome.stanford.edu>
 * @version    $Revision: 1.1 $ $Date: 2006-08-16 19:13:45 $
 */

public class ATRZoomView extends ModelView implements
		MouseListener, KeyListener {
	/**  Constructor, sets up AWT components  */
	public ATRZoomView() {
		super();

		panel = new JPanel();
		scrollbar = new JScrollBar(JScrollBar.VERTICAL, 0, 1, 0, 1);
		destRect = new Rectangle();

		panel.setLayout(new BorderLayout());
		panel.add(this, BorderLayout.CENTER);
		panel.add(scrollbar, BorderLayout.EAST);

		addMouseListener(this);
		addKeyListener(this);
	}

		protected HeaderSummary headerSummary = new HeaderSummary();
	/** Setter for headerSummary */
	public void setHeaderSummary(HeaderSummary headerSummary) {
		this.headerSummary = headerSummary;
	}
	/** Getter for headerSummary */
	public HeaderSummary getHeaderSummary() {
		return headerSummary;
	}


	private final static String[] hints = {
			"Click to select node",
			" - use arrow keys to navigate tree",
			};


	/*inherit description*/
	public String[] getHints() {
		return hints;
	}


	/**
	 *  Set the selected node, update the arraySelection, and redraw
	 *Does nothing if the node is already selected.
	 *
	 * @param  n  The new node to be selected. 
	 */
	public void setSelectedNode(TreeDrawerNode n) {
		if (selectedNode == n) {
			return;
		}
		/*
		if (selectedNode != null)
		 drawer.paintSubtree(offscreenGraphics,
		 xScaleEq, yScaleEq,
		 destRect, selectedNode, false);
		selectedNode = n;
	   if (selectedNode != null)
		 drawer.paintSubtree(offscreenGraphics,
		xScaleEq, yScaleEq,
		destRect, selectedNode, true);
		*/
		selectedNode = n;
		offscreenValid = false;
		if ((status != null) && hasMouse) {
			status.setMessages(getStatus());
		}
		synchMap();
		repaint();
	}


	private void synchMap() {
		if ((selectedNode != null) && (arraySelection != null)) {
			int start  = (int) (selectedNode.getLeftLeaf().getIndex());
			int end    = (int) (selectedNode.getRightLeaf().getIndex());
			
			if(viewFrame.getDataModel().getDataMatrix().getNumCol() > viewFrame.getDataModel().getDataMatrix().getNumUnappendedCol())
			{
				end = Math.max(viewFrame.getDataModel().getDataMatrix().getNumCol(), end); 
			}
			
			arraySelection.deselectAllIndexes();
			arraySelection.setSelectedNode(selectedNode.getId());
			arraySelection.selectIndexRange(start, end);
			arraySelection.notifyObservers();
		}
	}


	/**
	 *  Set arraySelection
	 *
	 * @param  arraySelection  The TreeSelection which clicking on this tree will modify.
	 */
	public void setArraySelection(TreeSelectionI arraySelection) {
		if (this.arraySelection != null) {
			this.arraySelection.deleteObserver(this);
		}
		this.arraySelection = arraySelection;
		this.arraySelection.addObserver(this);
	}


	/**
	 *  Set the drawer
	 *
	 * @param  d  The new drawer
	 */
	public void setInvertedTreeDrawer(InvertedTreeDrawer d) {
		if (drawer != null) {
			drawer.deleteObserver(this);
		}
		drawer = d;
		drawer.addObserver(this);
	}


	/**
	 *  Set the zoom map
	 *
	 *  Specifies where to draw leaves of tree.
	 */
	public void setZoomMap(MapContainer m) {
		if (zoomMap != null) {
			zoomMap.deleteObserver(this);
		}
		zoomMap = m;
		zoomMap.addObserver(this);
	}


	/**
	 *  expect updates to come from arraySelection, zoomMap and drawer
	 *
	 * @param  o    Observable sending update
	 * @param  arg  Argument, typically null
	 */
	public void update(Observable o, Object arg) {
		if (o == drawer) {
			//System.out.println("Got an update from drawer");
			offscreenValid = false;
			repaint();
		} else if (o == zoomMap) {
			// will call offscreenvalid, repaint() itself
			// could have been a translation...
			offscreenValid = false;
			repaint();
		} else if (o == arraySelection) {
			setSelectedNode(drawer.getNodeById(arraySelection.getSelectedNode()));
		} else {
			System.out.println(viewName() + "Got an update from unknown " + o);
		}
	}


	/**
	 *  Need to blit another part of the buffer to the screen when the scrollbar moves.
	 *
	 * @param  evt  scrollbar adjustment event
	 */
	public void adjustmentValueChanged(AdjustmentEvent evt) {
		repaint();
	}


	/**
	 *  Implementation of abstract method
	 *
	 * @return    returns name of this ModelView
	 */
	public String viewName() {
		return "ATRZoomView";
	}


	/**
	 *  Gets some user-interpretatble status information for the ATRZoomView object
	 *
	 * @return    Text describing selected node correlation
	 */
		public String[]  getStatus() {
			String [] status;
			if (selectedNode != null) {
				int [] nameIndex = getHeaderSummary().getIncluded();
				status = new String [nameIndex.length * 2];
				HeaderInfo atrInfo = getViewFrame().getDataModel().getAtrHeaderInfo();
				String [] names = atrInfo.getNames();
				for (int i = 0; i < nameIndex.length; i++) {
					status[2*i] = names[nameIndex[i]] +":";
					status[2*i+1] = " " +atrInfo.getHeader(atrInfo.getHeaderIndex(selectedNode.getId()))[ nameIndex[i]];
				}
			} else {
				status = new String [2];
				status[0] = "Select Node to ";
				status[1] = "view annotation.";
			}
			return status;
		}


	/**
	 *  updates buffer to reflect current state
	 *
	 * @param  g  Graphics object to draw to
	 */
	public void updateBuffer(Graphics g) {
		if (offscreenChanged == true) {
			offscreenValid = false;
		}
		if (offscreenValid == false) {
			if ((drawer != null) && (selectedNode != null)) {
				zoomMap.setAvailablePixels(offscreenSize.width);

				// clear the pallette...
				g.setColor(Color.white);
				g.fillRect
						(0, 0, offscreenSize.width, offscreenSize.height);
				g.setColor(Color.black);

				// don't bother drawing leaf
				if (selectedNode.isLeaf()) {
					return;
				}

				//	calculate Scaling
				destRect.setBounds(0, 0, zoomMap.getUsedPixels(), offscreenSize.height);
				g.setClip(destRect.x, destRect.y, destRect.width,
						destRect.height);
				xScaleEq = new LinearTransformation
						(zoomMap.getIndex(destRect.x), destRect.x,
						zoomMap.getIndex(destRect.x + destRect.width),
						destRect.x + destRect.width);
				yScaleEq = new LinearTransformation
						(selectedNode.getMinCorr(), destRect.y,
						drawer.getCorrMax(), destRect.y + destRect.height);

				// draw
				drawer.paint(g,
						xScaleEq, yScaleEq,
//						destRect, selectedNode);
// 5/13/2004 - this allows us to see colors in the zoomed dendrogram.
						destRect, null);
			} else {
				// most likely, no selection...
				// clear the pallette...
				g.setColor(Color.white);
				g.fillRect(0, 0, offscreenSize.width, offscreenSize.height);
			}
		}
	}


	/**
	 *  On mouseclick, select a node
	 *
	 * @param  e  Mouse clicking event
	 */
	public void mouseClicked(MouseEvent e) {
		if (this == null) {
			return;
		}
		if (isEnabled() == false) {
			return;
		}
		if (enclosingWindow().isActive() == false) {
			return;
		}
		if (drawer != null) {
			// the trick is translating back to the normalized space...
			setSelectedNode
					(drawer.getClosest(xScaleEq.inverseTransform(e.getX()),
					yScaleEq.inverseTransform(e.getY()),
			// weight must have correlation slope on top
					yScaleEq.getSlope() / xScaleEq.getSlope())
					);
		}
	}


	// method from KeyListener
	/**
	 *  Use keypress to navigate nodes
	 *
	 * up selects parent of current.
	 * left selects left child.
	 * right selects right child
	 * down selects child with most descendants.
	 */
	public void keyPressed(KeyEvent e) {
		if (selectedNode == null) {
			return;
		}

		int c                = e.getKeyCode();
		TreeDrawerNode cand  = null;
		switch (c) {
						case KeyEvent.VK_UP:
							cand = selectedNode.getParent();
							break;
						// hey, the tree is upside down!
						case KeyEvent.VK_LEFT:
							if (selectedNode.isLeaf() == false) {
								cand = selectedNode.getRight();
							}
							break;
						case KeyEvent.VK_RIGHT:
							if (selectedNode.isLeaf() == false) {
								cand = selectedNode.getLeft();
							}
							break;
						case KeyEvent.VK_DOWN:
							if (selectedNode.isLeaf() == false) {
								TreeDrawerNode right  = selectedNode.getRight();
								TreeDrawerNode left   = selectedNode.getLeft();
								if (right.getRange() > left.getRange()) {
									cand = right;
								} else {
									cand = left;
								}
							}
							break;
		}
		if (cand != null) {
			setSelectedNode(cand);
		}
	}


	/**
	 *  Ignore key releases
	 *
	 * @param  e  Key release event
	 */
	public void keyReleased(KeyEvent e) { }


	/**
	 *  Ignore key types
	 *
	 * @param  e  Key type event
	 */
	public void keyTyped(KeyEvent e) { }


	private TreeSelectionI arraySelection;
	private LinearTransformation xScaleEq, yScaleEq;
	private MapContainer zoomMap;
	private JScrollBar scrollbar;
	private InvertedTreeDrawer drawer = null;
	private TreeDrawerNode selectedNode = null;
	private Rectangle destRect = null;
}

