package net.filebot.util.ui;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.geom.GeneralPath;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.DefaultSingleSelectionModel;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.SingleSelectionModel;
import javax.swing.SwingConstants;

public class SelectButton<T> extends JButton {

	public static final String SELECTED_VALUE = "selected value";

	private final Color beginColor = new Color(0xF0EEE4);
	private final Color endColor = new Color(0xE0DED4);

	private final Color beginColorHover = beginColor;
	private final Color endColorHover = new Color(0xD8D7CD);

	private final SelectIcon selectIcon = new SelectIcon();

	private List<T> model = Collections.emptyList();
	private SingleSelectionModel selectionModel = new DefaultSingleSelectionModel();

	private LabelProvider<T> labelProvider = new NullLabelProvider<T>();

	private boolean hover = false;

	public SelectButton() {
		setContentAreaFilled(false);
		setFocusable(false);

		super.setIcon(selectIcon);

		setHorizontalAlignment(SwingConstants.CENTER);
		setVerticalAlignment(SwingConstants.CENTER);

		setBorder(BorderFactory.createLineBorder(new Color(0xA4A4A4), 1));
		setPreferredSize(new Dimension(32, 22));

		addActionListener(new OpenPopupOnClick());
	}

	public void setModel(Collection<T> model) {
		this.model = new ArrayList<T>(model);
		setSelectedIndex(0);
	}

	public LabelProvider<T> getLabelProvider() {
		return labelProvider;
	}

	public void setLabelProvider(LabelProvider<T> labelProvider) {
		this.labelProvider = labelProvider;

		// update icon
		this.setIcon(labelProvider.getIcon(getSelectedValue()));
	}

	@Override
	public void setIcon(Icon icon) {
		selectIcon.setInnerIcon(icon);
		repaint();
	}

	public void setSelectedValue(T value) {
		setSelectedIndex(model.indexOf(value));
	}

	public T getSelectedValue() {
		if (!selectionModel.isSelected())
			return null;

		return model.get(selectionModel.getSelectedIndex());
	}

	public void setSelectedIndex(int i) {
		if (i < 0 || i >= model.size()) {
			selectionModel.clearSelection();
			setIcon(null);
			return;
		}

		selectionModel.setSelectedIndex(i);
		T value = model.get(i);
		setIcon(labelProvider.getIcon(value));
		firePropertyChange(SELECTED_VALUE, null, value);
	}

	public int getSelectedIndex() {
		return selectionModel.getSelectedIndex();
	}

	public SingleSelectionModel getSelectionModel() {
		return selectionModel;
	}

	public void spinValue(int spin) {
		int size = model.size();

		spin = spin % size;

		int next = getSelectedIndex() + spin;

		if (next < 0)
			next += size;
		else if (next >= size)
			next -= size;

		setSelectedIndex(next);
	}

	@Override
	protected void paintComponent(Graphics g) {
		Graphics2D g2d = (Graphics2D) g;
		Rectangle bounds = new Rectangle(getSize());

		if (hover)
			g2d.setPaint(GradientStyle.TOP_TO_BOTTOM.getGradientPaint(bounds, beginColorHover, endColorHover));
		else
			g2d.setPaint(GradientStyle.TOP_TO_BOTTOM.getGradientPaint(bounds, beginColor, endColor));

		g2d.fill(bounds);

		super.paintComponent(g);
	}

	@Override
	protected void processMouseEvent(MouseEvent e) {
		switch (e.getID()) {
		case MouseEvent.MOUSE_ENTERED:
			hover = true;
			repaint();
			break;
		case MouseEvent.MOUSE_EXITED:
			hover = false;
			repaint();
			break;
		}

		super.processMouseEvent(e);
	}

	private class OpenPopupOnClick implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			JPopupMenu popup = new JPopupMenu();

			for (T value : model) {
				SelectPopupMenuItem item = new SelectPopupMenuItem(labelProvider.getText(value), labelProvider.getIcon(value), value);

				if (value == getSelectedValue())
					item.setSelected(true);

				popup.add(item);
			}

			popup.show(SelectButton.this, 0, getHeight() - 1);
		}
	}

	private class SelectPopupMenuItem extends JMenuItem implements ActionListener {

		private final T value;

		public SelectPopupMenuItem(String text, Icon icon, T value) {
			super(text, icon);

			this.value = value;

			setMargin(new Insets(3, 0, 3, 0));
			addActionListener(this);
		}

		@Override
		public void setSelected(boolean selected) {
			setFont(getFont().deriveFont(selected ? Font.BOLD : Font.PLAIN));
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			setSelectedValue(value);
		}

	}

	private static class SelectIcon implements Icon {

		private final GeneralPath arrow;

		private Icon icon;

		public SelectIcon() {
			arrow = new GeneralPath(Path2D.WIND_EVEN_ODD, 3);
			int x = 25;
			int y = 10;

			arrow.moveTo(x - 2, y);
			arrow.lineTo(x, y + 3);
			arrow.lineTo(x + 3, y);
			arrow.lineTo(x - 2, y);
		}

		public void setInnerIcon(Icon icon) {
			this.icon = icon;
		}

		@Override
		public void paintIcon(Component c, Graphics g, int x, int y) {
			Graphics2D g2d = (Graphics2D) g;

			if (icon != null) {
				icon.paintIcon(c, g2d, 4, 3);
			}

			g2d.setPaint(Color.BLACK);
			g2d.fill(arrow);
		}

		@Override
		public int getIconWidth() {
			return 30;
		}

		@Override
		public int getIconHeight() {
			return 20;
		}
	}

}
