
package com.badlogic.gdx.tools.flame;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;

import com.badlogic.gdx.graphics.g3d.particles.ParticleController;
import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsInfluencer;
import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier;
import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier.BrownianAcceleration;
import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier.CentripetalAcceleration;
import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier.FaceDirection;
import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier.PolarAcceleration;
import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier.Rotational3D;
import com.badlogic.gdx.graphics.g3d.particles.influencers.DynamicsModifier.TangentialAcceleration;
import com.badlogic.gdx.tools.flame.FlameMain.ControllerType;
import com.badlogic.gdx.utils.Array;

/** @author Inferno */
public class DynamicsInfluencerPanel extends InfluencerPanel<DynamicsInfluencer> {

	private static final String VEL_TYPE_ROTATIONAL_2D = "Angular Velocity 2D", VEL_TYPE_ROTATIONAL_3D = "Angular Velocity 3D",
		VEL_TYPE_CENTRIPETAL = "Centripetal", VEL_TYPE_TANGENTIAL = "Tangential", VEL_TYPE_POLAR = "Polar",
		VEL_TYPE_BROWNIAN = "Brownian", VEL_TYPE_FACE = "Face";

	protected class VelocityWrapper {
		public DynamicsModifier velocityValue;
		public boolean isActive;

		public VelocityWrapper (DynamicsModifier value, boolean isActive) {
			this.velocityValue = value;
			this.isActive = isActive;
		}
	}

	JComboBox velocityBox;
	JTable velocityTable;
	DefaultTableModel velocityTableModel;
	JPanel selectedVelocityPanel;
	AngularVelocityPanel angularVelocityPanel;
	StrengthVelocityPanel strengthVelocityPanel;
	ParticleValuePanel emptyPanel;
	Array<VelocityWrapper> velocities;

	public DynamicsInfluencerPanel (FlameMain editor, DynamicsInfluencer influencer) {
		super(editor, influencer, "Dynamics Influencer", "Defines how the particles dynamics (acceleration, angular velocity).");
		velocities = new Array<VelocityWrapper>();
		setValue(value);
		set(influencer);
	}

	private void set (DynamicsInfluencer influencer) {
		// Clear
		for (int i = velocityTableModel.getRowCount() - 1; i >= 0; i--) {
			velocityTableModel.removeRow(i);
		}
		velocities.clear();

		// Add
		for (int i = 0, c = influencer.velocities.size; i < c; ++i) {
			velocities.add(new VelocityWrapper((DynamicsModifier)influencer.velocities.items[i], true));
			velocityTableModel.addRow(new Object[] {"Velocity " + i, true});
		}

		DefaultComboBoxModel model = (DefaultComboBoxModel)velocityBox.getModel();
		model.removeAllElements();
		for (Object velocityObject : getAvailableVelocities(editor.getControllerType())) {
			model.addElement(velocityObject);
		}
	}

	private Object[] getAvailableVelocities (ControllerType type) {
		if (type == ControllerType.Billboard || type == ControllerType.PointSprite) {
			return new String[] {VEL_TYPE_ROTATIONAL_2D, VEL_TYPE_CENTRIPETAL, VEL_TYPE_TANGENTIAL, VEL_TYPE_POLAR,
				VEL_TYPE_BROWNIAN};
		} else if (type == ControllerType.ModelInstance || type == ControllerType.ParticleController) {
			return new String[] {VEL_TYPE_ROTATIONAL_3D, VEL_TYPE_CENTRIPETAL, VEL_TYPE_TANGENTIAL, VEL_TYPE_POLAR,
				VEL_TYPE_BROWNIAN, VEL_TYPE_FACE};
		}
		return null;
	}

