package jp.ac.osaka_u.sanken.util;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import javax.swing.event.ListSelectionListener;

/**
 * 項目の追加・削除可能なリスト
 * @author mouse
 *
 */
public class EditableList extends JPanel {
	/**
	 *
	 */
	private static final long serialVersionUID = 8390493649442688888L;

	private EditableListList list;

	private JTextField textField;

	public EditableList(){
		this(new DefaultListModel());
	}

	/**
	 * モデルを指定してリスト生成
	 * @param model
	 */
	public EditableList(ListModel model){
		this.list = new EditableListList(model);
		this.list.setFixedCellHeight(26);
		initialize();
	}

	/**
	 * リストイベント追加委譲
	 * @param l
	 */
	public void addListSelectionListener(ListSelectionListener l){
		this.list.addListSelectionListener(l);
	}

	/**
	 * リストイベント削除委譲
	 * @param l
	 */
	public void removeListSelectionListener(ListSelectionListener l){
		this.list.removeListSelectionListener(l);
	}

	/**
	 * モデル取得委譲
	 * @return
	 */
	public ListModel getModel(){
		return list.getModel();
	}

	/**
	 * 項目追加用テキストフィールド取得
	 * @return
	 */
	public JTextField getTextField(){
		if (textField == null){
			textField = new JTextField();
			textField.addActionListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent e) {
					addCurrentItem();
				}
			});
		}
		return textField;
	}


	private void initialize(){
		this.setLayout(new BorderLayout());
		this.add(list, BorderLayout.CENTER);
		this.add(getTextField(), BorderLayout.SOUTH);
	}

	/**
	 * 適切なサイズを返す（TODO 現実装では横幅400固定）
	 */
	public Dimension getPreferredSize(){
		return new Dimension(400, (list.getModel().getSize()) * list.getFixedCellHeight() + getTextField().getPreferredSize().height);
	}

	private void addCurrentItem(){
		String word = getTextField().getText();

		if (word.trim().isEmpty()){
			return;
		}
		boolean exists = false;
		for (int i=0; i<list.getModel().getSize(); i++){
			Object obj = list.getModel().getElementAt(i);
			if (obj.toString().equals(word)){
				exists = true;
				break;
			}
		}
		if (!exists){
			((DefaultListModel)list.getModel()).addElement(new EditableListItem(word, true));
			getTextField().setText("");
		}
	}

	class EditableListList extends JList {
		/**
		 *
		 */
		private static final long serialVersionUID = -8207275088256642406L;
		private EditableCellRenderer renderer;

		public EditableListList() {
			super();

			this.putClientProperty("List.isFileList", Boolean.TRUE);
		}

		public EditableListList(ListModel model) {
			super(model);
			this.putClientProperty("List.isFileList", Boolean.TRUE);
		}


		public void updateUI() {
			setForeground(null);
			setBackground(null);
			setSelectionForeground(null);
			setSelectionBackground(null);
			if(renderer != null) {
				removeMouseListener(renderer);
				removeMouseMotionListener(renderer);
			}
			super.updateUI();
			renderer = new EditableCellRenderer();
			setCellRenderer(renderer);
			addMouseListener(renderer);
			addMouseMotionListener(renderer);
		}

		private boolean pointOutsidePrefSize(Point p) {
			int index = locationToIndex(p);
			DefaultListModel m = (DefaultListModel)getModel();
			if (index < 0){
				return false;
			}
			Object n = m.get(index);
			Component c = getCellRenderer().getListCellRendererComponent(
					this, n, index, false, false);
			c.doLayout();
			Dimension d = c.getPreferredSize();
			Rectangle rect = getCellBounds(index, index);
			rect.width = d.width;
			return index < 0 || !rect.contains(p);
		}
		@Override
		protected void processMouseEvent(MouseEvent e) {
			if(!pointOutsidePrefSize(e.getPoint())) {
				super.processMouseEvent(e);
			}
		}
		@Override
		protected void processMouseMotionEvent(MouseEvent e) {
			if(!pointOutsidePrefSize(e.getPoint())) {
				super.processMouseMotionEvent(e);
			}else{
				e = new MouseEvent(
						(Component)e.getSource(), MouseEvent.MOUSE_EXITED, e.getWhen(),
						e.getModifiers(), e.getX(), e.getY(),
						e.getXOnScreen(), e.getYOnScreen(),
						e.getClickCount(), e.isPopupTrigger(), MouseEvent.NOBUTTON);
				super.processMouseEvent(e);
			}
		}
	}

	class EditableCellRenderer extends JPanel implements ListCellRenderer, MouseListener, MouseMotionListener {
		/**
		 *
		 */
		private static final long serialVersionUID = 5134902478737266926L;

		private final JRadioButton button;

		public EditableCellRenderer(){
			button = new JRadioButton(new ImageIcon(getClass().getResource("x.png")));
			button.setSelectedIcon(new ImageIcon(getClass().getResource("blank.png")));
		}

		@Override
		public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
			Component ret;

			if(value instanceof EditableListItem) {
				button.setSelected(!((EditableListItem)value).deleteable);
			} else {
				button.setSelected(false);
			}

			button.setBackground(list.getBackground());
			button.setForeground(list.getForeground());
			button.setText(value.toString());
			ret = button;

			return ret;
		}


		@Override
		public void mouseExited(MouseEvent e) {
		}

		@Override
		public void mouseClicked(MouseEvent e) {
			if(e.getButton()==MouseEvent.BUTTON1) {
				JList l = (JList)e.getComponent();
				DefaultListModel m = (DefaultListModel)l.getModel();
				Point p = e.getPoint();
				int index  = l.locationToIndex(p);
				if(index>=0) {
					Object value = m.get(index);
					if(value instanceof EditableListItem) {
						if (!((EditableListItem)value).deleteable){
							return;
						}
					}
					((DefaultListModel)l.getModel()).remove(index);
					Rectangle rect = l.getCellBounds(index, index);
					if (rect != null){
						l.repaint(rect);
					}
				}
			}
		}

		@Override
		public void mouseMoved(MouseEvent e) {
		}



		@Override
		public void mouseEntered(MouseEvent e) {}

		@Override
		public void mousePressed(MouseEvent e) {}

		@Override
		public void mouseReleased(MouseEvent e) {}

		@Override
		public void mouseDragged(MouseEvent e) {}
	}
}
