package com.hero.ui.widgets;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import com.hero.model.NumericDocument;
import com.hero.objects.GenericObject;

/**
 * Copyright (c) 2000 - 2005, CompNet Design, Inc. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, is prohibited unless the following conditions are met: 1.
 * Express written consent of CompNet Design, Inc. is obtained by the developer.
 * 2. Redistributions must retain this copyright notice. THIS SOFTWARE IS
 * PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * @author CompNet Design, Inc.
 * @version $Revision$
 */

public class LevelTF extends JComponent {

	private static final long serialVersionUID = -5052663705888531663L;

	int start;

	int max;

	int min;

	int oldVal = -999999;

	int holder = 0;

	JTextField tf;

	JButton inc;

	JButton dec;

	DocumentListener tfListener;

	boolean incMouseDown;

	boolean decMouseDown;
	
	private GenericObject trackObject;

	public LevelTF(int st, int mx, int mn) {
		super();
		setLayout(new BorderLayout());
		start = st;
		max = mx;
		min = mn;
		oldVal = start;
		holder = start;
		initWidgets();
		initListeners();
		layoutComponent();
		setCurrent(start);
	}
	
	public void setTrackObject(GenericObject o) {
		trackObject = o;
	}
	
	public GenericObject getTrackObject() {
		return trackObject;
	}

	/**
	 * Returns the current value of the field.
	 * 
	 * @return
	 */
	public int getCurrent() {
		// int current = 0;
		// try {
		// current = Integer.parseInt(tf.getText());
		// }
		// catch (NumberFormatException ex) {
		// }
		// return current;
		return holder;
	}

	/**
	 * Returns the minimum size of this widget for display purposes.
	 */
	@Override
	public Dimension getMinimumSize() {
		return super.getPreferredSize();
	}

