package industries.doublehelix.conditionalfilter.gui.filtersettings;

import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.Box;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.DocumentFilter;
import javax.swing.text.PlainDocument;

import industries.doublehelix.conditionalfilter.filtertypes.flat.NumberFilter;
import industries.doublehelix.conditionalfilter.filtertypes.flat.NumberFilter.NumberFilterMode;

public class NumberFilterSettingsPanel extends FilterSettingsPanel implements ActionListener, DocumentListener {
	private static final long serialVersionUID = -6614004532630323774L;
	
	JComboBox<NumberFilterMode> filterMode = new JComboBox<>();
	JTextField number = new JTextField();
	JTextField secondNumber = new JTextField();
	
	JLabel lblAnd = new JLabel("and");
	Component spacerForLabel = null;
	Component spacerForSecondNumber = Box.createHorizontalGlue();
	
	public NumberFilterSettingsPanel(NumberFilter filter, int preferredContentHeight) {
		super(filter, preferredContentHeight);
		
		filterMode.setModel(new DefaultComboBoxModel<>(NumberFilterMode.values()));
	//	filterMode.setMaximumSize(filterMode.getPreferredSize());
		filterMode.setSelectedItem(filter.getFilterMode());
		filterMode.addActionListener(this);
		
		PlainDocument doc = (PlainDocument) number.getDocument();
		doc.setDocumentFilter(new MyIntFilter());
		doc.addDocumentListener(this);
		doc = (PlainDocument) secondNumber.getDocument();
		doc.setDocumentFilter(new MyIntFilter());
		doc.addDocumentListener(this);

		number.setText(Integer.toString(filter.getFirstNumber()));
		// value of second number is set below via displaySecondNumber
		number.setHorizontalAlignment(SwingConstants.RIGHT);
		secondNumber.setHorizontalAlignment(SwingConstants.RIGHT);
		
		setPreferredHeightToComponents(number, secondNumber, filterMode);
		
		
		this.add(filterMode);
		this.add(Box.createHorizontalStrut(5));
		GridBagConstraints c = new GridBagConstraints();
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		this.add(number, c);
		this.add(Box.createHorizontalStrut(5));
		
		// build second label
		spacerForLabel = Box.createHorizontalStrut(lblAnd.getPreferredSize().width + 2);
		displaySecondNumber(filter);
		this.add(lblAnd);
		this.add(spacerForLabel);
		
		this.add(Box.createHorizontalStrut(5));
		this.add(secondNumber, c);
		this.add(spacerForSecondNumber, c);
		
		this.add(Box.createHorizontalStrut(5));
	}
	
	private void displaySecondNumber(NumberFilter filter) {
		boolean twoNumberFilter = filter.getFilterMode() == NumberFilterMode.BETWEEN
				|| filter.getFilterMode() == NumberFilterMode.NOTBETWEEN;
		
		if (twoNumberFilter && secondNumber.isVisible()) return;
		
		// changed from two numbers to one or vice versa
		spacerForLabel.setVisible(!twoNumberFilter);
		spacerForSecondNumber.setVisible(!twoNumberFilter);
		lblAnd.setVisible(twoNumberFilter);
		secondNumber.setVisible(twoNumberFilter);
		Integer secondNr = filter.getSecondNumber();
		if (secondNr != null) secondNumber.setText(secondNr.toString());
	}
	
	@Override
	public void actionPerformed(ActionEvent e) {
		Object source = e.getSource();
		if (source == filterMode) {
			NumberFilter castedFilter = (NumberFilter)this.filter;
			NumberFilterMode selectedFilterMode = (NumberFilterMode)filterMode.getSelectedItem();
			if (selectedFilterMode != castedFilter.getFilterMode()) {
				castedFilter.setFilterMode(selectedFilterMode);
				displaySecondNumber(castedFilter);
			}
		}
	}

	@Override
	public void insertUpdate(DocumentEvent e) {
		if (e.getDocument() == number.getDocument()) {
			((NumberFilter)this.filter).setFirstNumber(Integer.parseInt(number.getText()));
		} else if (e.getDocument() == secondNumber.getDocument()) {
			((NumberFilter)this.filter).setFirstNumber(Integer.parseInt(secondNumber.getText()));
		}
	}

	@Override
	public void removeUpdate(DocumentEvent e) {
		if (e.getDocument() == number.getDocument()) {
			((NumberFilter)this.filter).setFirstNumber(Integer.parseInt(number.getText()));
		} else if (e.getDocument() == secondNumber.getDocument()) {
			((NumberFilter)this.filter).setFirstNumber(Integer.parseInt(secondNumber.getText()));
		}
	}

	@Override
	public void changedUpdate(DocumentEvent e) {
	}

	/**
	 * From https://stackoverflow.com/questions/11093326/restricting-jtextfield-input-to-integers
	 * @author Hovercraft Full Of Eels
	 *
	 */
	class MyIntFilter extends DocumentFilter {
		@Override
		public void insertString(FilterBypass fb, int offset, String string,
				AttributeSet attr) throws BadLocationException {

			Document doc = fb.getDocument();
			StringBuilder sb = new StringBuilder();
			sb.append(doc.getText(0, doc.getLength()));
			sb.insert(offset, string);

			if (test(sb.toString())) {
				super.insertString(fb, offset, string, attr);
			} else {
				// warn the user and don't allow the insert
			}
		}

		private boolean test(String text) {
			try {
				Integer.parseInt(text);
				return true;
			} catch (NumberFormatException e) {
				return false;
			}
		}

		@Override
		public void replace(FilterBypass fb, int offset, int length, String text,
				AttributeSet attrs) throws BadLocationException {

			Document doc = fb.getDocument();
			StringBuilder sb = new StringBuilder();
			sb.append(doc.getText(0, doc.getLength()));
			sb.replace(offset, offset + length, text);

			if (test(sb.toString())) {
				super.replace(fb, offset, length, text, attrs);
			} else {
				// warn the user and don't allow the insert
			}

		}

		@Override
		public void remove(FilterBypass fb, int offset, int length)
				throws BadLocationException {
			Document doc = fb.getDocument();
			StringBuilder sb = new StringBuilder();
			sb.append(doc.getText(0, doc.getLength()));
			sb.delete(offset, offset + length);

			if (test(sb.toString())) {
				super.remove(fb, offset, length);
			} else {
				// warn the user and don't allow the insert
			}

		}
	}
}
