package lpv.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.Observable;
import java.util.Observer;

import javax.swing.JPanel;
import javax.vecmath.Vector4f;

import lpv.LPV;
import lpv.SH;

public class LPVVisualizer extends JPanel implements Observer {
	
	private static final int SH_RESOLUTION = 16;
	
	private LPV lpv;
	private ScaleManager scale;
	private LPV block;
	private StatusBar status;

	public LPVVisualizer(ScaleManager scale, final LPV lpv, LPV block) {
		this.scale = scale;
		this.lpv = lpv;
		this.block = block;
		this.status = null;
		addMouseMotionListener(new MouseMotionAdapter() {
			@Override
			public void mouseMoved(MouseEvent e) {
				if(status != null) {
					int w = getWidth() / lpv.getWidth();
					int h = getHeight() / lpv.getHeight();
					int x = e.getX() / w;
					int y = e.getY() / h;
					int z = lpv.getDepth() / 2;
					Vector4f v = lpv.get(x, y, z);
					status.setStatus(String.format("Level %2d. [%2d,%2d,%2d] = (%8.4f,%8.4f,%8.4f,%8.4f)", 
							lpv.getLevel(),
							x, y, z,
							v.x, v.y, v.z, v.w));
				}
			}
		});
		scale.addObserver(this);
	}

	@Override
	protected void paintComponent(Graphics graphics) {
		Graphics2D g = (Graphics2D)graphics;
		
		g.setColor(Color.black);
		g.fillRect(0, 0, getWidth(), getHeight());
		
		int w = getWidth() / lpv.getWidth();
		int h = getHeight() / lpv.getHeight();
		
		for(int i = 0; i < lpv.getHeight(); i++) {
			for(int j = 0; j < lpv.getWidth(); j++) {
				g.translate(w * j, h * i);
				Vector4f sh = lpv.get(j, lpv.getHeight() - i - 1, lpv.getDepth() / 2);
				Vector4f b = block.get(j, lpv.getHeight() - i - 1, lpv.getDepth() / 2);
				paintSH(sh, b, g, w, h);
				g.translate(-w * j, -h * i);
			}
		}
		
	}

	private void paintSH(Vector4f sh, Vector4f block, Graphics2D g2d, int w, int h) {
		g2d.setColor(Color.white);
		g2d.drawRect(0, 0, w-1, h-1);
		g2d.clipRect(0, 0, w, h);
		if(sh.lengthSquared() != 0.0f || block.lengthSquared() != 0.0f) {
			
			if(block.lengthSquared() != 0.0f) {
				g2d.translate(w / 2.0, h / 2.0);
				
				int[] polyx = new int[SH_RESOLUTION];
				int[] polyy = new int[SH_RESOLUTION];
				
				for(int i = 0; i < SH_RESOLUTION; i++) {
					double angle = i * 2.0*Math.PI / SH_RESOLUTION;
					float x = (float) Math.cos(angle);
					float y = (float) Math.sin(angle);
					float z = 0.0f;
					
					Vector4f dirSH = SH.construct(x, y, z);
					float r = Math.max(0.0f, block.dot(dirSH));
					
					x *= r * scale.getScale();
					y *= r * scale.getScale();
					
					polyx[i] = (int)x;
					polyy[i] = (int)-y;
				}
				
				g2d.setColor(Color.orange);
				g2d.fillPolygon(polyx, polyy, SH_RESOLUTION);
				g2d.translate(-w / 2.0, -h / 2.0);
			}
			
			if(sh.lengthSquared() != 0.0f) {
				g2d.translate(w / 2.0, h / 2.0);
				
				int[] polyx = new int[SH_RESOLUTION];
				int[] polyy = new int[SH_RESOLUTION];
				
				for(int i = 0; i < SH_RESOLUTION; i++) {
					double angle = i * 2.0*Math.PI / SH_RESOLUTION;
					float x = (float) Math.cos(angle);
					float y = (float) Math.sin(angle);
					float z = 0.0f;
					
					Vector4f dirSH = SH.construct(x, y, z);
					float r = Math.max(0.0f, sh.dot(dirSH));
					
					x *= r * scale.getScale();
					y *= r * scale.getScale();
					
					polyx[i] = (int)x;
					polyy[i] = (int)-y;
				}
				
				g2d.setColor(Color.white);
				g2d.fillPolygon(polyx, polyy, SH_RESOLUTION);
				
				g2d.translate(-w / 2.0, -h / 2.0);
			}
			
			{
				g2d.translate(w / 2.0, h / 2.0);
				g2d.setColor(Color.red);
				g2d.drawLine(-3, 0, 3, 0);
				g2d.drawLine(0, -3, 0, 3);
				g2d.translate(-w / 2.0, -h / 2.0);
			}
		}
		g2d.setClip(null);
	}

	@Override
	public void update(Observable o, Object arg) {
		repaint();
	}

	public Dimension getSHPanelSize() {
		Dimension d = new Dimension(getWidth() / lpv.getWidth(), getHeight() / lpv.getHeight());
		return d;
	}

	public void setStatusBar(StatusBar status) {
		this.status = status;
	}
}