	private void initListeners() {
		inc.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int current = 0;
				try {
					current = Integer.parseInt(tf.getText());
				} catch (NumberFormatException ex) {
				}
				current++;
				if (current <= max) {
					setCurrent(current);
				}
			}
		});
		inc.addMouseListener(new MouseAdapter() {
			Thread runner;

			int current;

			@Override
			public void mousePressed(MouseEvent e) {
				incMouseDown = true;
				if ((runner == null) || !runner.isAlive()) {
					runner = new Thread() {
						@Override
						public void run() {
							int count = 0;
							while (incMouseDown) {
								try {
									Thread.sleep(count == 0 ? 1000
											: count > 10 ? 200
													: count > 50 ? 100 : 500);
									count++;
									if (!incMouseDown) {
										return;
									}
									current = Integer.parseInt(tf.getText());
									current++;
									if (current <= max) {
										SwingUtilities
												.invokeAndWait(new Runnable() {
													public void run() {
														setCurrent(current);
													}
												});
									} else {
										return;
									}
								} catch (Exception exp) {
									return;
								}
							}
						}
					};
					runner.start();
				}
			}

			@Override
			public void mouseReleased(MouseEvent e) {
				incMouseDown = false;
				firePropertyChange("Level", holder - 1, holder);
			}
		});
		inc.addFocusListener(new FocusListener() {
			public void focusGained(FocusEvent e) {

			}

			public void focusLost(FocusEvent e) {
				incMouseDown = false;
				firePropertyChange("Level", holder - 1, holder);
			}
		});
		dec.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int current = 0;
				try {
					current = Integer.parseInt(tf.getText());
				} catch (NumberFormatException ex) {
				}
				current--;
				if (current >= min) {
					setCurrent(current);
				}
			}
		});
		dec.addMouseListener(new MouseAdapter() {
			Thread runner;

			int current;

			@Override
			public void mousePressed(MouseEvent e) {
				decMouseDown = true;
				if ((runner == null) || !runner.isAlive()) {
					runner = new Thread() {
						@Override
						public void run() {
							int count = 0;
							while (decMouseDown) {
								try {
									Thread.sleep(count == 0 ? 1000
											: count > 10 ? 200
													: count > 50 ? 100 : 500);
									count++;
									if (!decMouseDown) {
										return;
									}
									current = Integer.parseInt(tf.getText());
									current--;
									if (current >= min) {
										SwingUtilities
												.invokeAndWait(new Runnable() {
													public void run() {
														setCurrent(current);
													}
												});
									} else {
										return;
									}
								} catch (Exception exp) {
									return;
								}
							}
						}
					};
					runner.start();
				}
			}

			@Override
			public void mouseReleased(MouseEvent e) {
				decMouseDown = false;
				firePropertyChange("Level", holder - 1, holder);
			}

		});
		dec.addFocusListener(new FocusListener() {
			public void focusGained(FocusEvent e) {

			}

			public void focusLost(FocusEvent e) {
				decMouseDown = false;
				firePropertyChange("Level", holder - 1, holder);
			}
		});
		tfListener = new DocumentListener() {
			public void changedUpdate(DocumentEvent e) {
				if (tf.getText().trim().length() == 0) {
					return;
				}
				try {
					int val = Integer.parseInt(tf.getText());
					if ((val <= max) && ((val >= min) || (getCurrent() < min))) {
						setCurrent(val);
					} else if (val > max) {
						setCurrent(max);
					} else if (val < min) {
						setCurrent(min);
					}
				} catch (NumberFormatException ex) {
					setCurrent(getCurrent());
				}
			}

			public void insertUpdate(DocumentEvent e) {
				if (tf.getText().trim().length() == 0) {
					return;
				}
				try {
					int val = Integer.parseInt(tf.getText());
					if ((val <= max) && ((val >= min) || (getCurrent() < min))) {
						setCurrent(val);
					} else if (val > max) {
						setCurrent(max);
					} else if (val < min) {
						setCurrent(min);
					}
				} catch (NumberFormatException ex) {
					setCurrent(getCurrent());
				}
			}

			public void removeUpdate(DocumentEvent e) {
				if (tf.getText().trim().length() == 0) {
					return;
				}
				try {
					int val = Integer.parseInt(tf.getText());
					if ((val <= max) && ((val >= min) || (getCurrent() < min))) {
						setCurrent(val);
					} else if (val > max) {
						setCurrent(max);
					} else if (val < min) {
						setCurrent(min);
					}
				} catch (NumberFormatException ex) {
					setCurrent(getCurrent());
				}
			}
		};
		tf.getDocument().addDocumentListener(tfListener);
		tf.addFocusListener(new FocusListener() {
			public void focusGained(FocusEvent e) {
				tf.selectAll();
			}

			public void focusLost(FocusEvent e) {
				if (tf.getText().trim().length() == 0) {
					tf.setText("" + getCurrent());
				}
				incMouseDown = false;
				decMouseDown = false;
			}
		});
		addFocusListener(new FocusAdapter() {
			@Override
			public void focusLost(FocusEvent e) {
				incMouseDown = false;
				decMouseDown = false;
			}
		});
	}

	private void initWidgets() {
		int numcols = 4;
		if (max > 9999) {
			numcols = 7;
		}
		tf = new JTextField(new NumericDocument(max, min), "" + start, numcols);
		tf.setHorizontalAlignment(SwingConstants.CENTER);
		inc = new JButton(">");
		inc.setMargin(new Insets(0, 0, 0, 0));
		inc.setFocusable(false);
		dec = new JButton("<");
		dec.setMargin(new Insets(0, 0, 0, 0));
		dec.setFocusable(false);
	}

	private void layoutComponent() {
		add(dec, BorderLayout.WEST);
		add(tf, BorderLayout.CENTER);
		add(inc, BorderLayout.EAST);
	}

	/**
	 * Sets the current value of the field.
	 * 
	 * @param val
	 */
	public synchronized void setCurrent(int val) {
		holder = val;
		tf.getDocument().removeDocumentListener(tfListener);
		try {
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					if (tf.getText().equals("" + holder)) {
						return;
					}
					tf.setText("" + holder);
				}
			});
		} catch (Exception ex) {
		}
		if (holder >= max) {
			inc.setEnabled(false);
		} else {
			inc.setEnabled(true);
		}
		if (holder <= min) {
			dec.setEnabled(false);
		} else {
			dec.setEnabled(true);
		}
		if (holder != oldVal) {
			int temp = oldVal;
			oldVal = holder;
			if (!incMouseDown && !decMouseDown) {
				firePropertyChange("Level", temp, holder);
			}
		}
		tf.getDocument().addDocumentListener(tfListener);
	}

	/**
	 * Sets whether this widget is enabled or not.
	 */
	@Override
	public void setEnabled(boolean enabled) {
		tf.setEnabled(enabled);
		inc.setEnabled(enabled);
		dec.setEnabled(enabled);
	}

	/**
	 * Sets the maximum value for the field.
	 * 
	 * @param maxVal
	 */
	public void setMax(int maxVal) {
		max = maxVal;
		((NumericDocument) tf.getDocument()).setMax(max);
		if (getCurrent() > max) {
			setCurrent(max);
		} else if (getCurrent() < max) {
			inc.setEnabled(true);
		} else {
			inc.setEnabled(false);
		}
	}

	/**
	 * Sets the minimum value for the field.
	 * 
	 * @param minVal
	 */
	public void setMin(int minVal) {
		min = minVal;
		((NumericDocument) tf.getDocument()).setMin(min);
		if (getCurrent() < min) {
			setCurrent(min);
		} else if (getCurrent() > min) {
			dec.setEnabled(true);
		} else {
			dec.setEnabled(false);
		}
	}

}