/*
 *  This file is part of Java-GTK Mandel -
 *  Copyright (c) 2001 Markus Fritsche
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or any later version.
 *  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 mandel;

import java.io.FileNotFoundException;
import java.io.IOException;

import org.gnu.gdk.Color;
import org.gnu.gdk.Colormap;
import org.gnu.gdk.Event;
import org.gnu.gdk.GC;
import org.gnu.glade.GladeXMLException;
import org.gnu.glade.LibGlade;
import org.gnu.gtk.Button;
import org.gnu.gtk.DrawingArea;
import org.gnu.gtk.Entry;
import org.gnu.gtk.Gtk;
import org.gnu.gtk.Label;
import org.gnu.gtk.ProgressBar;
import org.gnu.gtk.RadioButton;
import org.gnu.gtk.Window;
import org.gnu.gtk.event.ButtonEvent;
import org.gnu.gtk.event.ButtonListener;
import org.gnu.gtk.event.LifeCycleEvent;
import org.gnu.gtk.event.LifeCycleListener;
import org.gnu.gtk.event.MouseEvent;
import org.gnu.gtk.event.MouseListener;

/**
 *This class is responsible for the GUI, i.e. building the window,
 *has the required callbacks etc.
 *
 * @author     Markus Fritsche
 * @created    12. November 2001
 */
public class JavaGTKMandelGUI extends java.lang.Object {
	// This one stores the last color allocated.
	// This causes a massive gain of performance
	// because allocating colors is expensive
	// (afaic)
	private short lastColor;
	// this ones store the beginning of the zoom request.
	// for further information look at zoomStart()
	private double newX;
	private double newY;

	short red = 0;
	short green = 0;
	short blue = 0;

	double oldReMin = -2.25;
	double oldImMin = -1.5;
	double oldImMax = 1.5;
	double oldReMax = 0.75;

	private JavaGTKMandelMath mandelmath;

	//    GdkPixmap pmap = null;

	// needed for the zoomStart/ zoomEnd functions
	// and the callbacks of the radiobuttons
	private Label lbl_Img_Julia = null;
	private Label lbl_Re_Julia = null;
	private Entry reMaxE = null;
	private Entry reMinE = null;
	private Entry imgMinE = null;
	private Entry imgMaxE = null;
	private Entry depthE = null;
	private Entry txt_red = null;
	private Entry txt_green = null;
	private Entry txt_blue = null;

	private RadioButton rbtn_julia = null;
	private RadioButton rbtn_apfel = null;

	private Entry txt_Img_Julia = null;
	private Entry txt_Re_Julia = null;

	private ProgressBar progressbar = null;
	// -----------------------------------

	// our output window
	private DrawingArea drwarea = null;

	// the corrosponding GdkWindow
	private org.gnu.gdk.Window gwin = null;

	// the GdkGC used for drawing
	private GC gc = null;
	// the GdkColor used for drawing
	private Color color = null;
	// the GdkColormap used for allocation of color
	private Colormap cmap = null;

	/**
	 *Constructor for the JavaGTKMandelGUI object
	 */
	public JavaGTKMandelGUI() {
		// build up the window and get various objects.
		createMainWindow();

		// here are our mathematical functions. Only interesting
		// for freaks ;-)
		mandelmath = new JavaGTKMandelMath();

		// create a GdkColormap. we will allocate our colors with
		// the help of it
		cmap = new Colormap();
	}

	/**
	 * what to do if a delete_event occurs
	 *
	 * @param  event  a GdkEvent
	 * @return        always false
	 */
	public boolean delete_event(Event event) {
		return (false);
	}

