package javacodebook.gui.buildcomponent;

import java.awt.*;
import java.awt.event.*;

/**
 * @author benjamin_rusch
 *
 * Der RoundButton ist eine komplett selbst definierte AWT Komponente, die beim 
 * Klicken seine Darstellung ändert und angemeldete ActionListener informiert.
 * Durch das geschilderte Vorgehen, lassen sich natürlich auch 
 * andere Komponenten selber bauen. Das Beispiel soll die Vorgehensweise
 * umreissen, im Detail kann es noch ausgebaut werden.
 */

public class RoundButton extends Component {

	// Beim Aufruf von addActionListener wird über den EventMulticaster 
	// ein neuer ActionListener aus allen bisher angemeldeten ActionListenern
	// gebaut, und hier abgelegt
	private ActionListener actionListener;
	
	// Beschriftung vom Button
	private String buttonLabel;
	
	// Zustand vom Button
	protected boolean pressed = false;
	  
	  
	/**
   	* Konstruktor vom RoundButton ohne Beschriftung
   	*/
  	public RoundButton() {
  		this("");
  	}

	 /**
	   * Konstruktor vom RoundButton mit Beschriftung
	   */
	public RoundButton(String buttonLabel) {
		this.buttonLabel = buttonLabel;
		// MouseEvents werden aktiviert. Wenn also eine Maus über dieser 
		// Komponente geklickt wird, werden Events an diese Komponente 
	  	// weitergegebene
	  	enableEvents(AWTEvent.MOUSE_EVENT_MASK);
	}
	  
	/**
	 * Die Darstellung des runden Buttons wird hier programmiert.
	 */
	public void paint(Graphics g) {
		// Radius des Buttons wird anhand der Komponentengroesse bestimmt 
		int s = Math.min(getSize().width - 1, getSize().height - 1);
	  
	  	// Das Button-Innnere wird gemalt
	  	if(pressed) {
	  		g.setColor(getBackground().darker().darker());
		} 
		else {
			g.setColor(getBackground());
		}
		g.fillArc(0, 0, s, s, 0, 360);
	  
	  	// Die Umrandung des Buttons wird gemalt
	  	g.setColor(getBackground().darker().darker().darker());
	  	g.drawArc(0, 0, s, s, 0, 360);
	  
	  	// Das Label wird im Center des Button platziert
	  	Font f = getFont();
		if(f != null) {
			FontMetrics fm = getFontMetrics(getFont());
			g.setColor(getForeground());
			g.drawString(buttonLabel,
					s/2 - fm.stringWidth(buttonLabel)/2,
			       	s/2 + fm.getMaxDescent());
		}
	}
	  
	/**
	 * Viele Layoutmamager benötigen die "preferred size", daher wird 
	 * diese Methode hier überschrieben
	 */
	public Dimension getPreferredSize() {
		Font f = getFont();
		if(f != null) {
			FontMetrics fm = getFontMetrics(getFont());
			int max = Math.max(fm.stringWidth(buttonLabel) + 40, fm.getHeight() + 40);
			return new Dimension(max, max);
		} 
		else {
			return new Dimension(100, 100);
		}
	  }
	  
	/**
	 * Der ActionListener wird an diesem Button angemeldet
	 */
	 public void addActionListener(ActionListener listener) {
		// Der AWTEventMulticaster führt mehrer ActionListener in einen zusammen, 
	   	// so dass einen Thread-sichere Abarbeitung ermöglicht wird. 
	   	actionListener = AWTEventMulticaster.add(actionListener, listener);

	 }
	 
	/**
	 *  Der ActionListener wird von diesem Button entfernt
	 */
	public void removeActionListener(ActionListener listener) {
		actionListener = AWTEventMulticaster.remove(actionListener, listener);
	}
	
	/**
	 * Nur wenn innerhalb des Kreises geklickt wird, soll ein Event ausgelöst werden
	 * contains liefert nur dann true zurück wenn die Maus sich innerhalb befindet.
	 */
	public boolean contains(int x, int y) {
		int mx = getSize().width/2;
	    int my = getSize().height/2;
	    // Formel die anhand der geometrischen Getzmäßigkeiten eines Kreises 
	   	// berechnet, ob eine Koordinate sich innerhalb oder außerhalb befindet. 
	    return (((mx-x)*(mx-x) + (my-y)*(my-y)) <= mx*mx);
	}
	   
	/**
	 * Wird aufgerufen wenn die Komponente geklickt wurde
	 */
	public void processMouseEvent(MouseEvent e) {
		Graphics g;
	    switch(e.getID()) {
	    	case MouseEvent.MOUSE_PRESSED:
		    	pressed = true;
		    	    
				// Die Komponente wird neu gemalt, intern wird paint aufgerufen, 
		    	// daher kann das gesetzte Attribut pressed berücksichtigt werden		
		    	repaint(); 
		    	break;
		    case MouseEvent.MOUSE_RELEASED:
	    		if(actionListener != null) {
	       			actionListener.actionPerformed(new ActionEvent(
		   					this, ActionEvent.ACTION_PERFORMED, buttonLabel));
	    		}
	    		if(pressed == true) {
					pressed = false;
					repaint();
				}
				break;
	  		case MouseEvent.MOUSE_ENTERED:
				break;
	  		case MouseEvent.MOUSE_EXITED:
				if(pressed == true) {
					pressed = false;
					repaint();
	    		}
				break;
	   	}
	    super.processMouseEvent(e);
	}
	
}
