/*
 * OpenPixi - Open Particle-In-Cell (PIC) Simulator
 * Copyright (C) 2012  OpenPixi.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package org.openpixi.pixi.ui.panel;

import static java.awt.geom.AffineTransform.getRotateInstance;
import static java.awt.geom.AffineTransform.getTranslateInstance;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;

import javax.swing.JPanel;

import org.openpixi.pixi.physics.Simulation;
import org.openpixi.pixi.physics.particles.Particle;
import org.openpixi.pixi.ui.SimulationAnimation;
import org.openpixi.pixi.ui.SimulationAnimationListener;
import org.openpixi.pixi.ui.util.FrameRateDetector;


/**
 * Displays 2D particles.
 */
public class Particle2DPanel extends AnimationPanel {

	private boolean drawCurrentGrid = false;

	private boolean drawFields = false;

	public boolean showinfo = false;

	/** A state for the trace */
	public boolean paint_trace = false;

	private boolean reset_trace;

	Color darkGreen = new Color(0x00, 0x80, 0x00);

	/** Constructor */
	public Particle2DPanel(SimulationAnimation simulationAnimation) {
		super(simulationAnimation);
	}

	public void clear() {
		reset_trace = true;
	}

	public void checkTrace() {
		paint_trace =! paint_trace;
		//startAnimation();
	}

	public void drawCurrentGrid() {
		drawCurrentGrid =! drawCurrentGrid;
	}

	public void drawFields() {
		drawFields =! drawFields;
	}

	/** Display the particles */
	public void paintComponent(Graphics graph1) {
		Graphics2D graph = (Graphics2D) graph1;
		setBackground(Color.white);
		graph.translate(0, this.getHeight());
		graph.scale(1, -1);
		double scale = 10;

		if(!paint_trace)
		{
			super.paintComponent(graph1);
		}
		if(reset_trace)
		{
			super.paintComponent(graph1);
			reset_trace = false;
		}

		Simulation s = getSimulationAnimation().getSimulation();

		/** Scaling factor for the displayed panel in x-direction*/
		double sx = getWidth() / s.getWidth();
		/** Scaling factor for the displayed panel in y-direction*/
		double sy = getHeight() / s.getHeight();

		for (int i = 0; i < s.particles.size(); i++) {
			Particle par = (Particle) s.particles.get(i);
			graph.setColor(par.getColor());
			double radius = par.getRadius();
			int width = (int) (2*sx*radius);
			int height = (int) (2*sy*radius);
			if(width > 2 && height > 2 && !paint_trace) {
				graph.fillOval((int) (par.getX()*sx) - width/2, (int) (par.getY()*sy) - height/2,  width,  height);
			}
			else {
				graph.drawRect((int) (par.getX()*sx), (int) (par.getY()*sy), 0, 0);
			}
		}

		if(drawCurrentGrid)
		{
			graph.setColor(Color.black);
			for(int i = 0; i < s.grid.getNumCellsX(); i++)
				for(int k = 0; k < s.grid.getNumCellsY(); k++)
				{
					int xstart = (int) (s.grid.getCellWidth() * (i + 0.5) * sx);
					int xstart2 = (int)(s.grid.getCellWidth() * i * sx);
					int ystart = (int) (s.grid.getCellHeight() * (k + 0.5) * sy);
					int ystart2 = (int) (s.grid.getCellHeight() * k * sy);
					//drawArrow(graph, xstart, ystart, (int) Math.round(s.grid.getJx(i,k)*sx + xstart), (int) Math.round(s.grid.getJy(i,k)*sy + ystart));
                                        drawArrow(graph, xstart, ystart2, (int) Math.round(s.grid.getJx(i,k)*sx+xstart), ystart2, Color.BLACK);
                                        drawArrow(graph, xstart2, ystart, xstart2, (int) Math.round(s.grid.getJy(i,k)*sy+ystart),Color.BLACK);
				}
			//return;
		}

		if(drawFields)
		{
			graph.setColor(Color.black);
			for(int i = 0; i < s.grid.getNumCellsX(); i++)
				for(int k = 0; k < s.grid.getNumCellsY(); k++)
				{
					int xstart = (int) (s.grid.getCellWidth() * (i + 0.5) * sx);
                    int xstart2 = (int)(s.grid.getCellWidth() * i * sx);
                    int ystart = (int) (s.grid.getCellHeight() * (k + 0.5) * sy);
                    int ystart2 = (int) (s.grid.getCellHeight() * k * sy);
//drawArrow(graph, xstart, ystart, (int) Math.round(scale * s.grid.getEx(i,k)*sx + xstart), (int) Math.round(scale* s.grid.getEy(i,k)*sy + ystart));
                    drawArrow(graph, xstart, ystart2, (int) Math.round(scale*s.grid.getEx(i,k)*sx+xstart),ystart2, Color.BLACK);
                    drawArrow(graph, xstart2, ystart, xstart2, (int) Math.round(scale*s.grid.getEy(i,k)*sy+ystart), Color.GREEN);
                    drawArrow(graph, xstart, ystart, xstart, (int) Math.round(scale*s.grid.getBz(i,k)*sy+ystart), Color.RED);
				}
			//return;
		}

		FrameRateDetector frameratedetector = getSimulationAnimation().getFrameRateDetector();

		if (showinfo) {
			graph.translate(0.0, this.getHeight());
			graph.scale(1.0, -1.0);
			graph.setColor(darkGreen);
			graph.drawString("Frame rate: " + frameratedetector.getRateString() + " fps", 30, 30);
			graph.drawString("Time step: " + (float) s.tstep, 30, 50);
			graph.drawString("Total time: " + (float) s.tottime, 30, 70);

			Runtime runtime = Runtime.getRuntime();
			long maxMemory = runtime.maxMemory();
			long allocatedMemory = runtime.totalMemory();
			long freeMemory = runtime.freeMemory();

			int bottom = getHeight();
			graph.drawString("free memory: " + freeMemory / 1024, 30, bottom - 90);
			graph.drawString("allocated memory: " + allocatedMemory / 1024, 30, bottom - 70);
			graph.drawString("max memory: " + maxMemory /1024, 30, bottom - 50);
			graph.drawString("total free memory: " +
				(freeMemory + (maxMemory - allocatedMemory)) / 1024, 30, bottom - 30);
		}
	}


	private void drawArrow(Graphics2D g, int x1, int y1, int x2, int y2, Color col) {

		int ARR_SIZE = 5;

        double dx = x2 - x1, dy = y2 - y1;
        double angle = Math.atan2(dy, dx);
        int len = (int) Math.sqrt(dx*dx + dy*dy);
        // get the old transform matrix
        AffineTransform old = g.getTransform();
        AffineTransform at = getTranslateInstance(x1, y1);
        at.concatenate(getRotateInstance(angle));
        g.transform(at);
        //g.setTransform(at);

        // Draw horizontal arrow starting in (0, 0)
        Color colold = g.getColor();
        g.setColor(col);
        g.drawLine(0, 0, (int) len, 0);
        if(Math.abs(x2 - x1) > 0 || Math.abs(y2 - y1) > 0)
        	g.fillPolygon(new int[] {len, len-ARR_SIZE, len-ARR_SIZE, len},
        				  new int[] {0, -ARR_SIZE, ARR_SIZE, 0}, 4);
        g.setColor(colold);

        // reset transformationmatrix
        g.setTransform(old);
     }

}
