package sim.util;

import java.awt.*;
import java.util.Vector;

public class BufferedContainer extends Container
{
	protected OffscreenBuffer buffers;
	
	public BufferedContainer()
	{
		this.buffers = new OffscreenBuffer(this);
	}
	
	public void addNotify()
	{
		super.addNotify();
		
		buffers.update();
	}
	
	public void update(Graphics g)
	{
	}
	
	public void updateBuffers()
	{
		this.buffers.update();
	}
	
	public void paint(Graphics g)
	{
		Rectangle clip = g.getClipBounds();
		this.buffers.blitWorkplaceToScreen(clip);
	}
	
	public Graphics getBuffer()
	{
		return this.buffers.getWorkplaceGraphics();
	}
	
	public void paintComponent(Component comp)
	{		
		this.paintComponent(comp, true);
	}
	
	public void eraseComponent(Component comp)
	{
		this.eraseComponent(comp, true);
	}
	
	public void paintComponent(Component comp, boolean update)
	{		
		Graphics  wpg    = this.buffers.getWorkplaceGraphics(); 
		
		Rectangle bounds = comp.getBounds();
		Graphics  compGraphics;

		compGraphics = wpg.create(bounds.x, bounds.y, bounds.width, bounds.height);
		comp.paint(compGraphics);

		if(update)
			this.buffers.blitWorkplaceToScreen(bounds);

		wpg.dispose();
	}
	
	public Rectangle paintComponents(Vector components) // not updated
	{
		Graphics wpg = this.buffers.getWorkplaceGraphics(); 
		int size = components.size();
		
		Component comp = (Component)components.elementAt(0);
		Rectangle bounds = comp.getBounds();
		comp.setVisible(true);
		Graphics  compGraphics = wpg.create(bounds.x, bounds.y, bounds.width, bounds.height);
		comp.paint(compGraphics);
		compGraphics.dispose();
		
		Rectangle result = new Rectangle(bounds);
		
		for(int index = 1; index < size; index++)
		{
			comp = (Component)components.elementAt(index);
			bounds = comp.getBounds();
			comp.setVisible(true);
			compGraphics = wpg.create(bounds.x, bounds.y, bounds.width, bounds.height);
			comp.paint(compGraphics);
			compGraphics.dispose();
			
			result = result.union(bounds);
		}
		
		wpg.dispose();
		
		return result;
	}
	
	public void eraseComponent(Component comp, boolean update)
	{
		Rectangle bounds = comp.getBounds();
		
		this.paintOverlappingComponents(comp);

		if(update)
			this.buffers.blitWorkplaceToScreen(bounds);
	}
	
	public Rectangle eraseComponents(Vector components) // not updated
	{
		int size = components.size();
		Component comp = (Component)components.elementAt(0);
		comp.setVisible(false);
		Rectangle bounds = comp.getBounds();
		
		int index;
		
		for(index = 1; index < size; index++)
		{
			comp = (Component)components.elementAt(index);
			bounds = bounds.union(comp.getBounds());
			comp.setVisible(false);		// so comp won't be drawn below
		}
																					   
		Graphics wpg = this.buffers.getWorkplaceGraphics();
		
		wpg.setClip(bounds);	// set offscreen clip to bounds
		super.paint(wpg);		// draw all but comp
		wpg.dispose();
		
		return bounds;
	}
	
	protected boolean windowDamaged(Graphics g)
	{
		Rectangle clip = g.getClipBounds();
		Dimension size = this.getSize();

		return ((clip.x != 0) || (clip.y != 0) || (clip.width  < size.width) || (clip.height < size.height));
	}
	
	protected void paintOverlappingComponents(Component comp)
	{
		Graphics wpg = this.buffers.getWorkplaceGraphics();
		Rectangle bounds = comp.getBounds();

		wpg.setClip(bounds);	// set offscreen clip to bounds
		comp.setVisible(false);	// so comp won't be drawn below
		super.paint(wpg);		// draw all but comp
		comp.setVisible(true);	// reset comp visibility
		wpg.dispose();
	}
	
	protected class OffscreenBuffer
	{
		private BufferedContainer container;
		private Image workplaceBuffer;
		private Dimension offscreenSize;
		
		public OffscreenBuffer(BufferedContainer container)
		{
			this.container = container;
			this.workplaceBuffer = null;
			this.offscreenSize = null;
		}

		public Image getWorkplaceBuffer()
		{
			return this.workplaceBuffer;
		}
		
		public Graphics getWorkplaceGraphics()
		{
			return this.workplaceBuffer.getGraphics();
		}
		
		public void blitWorkplaceToScreen()
		{
			this.blitWorkplaceToScreen(null);
		}
		
		public void blitWorkplaceToScreen(Rectangle clip)
		{
			Graphics screenGraphics = this.container.getGraphics();

			if(screenGraphics != null)
			{
				if(clip != null)
					screenGraphics.setClip(clip);
				
				screenGraphics.drawImage(this.workplaceBuffer, 0, 0, this.container);
				screenGraphics.dispose();
			}
		}
		
		void update()
		{
			if(this.needNewOffscreenBuffer())
				this.createBuffers();
		}
		
 		private boolean needNewOffscreenBuffer()
		{
			Dimension size = this.container.getSize();
   	      	return ((this.workplaceBuffer == null) || (size.width != this.offscreenSize.width) || (size.height != this.offscreenSize.height));
   		}
		
   	 	private void createBuffers()
		{
			Dimension size = this.container.getSize();
			Image im = this.container.createImage(size.width, size.height);
			
			this.offscreenSize = size;
			
			if(this.workplaceBuffer != null)
				this.workplaceBuffer.flush();
			
			this.workplaceBuffer = im;
   	 	}
	}
}