/*--- formatted by Jindent 2.1, (www.c-lab.de/~jindent) ---*/

/*
 * The package jm.kidney is part of the Renal Function Project
 * for analysis of dynamic contrast medium evalutions MRT-Images
 * of the Kidneys.
 * 
 * Copyright (C) 1999 / 2000 Jens Martin
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License 2
 * as published by the Free Software Foundation.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package jm.kidney;

import ij.*;

import java.awt.*;
import java.awt.event.*;
import java.io.*;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;

import jm.extension.*;
import jm.cepstrum.*;
import jm.util.*;


/**
 * Diese Klasse ermoeglicht das manuelle Verschieben von Halbbildern
 * zur Nachjustierung von schlechten Ergebnissen der Kaptrum-Filterung.
 * <BR> Der Aufrug erfolgt aus der Klasse ExImageCanvas.
 * 
 * @see jm.extension.ExImageCanvas
 * @version  1.0, 14/02/2000
 * @author   Jens Martin
 */
public class ManualMover extends JFrame implements ActionListener, ChangeListener {
	private ImagePlus							imagePlus;
	private ImageStack						imageStack;
	private ExStackWindow					stackWindow;
	private int										imageNr;
	private int										half;
	private int										width;
	private int										height;
	private short[]								orgPixel;
	private short[]								newPixel;
	private double								moveAmountX;
	private double								moveAmountY;
	private double								moveStep;
	private Rectangle							windowPos;
	private BicubicInterpolation	bic;

	/*
	 * 2000.8.2 tha ersetzt:
	 * String imagePath = "." + File.separator + "images" + File.separator;
	 * private ImageIcon ImageIconUp = new ImageIcon(imagePath + "ArrowUp.gif");
	 * private ImageIcon ImageIconDown = new ImageIcon(imagePath + "ArrowDown.gif");
	 * private ImageIcon ImageIconLeft = new ImageIcon(imagePath + "ArrowLeft.gif");
	 * private ImageIcon ImageIconRight = new ImageIcon(imagePath + "ArrowRight.gif");
	 */
	private ImageIcon							ImageIconUp = new ImageIcon(ManualMover.class.getResource("ArrowUp.gif"));
	private ImageIcon							ImageIconDown = new ImageIcon(ManualMover.class.getResource("ArrowDown.gif"));
	private ImageIcon							ImageIconLeft = new ImageIcon(ManualMover.class.getResource("ArrowLeft.gif"));
	private ImageIcon							ImageIconRight = new ImageIcon(ManualMover.class.getResource("ArrowRight.gif"));

	// private ImageIcon ImageIconUp = null;
	// private ImageIcon ImageIconDown = null;
	// private ImageIcon ImageIconLeft = null;
	// private ImageIcon ImageIconRight = null;

	javax.swing.JPanel						jPanelMain = new javax.swing.JPanel();
	GridBagLayout									gridBagLayout1 = new GridBagLayout();
	javax.swing.JSlider						jSliderStepping = new javax.swing.JSlider();
	javax.swing.JLabel						jLabelStepping = new javax.swing.JLabel();


	/*
	 * javax.swing.plaf.basic.BasicArrowButton jButtonUp = new javax.swing.plaf.basic.BasicArrowButton(javax.swing.SwingConstants.NORTH);
	 * javax.swing.plaf.basic.BasicArrowButton jButtonLeft = new javax.swing.plaf.basic.BasicArrowButton(javax.swing.SwingConstants.WEST);
	 * javax.swing.plaf.basic.BasicArrowButton jButtonRight = new javax.swing.plaf.basic.BasicArrowButton(javax.swing.SwingConstants.EAST);
	 * javax.swing.plaf.basic.BasicArrowButton jButtonDown = new javax.swing.plaf.basic.BasicArrowButton(javax.swing.SwingConstants.SOUTH);
	 */
	javax.swing.JButton						jButtonUp = new javax.swing.JButton(ImageIconUp);
	javax.swing.JButton						jButtonDown = new javax.swing.JButton(ImageIconDown);
	javax.swing.JButton						jButtonLeft = new javax.swing.JButton(ImageIconLeft);
	javax.swing.JButton						jButtonRight = new javax.swing.JButton(ImageIconRight);

	javax.swing.JLabel						jLabelAmountX = new javax.swing.JLabel();
	javax.swing.JLabel						jLabelAmountY = new javax.swing.JLabel();
	javax.swing.JPanel						jPanelButtons = new javax.swing.JPanel();
	ExJButton											jButtonCancel = new ExJButton();
	ExJButton											jButtonOk = new ExJButton();


