package catdata.ide;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableRowSorter;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;

import catdata.Pair;
import catdata.Util;

public class GuiUtil {

	public static void show(JComponent p, int w, int h, String title) {
		JFrame f = new JFrame(title);
		f.setContentPane(p);
		f.pack();
		if (w > 0 && h > 0) {
			f.setSize(new Dimension(w, h));
		}
		f.setLocationRelativeTo(null);
		f.setVisible(true);
	}

	public static class MissingIcon implements Icon {

		private int width;
		private int height;
		private Color color;

		// private BasicStroke stroke = new BasicStroke(4);

		public MissingIcon(Color c, int w, int h) {
			this.color = c;
			this.width = w;
			this.height = h;
		}

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

			g2d.setColor(color);

			g2d.fillRect(x, y, width, height);

			g2d.dispose();
		}

		public int getIconWidth() {
			return width;
		}

		public int getIconHeight() {
			return height;
		}
	}

	public static JComponent makeGrid(List<JComponent> list) {

		JPanel ret = new JPanel();
		ret.setLayout(new BoxLayout(ret, BoxLayout.PAGE_AXIS));
		// int i = 0;
		for (JComponent x : list) {
			x.setAlignmentX(Component.LEFT_ALIGNMENT);
			x.setMinimumSize(x.getPreferredSize());
			ret.add(x);
			// ret.add(Box.)
			// ret.add(Box.createHorizontalGlue());
		}
//		ret.add(new JLabel(""), Box.cre);

		JPanel p = new JPanel(new GridLayout(1, 1));

		p.add(ret);

		JScrollPane jsp = new JScrollPane(p);// , JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
												// JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

		ret.setBorder(BorderFactory.createEmptyBorder());
		p.setBorder(BorderFactory.createEmptyBorder());
		// jsp.setBorder(BorderFactory.createEmptyBorder());

		return jsp;
	}

	public static JPanel makeGrid2(List<JComponent> list) {
		JPanel ret = new JPanel(new GridBagLayout());
		int i = 0;
		for (JComponent x : list) {
			// JScrollPane jsp = new JScrollPane(x);
			// JPanel p = new JPanel(new GridLayout(1, 1));
			// p.add(jsp);
			GridBagConstraints c = new GridBagConstraints();
			c.gridx = 0;
			c.weightx = 1;
			// c.gridwidth = 1;
			// c.weighty = 1;
			c.gridy = i++;
			c.anchor = GridBagConstraints.NORTHWEST;
			// c.fill = c.HORIZONTAL;
			ret.add(x, c);
		}
		GridBagConstraints c = new GridBagConstraints();
		c.gridx = 0;
		c.weighty = 1;
		// c.gridwidth = 1;
		// c.weighty = 1;
		c.gridy = i++;
		c.anchor = GridBagConstraints.NORTHWEST;
		c.fill = GridBagConstraints.VERTICAL;
		ret.add(new JLabel(""), c);

		JScrollPane jsp = new JScrollPane(ret); // , JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
												// JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		JPanel p = new JPanel(new GridLayout(1, 1));

		p.add(jsp);
		return p;
	}

	@SuppressWarnings("serial")
	public static JPanel makeTable(Border b, String border, Object[][] rowData, Object... colNames) {
		JTable t = new JTable(rowData, colNames) {
			@Override
			public Dimension getPreferredScrollableViewportSize() {
				Dimension d = getPreferredSize();
				return new Dimension(Integer.max(640, d.width), (d.height));
			}
		};
		JPanel p = new JPanel(new GridLayout(1, 1));
		TableRowSorter<?> sorter = new MyTableRowSorter(t.getModel());
		if (colNames.length > 0) {
			sorter.toggleSortOrder(0);
		}
		t.setRowSorter(sorter);
		sorter.allRowsChanged();
		p.add(new JScrollPane(t));

		for (int row = 0; row < t.getRowCount(); row++) {
			int rowHeight = t.getRowHeight();

			for (int column = 0; column < t.getColumnCount(); column++) {
				Component comp = t.prepareRenderer(t.getCellRenderer(row, column), row, column);
				rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
			}

			t.setRowHeight(row, rowHeight);
		}

		Font font = UIManager.getFont("TableHeader.font");
		p.setBorder(BorderFactory.createTitledBorder(b, border, TitledBorder.DEFAULT_JUSTIFICATION,
				TitledBorder.DEFAULT_POSITION, font, Color.black));
		return p;

	}

	@SuppressWarnings("serial")
	// TODO aql merge with other makeTable method
	public static JComponent makeBoldHeaderTable(Set<Pair<Integer, Integer>> nulls, Collection<String> atts, Border b,
			String border, Object[][] rowData, String... colNames) {
		JTable t = new JTable(rowData, colNames) {
			@Override
			public Dimension getPreferredScrollableViewportSize() {
				Dimension d = getPreferredSize();
				int m = this.getVisibleRect().width;
				int n = Integer.max(d.height, this.getVisibleRect().height);
				return new Dimension(m, n);
			}

			@Override
			public TableCellRenderer getCellRenderer(int row, int col) {
				TableCellRenderer l = super.getCellRenderer(row, col);

				if (col > 0 && col < atts.size() + 1) {
					return new DefaultTableCellRenderer() {
						@Override
						public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
								boolean hasFocus, int row, int col) {
							JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
									row, col);

							l.setFont(l.getFont().deriveFont(Font.BOLD));
							int row0 = convertRowIndexToModel(row);
							int col0 = convertColumnIndexToModel(col);
							if (nulls != null && nulls.contains(new Pair<>(row0, col0))) {
								l.setForeground(Color.RED);
							}
							return l;
						}

					};
				}
				return l;
			}
		};

		JPanel tt = new JPanel(new GridLayout(1, 1));
		tt.add(new PDControlScrollPane(t));
		 TableRowSorter<?> sorter = new MyTableRowSorter(t.getModel());
		 if (colNames.length > 0) {
		 sorter.toggleSortOrder(0);
		 }
		 t.setRowSorter(sorter);

		 sorter.allRowsChanged();

		for (int row = 0; row < t.getRowCount(); row++) {
			int rowHeight = t.getRowHeight();

			for (int column = 0; column < t.getColumnCount(); column++) {
				Component comp = t.prepareRenderer(t.getCellRenderer(row, column), row, column);
				rowHeight = Math.max(rowHeight, comp.getPreferredSize().height);
			}

			t.setRowHeight(row, rowHeight);
		}

		tt.setBorder(BorderFactory.createTitledBorder(b, border));
		// for (int i = 0; i < t.getColumnModel().getColumnCount(); i++) {
		// TableColumn col = t.getColumnModel().getColumn(i);

		// col.setHeaderRenderer(new BoldifyingColumnHeaderRenderer(atts,
		// t.getTableHeader().getDefaultRenderer()));
		// }

		Font font = UIManager.getFont("Label.font");

		tt.setBorder(BorderFactory.createTitledBorder(b, border, TitledBorder.DEFAULT_JUSTIFICATION,
				TitledBorder.DEFAULT_POSITION, font, Color.black));

		return tt;
	}

	public static void centerLineInScrollPane(JTextComponent component) {
		Container container = SwingUtilities.getAncestorOfClass(JViewport.class, component);

		if (container == null)
			return;

		try {
			Rectangle r = component.modelToView(component.getCaretPosition());
			// Rectangle2D r = component.modelToView2D(component.getCaretPosition());
			JViewport viewport = (JViewport) container;
			if (r == null) { // || viewport == null) {
				return;
			}
			int extentHeight = viewport.getExtentSize().height;
			int viewHeight = viewport.getViewSize().height;

			int y = Math.max(0, r.y - ((extentHeight - r.height) / 2));
			// double y = Math.max(0, r.getY() - ((extentHeight - r.getHeight()) / 2));
			y = Math.min(y, viewHeight - extentHeight);

			viewport.setViewPosition(new Point(0, y));
			// viewport.setViewPosition(new Point(0, (int) y));
		} catch (BadLocationException ble) {
		}
	}

	public static String readFile(InputStream file) {
		Util.assertNotNull(file);
		try (InputStreamReader r = new InputStreamReader(file)) {

			return Util.readFile(r);
		} catch (IOException e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(null, "Could not read from " + file);
		}
		return null;
	}

	public static String readFile(String file) {
		try (FileReader r = new FileReader(file)) {
			return Util.readFile(r);
		} catch (IOException e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(null, "Could not read from " + file);
		}
		return null;
	}

	public static String readFile(File file) {
		try (FileReader r = new FileReader(file)) {
			return Util.readFile(r);
		} catch (IOException e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(null, "Could not read from " + file);
		}
		return null;
	}

	public static JList<String> makeList() {
		JList<String> list = new JList<>() {
			private static final long serialVersionUID = 1L;

			@Override
			public int locationToIndex(Point location) {
				int index = super.locationToIndex(location);
				if (index != -1 && !getCellBounds(index, index).contains(location)) {
					return -1;
				}
				return index;

			}
		};
		list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		return list;
	}

	static class PDControlScrollPane extends JScrollPane {

		private static final long serialVersionUID = 1L;

		public PDControlScrollPane(JComponent o) {
			super(o);
			addMouseWheelListener(new PDMouseWheelListener());
		}

		class PDMouseWheelListener implements MouseWheelListener {

			private JScrollBar bar;
			private int previousValue = 0;
			private JScrollPane parentScrollPane;

			private JScrollPane getParentScrollPane() {
				if (parentScrollPane == null) {
					Component parent = getParent();
					while (!(parent instanceof JScrollPane) && parent != null) {
						parent = parent.getParent();
					}
					parentScrollPane = (JScrollPane) parent;
				}
				return parentScrollPane;
			}

			public PDMouseWheelListener() {
				bar = PDControlScrollPane.this.getVerticalScrollBar();
			}

			public void mouseWheelMoved(MouseWheelEvent e) {
				JScrollPane parent = getParentScrollPane();
				if (parent != null) {
					/*
					 * Only dispatch if we have reached top/bottom on previous scroll
					 */
					if (e.getWheelRotation() < 0) {
						if (bar.getValue() == 0 && previousValue == 0) {
							parent.dispatchEvent(cloneEvent(e));
						}
					} else {
						if (bar.getValue() == getMax() && previousValue == getMax()) {
							parent.dispatchEvent(cloneEvent(e));
						}
					}
					previousValue = bar.getValue();
				}
				/*
				 * If parent scrollpane doesn't exist, remove this as a listener. We have to
				 * defer this till now (vs doing it in constructor) because in the constructor
				 * this item has no parent yet.
				 */
				else {
					PDControlScrollPane.this.removeMouseWheelListener(this);
				}
			}

			private int getMax() {
				return bar.getMaximum() - bar.getVisibleAmount();
			}

			private MouseWheelEvent cloneEvent(MouseWheelEvent e) {
				return new MouseWheelEvent(getParentScrollPane(), e.getID(), e.getWhen(), e.getModifiers(), 1, 1,
						e.getClickCount(), false, e.getScrollType(), e.getScrollAmount(), e.getWheelRotation());
			}
		}
	}

}