import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

// Clip, element dessinable, deplaceable, cliquable, survolable
public abstract class Clip {
	
	// Canvas hardware
	HWCanvas canvas;
	
	// Image de base du composant (pour eviter de redessiner la base a chaque fois)
	BufferedImage i_base;
	public BufferedImage getImage(){ return i_base; }
	
	// Position et taille du composant
	int x, y, width, height;
	
	// Zone de clique
	Rectangle zone;
	
	// Constructeur (canvas parent et si on doit l'ajouter au canvas automatiquement)
	public Clip(HWCanvas canvas, boolean add){
		this.canvas = canvas;
		this.zone = new Rectangle();
		if(add) canvas.addClip(this);
	}
	
	// Constructeur (canvas parent)
	public Clip(HWCanvas canvas){
		this.canvas = canvas;
		this.zone = new Rectangle();
		canvas.addClip(this);
	}
	
	// Methode de repaint abstraite (obligatoirement redefinie dans une classe fille non abstraite)
	abstract void repaint(Graphics2D g);
	
	// Deplacement du clip
	public void move(int x, int y){
		if(isDisposed) return;
		this.x = Math.max(0, Math.min(canvas.getWidth()-width, x));
		this.y = Math.max(0, Math.min(canvas.getHeight()-height, y));
		fireMoved();
		Invalidate();
	}
	
	// Invalidation du clip -> Repaint
	public void Invalidate(){
		if(isDisposed) return;
		if(canvas != null) canvas.redraw();
	}
	
	// Verification si un point touche la zone rectangulaire
	public boolean hitTest(int x, int y){
		if(isDisposed) return false;
		return (x >= zone.x && x<=zone.x+zone.width &&
				y >= zone.y && y<=zone.y+zone.height);
	}
	
	// Fonction statique, si un point touche une zone rectangulaire (mieux que Rectangle.contains() car prend en compte les largeurs/hauteurs negatives)
	public static boolean hitTest(Rectangle zone, Point pt){
		if(zone.width<0)
			zone.x -= zone.width *= -1;
		if(zone.height<0)
			zone.y -= zone.height *= -1;
		return (pt.x >= zone.x && pt.x<=zone.x+zone.width &&
				pt.y >= zone.y && pt.y<=zone.y+zone.height);
	}
	
	
	// Gestion de destruction de l'element
	protected boolean isDisposed = false;
	public boolean isDisposed(){ return isDisposed; }
	public void finalize(){ Dispose(); }
	public void Dispose(){
		if(isDisposed) return;
		isDisposed = true;
		fireDisposed();
		clipListeners.clear();
		i_base = null;
		canvas.removeClip(this);
		canvas = null;
	}
	
	
	// Ecouteur du clip, destruction ou deplacement du clip
	public interface ClipListener {
	    void disposed(Clip clip);
	    void moved(Clip clip);
	}
	
	private ArrayList<ClipListener> clipListeners = new ArrayList<ClipListener>();
	public void addClipListener(ClipListener listener) {
		if(isDisposed) return;
		clipListeners.add(listener);
    }    
    public void remClipListener(ClipListener listener) {
    	if(isDisposed) return;
    	clipListeners.remove(listener);
    }
    
    private void fireDisposed(){
    	for(ClipListener clipListener : clipListeners)
    		clipListener.disposed(this);    		
    }
    private void fireMoved(){
    	for(ClipListener clipListener : clipListeners)
    		clipListener.moved(this);    		
    }
    
}