	/**
	 *computes the values and paints the fractal
	 */
	public void btn_paint_clicked() {
		// get the width and height of the window
		int width = (int) gwin.getWidth();
		int height = (int) gwin.getHeight();
		// get the preferences
		double reMax = Double.parseDouble(reMaxE.getText());
		double reMin = Double.parseDouble(reMinE.getText());
		double imMax = Double.parseDouble(imgMaxE.getText());
		double imMin = Double.parseDouble(imgMinE.getText());
		int depth = Integer.parseInt(depthE.getText());
		double restRe = 0.0;
		double restIm = 0.0;
		red = Short.parseShort(txt_red.getText());
		green = Short.parseShort(txt_green.getText());
		blue = Short.parseShort(txt_blue.getText());

		lastColor = -1;

		if (rbtn_julia.getState()) {
			restRe = Double.parseDouble(txt_Re_Julia.getText());
			restIm = Double.parseDouble(txt_Img_Julia.getText());
			for (int j = 0; j < width; ++j) {

				progressbar.setFraction(.01 * ((double) j / (double) width));

				Gtk.mainIteration();

				for (int i = 0; i < height; ++i) {
					double teil_re =
						mandelmath.realPart(width, j, reMin, reMax);
					double teil_im = mandelmath.imPart(height, i, imMin, imMax);
					paintPixel(
						gwin,
						j,
						i,
						mandelmath.compute(
							depth,
							teil_re,
							teil_im,
							restRe,
							restIm));
				}
			}

		} else {

			for (int j = 0; j < width; ++j) {

				progressbar.setFraction(.01 * ((double) j / (double) width));

				Gtk.mainIteration();

				for (int i = 0; i < height; ++i) {

					double teil_re =
						mandelmath.realPart(width, j, reMin, reMax);
					double teil_im = mandelmath.imPart(height, i, imMin, imMax);
					paintPixel(
						gwin,
						j,
						i,
						mandelmath.compute(
							depth,
							teil_re,
							teil_im,
							teil_re,
							teil_im));
				}
			}
		}

	}

	/**
	 *Description of the Method
	 *
	 * @param  event  a GdkEvent, emitted by clicking on
	 * the drawing area. Calculate the new reMin/ imMax
	 * @return        always false
	 */
	public void zoomStart(MouseEvent event) {
		int width = gwin.getWidth();
		int height = gwin.getHeight();
		double reMin = Double.parseDouble(reMinE.getText());
		double reMax = Double.parseDouble(reMaxE.getText());
		double imMin = Double.parseDouble(imgMinE.getText());
		double imMax = Double.parseDouble(imgMaxE.getText());
		newX = mandelmath.realPart(width, (int) event.getX(), reMin, reMax);
		newY = mandelmath.imPart(height, (int) event.getY(), imMax, imMin);
	}

	/**
	 *Called when the mousebutton is released. For further information look
	 *at zoomStart
	 *
	 * @param  event  a GdkEvent
	 * @return        always false
	 */
	public void zoomEnd(MouseEvent event) {
		int width = gwin.getWidth();
		int height = gwin.getHeight();
		double reMin = oldReMin = Double.parseDouble(reMinE.getText());
		double reMax = oldReMax = Double.parseDouble(reMaxE.getText());
		double imMin = oldImMin = Double.parseDouble(imgMinE.getText());
		double imMax = oldImMax = Double.parseDouble(imgMaxE.getText());
		reMax = mandelmath.realPart(width, (int) event.getX(), reMin, reMax);
		imMin = mandelmath.imPart(height, (int) event.getY(), imMax, imMin);
		reMaxE.setText(Double.toString(reMax));
		imgMinE.setText(Double.toString(imMin));
		reMinE.setText(Double.toString(newX));
		imgMaxE.setText(Double.toString(newY));
	}

	/**
	 *Sets the defaults when the button Default was clicked
	 */
	public void defaults() {
		// set default values
		txt_Img_Julia.setText("0.18");
		txt_Re_Julia.setText("-0.743");
		reMaxE.setText("0.75");
		reMinE.setText("-2.25");
		imgMaxE.setText("1.5");
		imgMinE.setText("-1.5");
		depthE.setText("400");
	}

	public void btn_back_clicked() {
		reMaxE.setText(Double.toString(oldReMax));
		reMinE.setText(Double.toString(oldReMin));
		imgMinE.setText(Double.toString(oldImMin));
		imgMaxE.setText(Double.toString(oldImMax));
	}

	/**
	 *activates the Entry fields for julia
	 */
	public void rbtn_julia_clicked() {
		// activate the fields for julia
		lbl_Img_Julia.setSensitive(true);
		txt_Img_Julia.setSensitive(true);
		lbl_Re_Julia.setSensitive(true);
		txt_Re_Julia.setSensitive(true);
	}

	/**
	 *deactivates the Entrys for julia
	 */
	public void rbtn_apfel_clicked() {
		// deactivate fields for julia
		lbl_Img_Julia.setSensitive(false);
		txt_Img_Julia.setSensitive(false);
		lbl_Re_Julia.setSensitive(false);
		txt_Re_Julia.setSensitive(false);
	}

	/**
	 *Gets the lowLevels attribute of the MandelGUI object
	 */
	private void getLowLevels() {
		// get the GdkWindow of drwarea
		gwin = drwarea.getParentWindow();
		color = new Color(0, 0, 0);
		// create a new GdkGC, here we will put our color with
		// which we will draw the pixels with
		gc = new GC(gwin);
	}