	protected void initializeComponents () {
		super.initializeComponents();
		JPanel velocitiesPanel = new JPanel();
		velocitiesPanel.setLayout(new GridBagLayout());
		{
			JPanel sideButtons = new JPanel(new GridBagLayout());
			velocitiesPanel.add(sideButtons, new GridBagConstraints(1, 0, 1, 1, 1, 1, GridBagConstraints.NORTHWEST,
				GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
			{
				sideButtons.add(velocityBox = new JComboBox(new DefaultComboBoxModel()), new GridBagConstraints(0, -1, 1, 1, 0, 0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0));
			}
			{
				JButton newButton = new JButton("New");
				sideButtons.add(newButton, new GridBagConstraints(0, -1, 1, 1, 0, 0, GridBagConstraints.CENTER,
					GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0));
				newButton.addActionListener(new ActionListener() {
					public void actionPerformed (ActionEvent event) {
						createVelocity(velocityBox.getSelectedItem());
					}
				});
			}
			{
				JButton deleteButton = new JButton("Delete");
				sideButtons.add(deleteButton, new GridBagConstraints(0, -1, 1, 1, 0, 0, GridBagConstraints.CENTER,
					GridBagConstraints.HORIZONTAL, new Insets(0, 0, 6, 0), 0, 0));
				deleteButton.addActionListener(new ActionListener() {
					public void actionPerformed (ActionEvent event) {
						deleteVelocity();
					}
				});
			}
		}
		JScrollPane scroll = new JScrollPane();
		velocitiesPanel.add(scroll, new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE,
			new Insets(0, 0, 0, 6), 0, 0));
		velocityTable = new JTable() {
			public Class getColumnClass (int column) {
				return column == 1 ? Boolean.class : super.getColumnClass(column);
			}

			@Override
			public Dimension getPreferredScrollableViewportSize () {
				Dimension dim = super.getPreferredScrollableViewportSize();
				dim.height = getPreferredSize().height;
				return dim;
			}
		};
		velocityTable.getTableHeader().setReorderingAllowed(false);
		velocityTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		scroll.setViewportView(velocityTable);
		velocityTableModel = new DefaultTableModel(new String[0][0], new String[] {"Velocity", "Active"});
		velocityTable.setModel(velocityTableModel);
		velocityTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged (ListSelectionEvent event) {
				if (event.getValueIsAdjusting()) return;
				velocitySelected();
			}
		});
		velocityTableModel.addTableModelListener(new TableModelListener() {
			public void tableChanged (TableModelEvent event) {
				if (event.getColumn() != 1) return;
				velocityChecked(event.getFirstRow(), (Boolean)velocityTable.getValueAt(event.getFirstRow(), 1));
			}
		});

		// Velocity values
		emptyPanel = new ParticleValuePanel(editor, "", "", true, false);
		strengthVelocityPanel = new StrengthVelocityPanel(editor, null, "Life", "", "");
		angularVelocityPanel = new AngularVelocityPanel(editor, null, "Life", "", "");
		strengthVelocityPanel.setVisible(false);
		angularVelocityPanel.setVisible(false);
		emptyPanel.setVisible(false);
		strengthVelocityPanel.setIsAlwayShown(true);
		angularVelocityPanel.setIsAlwayShown(true);
		emptyPanel.setIsAlwayShown(true);
		emptyPanel.setValue(null);