	/**
	 * Der Konstruktor.
	 * @param    imp    ImagePlus-Objekt von ImageJ
	 * @param    nr     Nummer der Bildes innerhalb des Stacks
	 * @param    h      Bildhaelfte (0 = links, 1 = rechts)
	 * @param    bounds Position des Fensters des ImagePlus-Objektes
	 */
	public ManualMover(ImagePlus imp, int nr, int h, Rectangle bounds) {
		super();
		try {
			imagePlus = imp;
			width = imagePlus.getWidth();
			height = imagePlus.getHeight();
			imageStack = imagePlus.getStack();
			stackWindow = (ExStackWindow) imagePlus.getWindow();
			windowPos = bounds;
			imageNr = nr + 1;
			half = h;
			newPixel = (short[]) imageStack.getPixels(imageNr);
			orgPixel = new short[width * height];
			System.arraycopy(newPixel, 0, orgPixel, 0, width * height);
			bic = new BicubicInterpolation(newPixel, width, height);
			moveAmountX = 0.0;
			moveAmountY = 0.0;
			moveStep = 1.0;
			stackWindow.lock();

			jbInit();
			guiInit();
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}


	/**
	 * Initialisierung der Objekte des Fensters.
	 * Von JBuilder automatisch eingefuegt.
	 */
	private void jbInit() throws Exception {

		/*
		 * String imagePath = "." + File.separator + "images" + File.separator;
		 * IJ.write("IP: " + imagePath);
		 * ImageIconUp = new ImageIcon(imagePath + "ArrowUp.gif");
		 * ImageIconDown = new ImageIcon(imagePath + "ArrowDown.gif");
		 * ImageIconLeft = new ImageIcon(imagePath + "ArrowLeft.gif");
		 * ImageIconRight = new ImageIcon(imagePath + "ArrowRight.gif");
		 */
		this.setForeground(Color.lightGray);
		this.setBackground(Color.black);
		jPanelMain.setForeground(Color.lightGray);
		jPanelMain.setBackground(Color.black);
		jPanelMain.setLayout(gridBagLayout1);
		jPanelMain.setFont(new Font("Dialog", 1, 11));
		jSliderStepping.setMaximum(20);
		jSliderStepping.setMinimum(1);
		jSliderStepping.setRequestFocusEnabled(false);
		jSliderStepping.setForeground(Color.lightGray);
		jSliderStepping.setBackground(Color.black);
		jSliderStepping.setToolTipText("Movement per Mouseclick in Pixels");
		jLabelStepping.setText("-  Move Stepping: 0,1 Pixel  +");
		jLabelStepping.setForeground(Color.lightGray);
		jLabelStepping.setBackground(Color.black);
		jLabelStepping.setFont(new Font("Dialog", 1, 11));
		jButtonUp.setText("");
		jButtonUp.setMargin(new Insets(0, 0, 0, 0));

		// jButtonUp.setLabel("");
		jButtonUp.setBackground(Color.black);
		jButtonUp.setToolTipText("move up");
		jButtonUp.setFocusPainted(false);
		jButtonLeft.setText("");
		jButtonLeft.setMargin(new Insets(0, 0, 0, 0));

		// jButtonLeft.setLabel("");
		jButtonLeft.setBackground(Color.black);
		jButtonLeft.setToolTipText("move left");
		jButtonLeft.setFocusPainted(false);
		jButtonRight.setText("");
		jButtonRight.setMargin(new Insets(0, 0, 0, 0));

		// jButtonRight.setLabel("");
		jButtonRight.setBackground(Color.black);
		jButtonRight.setToolTipText("move right");
		jButtonRight.setFocusPainted(false);
		jLabelAmountX.setText(" X: +0,0");
		jLabelAmountX.setForeground(Color.white);
		jLabelAmountX.setBackground(Color.black);
		jLabelAmountX.setFont(new Font("Monospaced", 0, 11));
		jLabelAmountX.setToolTipText("Amount moved");
		jLabelAmountY.setText(" Y: +0,0");
		jLabelAmountY.setForeground(Color.white);
		jLabelAmountY.setBackground(Color.black);
		jLabelAmountY.setFont(new Font("Monospaced", 0, 11));
		jLabelAmountY.setToolTipText("Amount moved");
		jButtonDown.setText("");
		jButtonDown.setMargin(new Insets(0, 0, 0, 0));

		// jButtonDown.setLabel("");
		jButtonDown.setBackground(Color.black);
		jButtonDown.setToolTipText("move down");
		jButtonDown.setFocusPainted(false);
		jPanelButtons.setBackground(Color.black);
		jButtonCancel.setText("Cancel");
		jButtonCancel.setPreferredSize(new Dimension(75, 20));

		// jButtonCancel.setLabel("Cancel");
		jButtonCancel.setForeground(Color.lightGray);
		jButtonCancel.setBackground(Color.black);
		jButtonCancel.setFont(new Font("Dialog", 1, 11));
		jButtonOk.setText("Ok");
		jButtonOk.setPreferredSize(new Dimension(75, 20));

		// jButtonOk.setLabel("Ok");
		jButtonOk.setForeground(Color.lightGray);
		jButtonOk.setBackground(Color.black);
		jButtonOk.setFont(new Font("Dialog", 1, 11));
		this.getContentPane().add(jPanelMain, null);
		jPanelMain.add(jSliderStepping, new GridBagConstraints2(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
		jPanelMain.add(jLabelStepping, new GridBagConstraints2(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 15, 0), 0, 0));
		jPanelMain.add(jButtonUp, new GridBagConstraints2(1, 2, 1, 1, 0.0, 0.0, GridBagConstraints.SOUTH, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
		jPanelMain.add(jButtonLeft, new GridBagConstraints2(0, 3, 1, 2, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 20, 0, 0), 0, 0));
		jPanelMain.add(jButtonRight, new GridBagConstraints2(2, 3, 1, 2, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0));
		jPanelMain.add(jLabelAmountX, new GridBagConstraints2(1, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
		jPanelMain.add(jLabelAmountY, new GridBagConstraints2(1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
		jPanelMain.add(jButtonDown, new GridBagConstraints2(1, 5, 1, 1, 0.0, 0.0, GridBagConstraints.NORTH, GridBagConstraints.NONE, new Insets(2, 0, 0, 0), 0, 0));
		jPanelMain.add(jPanelButtons, new GridBagConstraints2(0, 6, 3, 1, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(15, 0, 0, 0), 0, 0));
		jPanelButtons.add(jButtonCancel, null);
		jPanelButtons.add(jButtonOk, null);
	} 


	/**
	 * Zusaetzliche Formatierungen von Objekten der GUI
	 */
	public void guiInit() {

		/*
		 * jButtonUp.setPreferredSize(new Dimension(20, 20));
		 * jButtonDown.setPreferredSize(new Dimension(20, 20));
		 * jButtonLeft.setPreferredSize(new Dimension(20, 20));
		 * jButtonRight.setPreferredSize(new Dimension(20, 20));
		 */

		jButtonCancel.addActionListener(this);
		jButtonOk.addActionListener(this);
		jButtonUp.addActionListener(this);
		jButtonDown.addActionListener(this);
		jButtonLeft.addActionListener(this);
		jButtonRight.addActionListener(this);

		jSliderStepping.addChangeListener(this);
		jSliderStepping.setValue((int) (moveStep * 10.0));

		this.setTitle(" Adjuster");
		this.pack();
		this.setResizable(false);

		Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize();
		Rectangle rect = this.getBounds();

		rect.y = windowPos.y;
		rect.x = windowPos.x + windowPos.width + 1;
		if ((windowPos.x + windowPos.width) > (screensize.width - 50)) {
			rect.x = windowPos.x - rect.width - 1;
		} 
		this.setBounds(rect);
		this.setVisible(true);

		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());	// getCrossPlatformLookAndFeelClassName());
		} catch (Exception e) {
			System.err.println("Can't set GUI to look and feel: " + e);
		} 
	} 


	/**
	 * Routine zur Nachrichtenbehandung von Mausklicks.
	 * @param evt uebergebener ActionEvent
	 */
	public void actionPerformed(ActionEvent evt) {
		Object	src = evt.getSource();

		if (src instanceof JButton) {
			if (src == jButtonOk) {
				stackWindow.unlock();
				this.setVisible(false);
				this.dispose();
			} 
			if (src == jButtonCancel) {
				System.arraycopy(orgPixel, 0, newPixel, 0, width * height);

				// imageStack.setSlice(imageNr);
				// imageStack.setPixels(orgPixel);
				imagePlus.updateAndRepaintWindow();
				stackWindow.unlock();
				this.setVisible(false);
				this.dispose();
			} 
			if (src == jButtonUp) {
				moveAmountY = moveAmountY - moveStep;
				applyShift(moveAmountX * (-1), moveAmountY * (-1));
			} 
			if (src == jButtonDown) {
				moveAmountY = moveAmountY + moveStep;
				applyShift(moveAmountX * (-1), moveAmountY * (-1));
			} 
			if (src == jButtonLeft) {
				moveAmountX = moveAmountX + moveStep;
				applyShift(moveAmountX * (-1), moveAmountY * (-1));
			} 
			if (src == jButtonRight) {
				moveAmountX = moveAmountX - moveStep;
				applyShift(moveAmountX * (-1), moveAmountY * (-1));
			} 

			if (moveAmountX <= 0) {
				jLabelAmountX.setText(" X: +" + d2s(Math.abs(moveAmountX), 1));
			} else {
				jLabelAmountX.setText(" X: -" + d2s(moveAmountX, 1));
			}

			if (moveAmountY < 0) {
				jLabelAmountY.setText(" Y: -" + d2s(Math.abs(moveAmountY), 1));
			} else {
				jLabelAmountY.setText(" Y: +" + d2s(moveAmountY, 1));
			}

		} 
	} 


	/**
	 * Wandelt eine String in einen Integer um.
	 * Erweitert die Java-Standard-Umwandlung um zusaetzliche
	 * Sicherheitsabfragen. Wenn der Sting keine Zahlen enthaelt,
	 * wird 0 zurueckgegeben.
	 * @param input uebergebener String
	 * @return int Ergebnis als Integer
	 */
	private int s2i(String input) {
		int			i;
		int			result = -1;
		String	filter = "";

		for (i = 0; i < input.length(); i++) {
			if (Character.isDigit(input.charAt(i))) {
				filter = filter + input.charAt(i);
			} 
		} 
		while ((filter.length() > 0) && (filter.charAt(0) == '0')) {
			filter = filter.substring(1);
		} 
		IJ.wait(5);
		if (filter.length() == 0) {
			return 0;
		} 
		result = (Integer.valueOf(filter)).intValue();
		return result;
	} 


	/**
	 * Wandelt ein Double in einen String um.
	 * Erweitert die IJ-Umwandlungsroutine um zusaetzliche
	 * Sicherheitsabfragen.
	 * @param d Double-Wert
	 * @param l Anzahl der beruecksichtigten Nachkommastellen
	 * @return String Ergebnis als String
	 */
	private String d2s(double d, int l) {
		String	result = IJ.d2s(d, l);

		result = result.replace('.', ',');
		while ((result.length() > 1) && (result.charAt(0) == '0') && (result.charAt(1) != ',')) {
			result = result.substring(1);
		} 
		return result;
	} 


	/**
	 * Routine zur Nachrichtenbehandung.
	 * @param evt uebergebener ChangeEvent
	 */
	public void stateChanged(ChangeEvent evt) {
		Object	src = evt.getSource();

		if (src instanceof JSlider) {
			if (src == jSliderStepping) {
				moveStep = (double) jSliderStepping.getValue() / 10.0;
				String	text = "-  Move Stepping: ";

				text = text + d2s(moveStep, 1);		// (Double.toString(min)).substring(0,3);
				text = text + " Pixel  +";
				jLabelStepping.setText(text);

				// jLabelStepping.setText("-  Move Stepping: 0,1 Pixel  +");
			} 
		} 
	} 


	/**
	 * Verschiebt ein Halbbild um reelle Pixelwert.
	 * Benuetzt bikubische Interpolation.
	 * @param shiftX Verschiebung in x-Richtung
	 * @param shiftY Verschiebung in y-Richtung
	 */
	private void applyShift(double shiftX, double shiftY) {
		int x, y;

		// IJ.write ("shifting Images, shift: x=" + shiftX + " y=" + shiftY);

		int start_x, stop_x, adder_x, start_y, stop_y, adder_y, start_half, stop_half;

		if (half == JMConst.LEFT) {
			start_half = 0;
			stop_half = width / 2;
		} else {
			start_half = width / 2;
			stop_half = width;
		} 

		start_x = start_half;
		stop_x = stop_half;
		adder_x = 1;
		start_y = 0;
		stop_y = height;
		adder_y = 1;

		if (shiftX > 0) {
			start_x = stop_half - 1;	// (width / 2) - 1;
			stop_x = start_half - 1;	// (-1);
			adder_x = (-1);
		} 
		if (shiftY > 0) {
			start_y = height - 1;
			stop_y = (-1);
			adder_y = (-1);
		} 

		// IJ.write("ApplyShift: Vorbereitung fertig!");
		y = start_y;
		while (y != stop_y) {
			x = start_x;
			while (x != stop_x) {
				int val = bic.getPixel((double) x - shiftX, (double) y + shiftY);		// [0][(y*256)+x];

				newPixel[(y * width) + x] = (short) val;
				x = x + adder_x;
			} 
			y = y + adder_y;
		} 

		// imageStack.setSlice(imageNr);
		// imageStack.setPixels(newPixel);
		imagePlus.updateAndRepaintWindow();
	} 

}


/*--- formatting done in "My Own Convention" style on 08-04-2000 ---*/