	/**
	 *Description of the Method
	 *
	 * @param  window      window (GdkWindow or GdkPixmap) to draw the
	 *pixel in
	 * @param  x           x position of the pixel
	 * @param  y           y position of the pixel
	 * @param  colorvalue  a value to generate a color of
	 */
	private void paintPixel(
		org.gnu.gdk.Window window,
		int x,
		int y,
		short colorvalue) {
		if (lastColor != colorvalue) {
			color.setRed((short) (colorvalue * red));
			color.setBlue((short) (colorvalue * blue));
			color.setGreen((short) (colorvalue * green));
			cmap.allocateColor(color, false, true);
			gc.setForeground(color);
		}
		// draw the pixel
		window.drawPoint(gc, x, y);
	}

	/**
	 *Builds up the application window and attaches the callbacks
	 */
	private void createMainWindow() {
		// load the glade interface definition
		LibGlade glade = null;
		try {
			glade = new LibGlade("mandel.glade", this);
		} catch (GladeXMLException ex) {
			ex.printStackTrace();			
		} catch (FileNotFoundException ex) {
			 ex.printStackTrace();
		} catch (IOException ex) {
			ex.printStackTrace();
		}

		// attach the callback function for btn_end
		Button btn_end = (Button) glade.getWidget("btn_end");
		btn_end.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK))
					Gtk.mainQuit();
			}
		});

		// attach the callback function for btn_default
		Button btn_default = (Button) glade.getWidget("btn_default");
		btn_default.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK))
					defaults();
			}
		});

		// get the labels for julia options
		lbl_Img_Julia = (Label) glade.getWidget("lbl_Img_Julia");
		lbl_Re_Julia = (Label) glade.getWidget("lbl_Re_Julia");
		txt_Img_Julia = (Entry) glade.getWidget("txt_Img_Julia");
		txt_Re_Julia = (Entry) glade.getWidget("txt_Re_Julia");

		// get the other option fields
		reMaxE = (Entry) glade.getWidget("reMax");
		reMinE = (Entry) glade.getWidget("reMin");
		imgMaxE = (Entry) glade.getWidget("imgMax");
		imgMinE = (Entry) glade.getWidget("imgMin");
		depthE = (Entry) glade.getWidget("depth");
		txt_red = (Entry) glade.getWidget("txt_red");
		txt_green = (Entry) glade.getWidget("txt_green");
		txt_blue = (Entry) glade.getWidget("txt_blue");

		// get the two radiobuttons and attach the callback functions
		rbtn_julia = (RadioButton) glade.getWidget("rbtn_julia");
		rbtn_julia.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK))
					rbtn_julia_clicked();
			}
		});
		rbtn_apfel = (RadioButton) glade.getWidget("rbtn_apfel");
		rbtn_apfel.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK))
					rbtn_apfel_clicked();
			}
		});

		// get the main window and attach the callback functions
		Window win = (Window) glade.getWidget("window1");
		win.addListener(new LifeCycleListener() {
			public void lifeCycleEvent(LifeCycleEvent event) {}
			public boolean lifeCycleQuery(LifeCycleEvent event) {
				if (event.isOfType(LifeCycleEvent.Type.DESTROY) || 
						event.isOfType(LifeCycleEvent.Type.DELETE)) {
						Gtk.mainQuit();
				}
				return false;
			}
		});

		// get the action button
		Button btn_paint = (Button) glade.getWidget("btn_paint");
		btn_paint.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK))
					btn_paint_clicked();
			}
		});

		Button btn_back = (Button) glade.getWidget("btn_back");
		btn_back.addListener(new ButtonListener() {
			public void buttonEvent(ButtonEvent event) {
				if (event.isOfType(ButtonEvent.Type.CLICK))
					btn_back_clicked();
			}
		});

		progressbar = (ProgressBar) glade.getWidget("progressbar");

		// get the DrawingArea, where we will put the computed
		// picture
		drwarea = (DrawingArea) glade.getWidget("drwarea");

		// and, of course, attach the functions for zooming.
		// look into mandel.glade
		// (GDK_BUTTON_PRESS_MASK and GDK_BUTTON_RELEASE_MASK will
		// be emitted by drwarea)
		drwarea.addListener(new MouseListener() {
			public boolean mouseEvent(MouseEvent event) {
				if (event.isOfType(MouseEvent.Type.BUTTON_PRESS)) {
					zoomStart(event);
				} else if (event.isOfType(MouseEvent.Type.BUTTON_RELEASE)) {
					zoomEnd(event);
				}
				return true;
			}
		});

		// get some lowlevel graphic things, like a color, etc.
		getLowLevels();

		// show the main window with all child widgets
		win.showAll();

	}

}