		// Assemble
		int i = 0;
		addContent(i++, 0, velocitiesPanel);
		addContent(i++, 0, strengthVelocityPanel);
		addContent(i++, 0, angularVelocityPanel);
		addContent(i++, 0, emptyPanel);
	}

	protected void velocityChecked (int index, boolean isChecked) {
		ParticleController controller = editor.getEmitter();
		DynamicsInfluencer influencer = (DynamicsInfluencer)controller.findInfluencer(DynamicsInfluencer.class);
		influencer.velocities.clear();
		velocities.get(index).isActive = isChecked;
		for (VelocityWrapper wrapper : velocities) {
			if (wrapper.isActive) influencer.velocities.add(wrapper.velocityValue);
		}
		// Restart the effect and reinit the controller
		editor.restart();
	}

	protected void velocitySelected () {
		// Show the velocity value panel
		int index = velocityTable.getSelectedRow();
		if (index == -1) return;

		DynamicsModifier velocityValue = velocities.get(index).velocityValue;
		EditorPanel velocityPanel = getVelocityPanel(velocityValue);

		// Show the selected velocity
		if (selectedVelocityPanel != null && selectedVelocityPanel != velocityPanel) selectedVelocityPanel.setVisible(false);
		velocityPanel.setVisible(true);
		velocityPanel.showContent(true);
		selectedVelocityPanel = velocityPanel;
	}

	private EditorPanel getVelocityPanel (DynamicsModifier velocityValue) {
		EditorPanel panel = null;
		// Billboards
		if (velocityValue instanceof DynamicsModifier.Rotational2D) {
			strengthVelocityPanel.setValue((DynamicsModifier.Strength)velocityValue);
			strengthVelocityPanel.setName("Angular Velocity");
			strengthVelocityPanel.setDescription("The angular speed around the billboard facing direction, in degrees/sec .");
			panel = strengthVelocityPanel;
		} else if (velocityValue instanceof CentripetalAcceleration) {
			strengthVelocityPanel.setValue((DynamicsModifier.CentripetalAcceleration)velocityValue);
			strengthVelocityPanel.setName("Centripetal Acceleration");
			strengthVelocityPanel.setDescription(
				"A directional acceleration, the direction is towards the origin (global), or towards the emitter position (local), in world units/sec2 .");
			panel = strengthVelocityPanel;
		} else if (velocityValue instanceof TangentialAcceleration) {
			angularVelocityPanel.setValue((DynamicsModifier.Angular)velocityValue);
			angularVelocityPanel.setName("Tangetial Velocity");
			angularVelocityPanel.setDescription(
				"A directional acceleration (axis and magnitude), the final direction is the cross product between particle position and the axis, in world units/sec2 .");
			panel = angularVelocityPanel;
		} else if (velocityValue instanceof PolarAcceleration) {
			angularVelocityPanel.setValue((DynamicsModifier.Angular)velocityValue);
			angularVelocityPanel.setName("Polar Velocity");
			angularVelocityPanel.setDescription("A directional acceleration (axis and magnitude), in world units/sec2 .");
			panel = angularVelocityPanel;
		} else if (velocityValue instanceof BrownianAcceleration) {
			strengthVelocityPanel.setValue((DynamicsModifier.Strength)velocityValue);
			strengthVelocityPanel.setName("Brownian Velocity");
			strengthVelocityPanel
				.setDescription("A directional acceleration which has random direction at each update, in world units/sec2.");
			panel = strengthVelocityPanel;
		} else if (velocityValue instanceof Rotational3D) {
			angularVelocityPanel.setValue((DynamicsModifier.Angular)velocityValue);
			angularVelocityPanel.setName("Angular Velocity");
			angularVelocityPanel.setDescription("An angular velocity (axis and magnitude), in degree/sec2.");
			panel = angularVelocityPanel;
		} else if (velocityValue instanceof FaceDirection) {
			emptyPanel.setName("Face");
			emptyPanel.setDescription(
				"Rotates the model to face its current velocity (Do not add any other angular velocity when using this).");
			panel = emptyPanel;
		}

		return panel;
	}

	private DynamicsModifier createVelocityValue (Object selectedItem) {
		DynamicsModifier velocityValue = null;
		if (selectedItem == VEL_TYPE_ROTATIONAL_2D)
			velocityValue = new DynamicsModifier.Rotational2D();
		else if (selectedItem == VEL_TYPE_ROTATIONAL_3D)
			velocityValue = new DynamicsModifier.Rotational3D();
		else if (selectedItem == VEL_TYPE_CENTRIPETAL)
			velocityValue = new DynamicsModifier.CentripetalAcceleration();
		else if (selectedItem == VEL_TYPE_TANGENTIAL)
			velocityValue = new DynamicsModifier.TangentialAcceleration();
		else if (selectedItem == VEL_TYPE_POLAR)
			velocityValue = new DynamicsModifier.PolarAcceleration();
		else if (selectedItem == VEL_TYPE_BROWNIAN)
			velocityValue = new DynamicsModifier.BrownianAcceleration();
		else if (selectedItem == VEL_TYPE_FACE) velocityValue = new DynamicsModifier.FaceDirection();
		return velocityValue;
	}

	protected void deleteVelocity () {
		int row = velocityTable.getSelectedRow();
		if (row == -1) return;

		// Remove the velocity from the table
		ParticleController controller = editor.getEmitter();
		DynamicsInfluencer influencer = (DynamicsInfluencer)controller.findInfluencer(DynamicsInfluencer.class);
		influencer.velocities.removeValue(velocities.removeIndex(row).velocityValue, true);
		velocityTableModel.removeRow(row);

		// Restart the effect and reinit the controller
		editor.restart();

		selectedVelocityPanel.setVisible(false);
		selectedVelocityPanel = null;
	}

	protected void createVelocity (Object selectedItem) {
		// Add the velocity to the table and to the influencer
		ParticleController controller = editor.getEmitter();
		DynamicsInfluencer influencer = (DynamicsInfluencer)controller.findInfluencer(DynamicsInfluencer.class);
		VelocityWrapper wrapper = new VelocityWrapper(createVelocityValue(selectedItem), true);
		velocities.add(wrapper);
		influencer.velocities.add(wrapper.velocityValue);
		int index = velocities.size - 1;
		velocityTableModel.addRow(new Object[] {"Velocity " + index, true});

		// Reinit
		editor.restart();

		// Select new velocity
		velocityTable.getSelectionModel().setSelectionInterval(index, index);
		revalidate();
		repaint();
	}

}
