package edu.calpoly.csc480.Entomo.Area;

import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;

import com.bcurry.www.swing.*;

import edu.calpoly.csc480.Entomo.Ant.*;
import edu.calpoly.csc480.Entomo.Seer.*;
import edu.calpoly.csc480.Entomo.Tile.*;

public class AreaView extends BasicPanel
{
	static final public Dimension defaultTileSize = new Dimension(10, 10);
	static final public int defaultLineSize = 2;

	public AreaView() {
		final int fontSize = 10;
		font = new Font("Serif", Font.BOLD, fontSize);

      addComponentListener(new ComponentAdapter() {
         public void componentMoved(ComponentEvent e)		{disposeGraphics();}         
         public void componentResized(ComponentEvent e)	{disposeGraphics();}
      });
		
		size = new Dimension();
		lines = new ArrayList();

		tileSize = defaultTileSize;

		lineColor = Color.gray;
		lineStroke = new BasicStroke((float)defaultLineSize);

		streamClosed();
	}
	
   public Dimension getMinimumSize()		{return getPreferredSize();}
   public Dimension getPreferredSize()		{return size;}
	
	public final Area getDocument()			{return area;}
	public final SeerView getOwner()			{return seerView;}

	public final Dimension getTileSize()	{return tileSize;}

	public void setDocument(Area area)	{
		this.area = area;
	}

	public void setOwner(SeerView seerView)  {
		this.seerView = seerView;
		this.seer = seerView.getDocument();
	}

	public void setTileSize(Dimension tileSize) {
		this.tileSize = tileSize;

		if (area != null && area.isOpen()) {
			inferSizes(tileSize, area.getDimension());
		}
	}

	public void update(Graphics draw) {
		if (tiles != null) {
			updateTiles(draw);
		}
	}

	public void paint(Graphics draw) {
		if (tiles != null)  {
			synchronized (seer) {
				paintTiles(draw);
			}

			if (paintLines) {
				paintLines(draw);
			}
		}
	}

	public void streamRead() {
		this.tiles = area.getTiles();
		this.dirty = area.getDirty();

		inferSizes(tileSize, area.getDimension());

		repaint();
	}

	public void streamClosed() {
		tiles = null;
		dirty = null;
		lines.clear();

		repaint();
	}
	
	public void update() {
		if (draw == null) {
			draw = (Graphics)getGraphics();
			draw.setFont(font);
		}

		updateTiles(draw);
	}

	protected Area area;
	protected Seer seer;
	protected SeerView seerView;

	protected Dimension size;
	protected Graphics draw;
	protected Font font;
	
  	protected Tile tiles[][];	
	protected Dimension tileSize;
	protected int lineSize;

	protected java.util.List dirty;

	protected boolean paintLines;
	protected java.util.List lines;
	protected Color lineColor;
	protected Stroke lineStroke;
	
	protected void finalize() throws Throwable {
		disposeGraphics();
	}

   synchronized protected void disposeGraphics() {
      if (draw != null) {
         draw.dispose();
         draw = null;
      }

		packWindow();
   }
	
	protected void inferSizes(Dimension tileSize, Dimension areaSize) {
/*		paintLines = (
		 (tileSize.width  > defaultLineSize * 2) &&
		 (tileSize.height > defaultLineSize * 2)
		);
*/		
		paintLines = false;
		lineSize = (paintLines ? defaultLineSize : 0);

		inferPreferredSize(tileSize, areaSize);
		inferAgentSizes(tileSize, areaSize);
		inferTileBounds(tileSize, areaSize);

		if (paintLines) {
			inferLineBounds(tileSize, areaSize);
		}
	}

	protected void inferPreferredSize(Dimension tileSize, Dimension areaSize) {
		size.width =
		 areaSize.width*tileSize.width +
		 areaSize.width*lineSize + lineSize;
		size.height =
		 areaSize.height*tileSize.height +
		 areaSize.height*lineSize + lineSize;
		
		setSize(size);
	}

	protected void inferAgentSizes(Dimension tileSize, Dimension areaSize) {
		ListIterator itr = area.getMeans().listIterator();

		while (itr.hasNext()) {
		   ((Means)itr.next()).getAnt().getView().setTileSize(tileSize);
		}
	}

	protected void inferTileBounds(Dimension tileSize, Dimension areaSize) {
		int row, col;
		int x, y, width, height;

		width  = tileSize.width;
		height = tileSize.height;
		
      for (
		 row = 0, y = lineSize;
		 row < areaSize.height;
		 row++, y += lineSize + height) {
	      for (
			 col = 0, x = lineSize;
			 col < areaSize.width;
			 col++, x += lineSize + width) {
				tiles[row][col].getView().setBounds(x, y, width, height);
			}
		}
	}
	
	protected void inferLineBounds(Dimension tileSize, Dimension areaSize) {
		int row, col;
		int x, y;
		int xBeg, yBeg, xEnd, yEnd;
		Line2D line;

		xBeg = yBeg = 0;
		xEnd = size.width - 1;
		yEnd = size.height - 1;

		lines.clear();
		
      for (
		 row = 0, y = 1;
		 row <= areaSize.height;
		 row++, y += lineSize + tileSize.height) {
			line = new Line2D.Float();
			line.setLine(xBeg, y, xEnd, y);
			lines.add(line);
		}

      for (
		 col = 0, x = 1;
		 col <= areaSize.width;
		 col++, x += lineSize + tileSize.width) {
			line = new Line2D.Float();
			line.setLine(x, yBeg, x, yEnd);
			lines.add(line);
		}
	}

	protected void paintTiles(Graphics draw) {
		int row, col;

		draw.setFont(font);

      for (row = 0; row < tiles.length; row++) {
	      for (col = 0; col < tiles[row].length; col++) {
				tiles[row][col].getView().paint(draw);
			}
      }
  	}

	protected void paintLines(Graphics drawBase) {
		Graphics2D draw;
      Color oldColor;
		Stroke oldStroke;
		Iterator itr;

		draw = (Graphics2D)drawBase;

		oldColor = draw.getColor();
		oldStroke = draw.getStroke();
		
      draw.setColor(lineColor);
		draw.setStroke(lineStroke);

		itr = lines.iterator();

		while (itr.hasNext()) {
			draw.draw((Shape)itr.next());
		}

		draw.setColor(oldColor);
		draw.setStroke(oldStroke);
  	}

  	protected void updateTiles(Graphics draw) {
  		int row, col;
		ListIterator itr = dirty.listIterator();
  	
  		while (itr.hasNext()) {
  		   ((Tile)itr.next()).getView().paint(draw);
		}
  	}
}