import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SwingPresenter {
	private Main main;
	private JPanel mainPanel;
	private Map<String, Component> components = new HashMap<>();
    private String curScreenId;
	private boolean hasSaved = true;

	public SwingPresenter(Main main, JPanel mainPanel) {
		this.main = main;
		this.mainPanel = mainPanel;
	}

    public JPanel getMainPanel() {
        return mainPanel;
    }

	public void screenUpdate(Map<String, Object> prevValue, Map<String, Object> value) {
		Map<String, Object> widgets = (Map<String, Object>) value.get("widgets");
		boolean layout = (boolean) value.get("layout");
		String prevScreenId = (String) prevValue.get("screenId");
		curScreenId = (String) value.get("screenId");
		if (curScreenId.equals(prevScreenId)) {
			return;
		}
		mainPanel.removeAll();
		if (layout) {
			mainPanel.setLayout(new FlowLayout());
			for (String key : widgets.keySet()) {
				String type = (String) ((Map<String, Object>) widgets.get(key)).get("type");
				String text = (String) ((Map<String, Object>) widgets.get(key)).get("text");
				boolean visible = (boolean) ((Map<String, Object>) widgets.get(key)).get("visible");
				int state = (int) ((Map<String, Object>) widgets.get(key)).get("state");
				if (type.equals("textInput")) {
					JTextField textField = new JTextField(text,10);
					mainPanel.add(textField);
					components.put(key, textField);
					textField.getDocument().addDocumentListener(new DocumentListener() {
						@Override
						public void insertUpdate(DocumentEvent e) {
							Document d = e.getDocument();
							try {
								String text = d.getText(0, d.getLength());
								main.textEvent(key, text);
							} catch (BadLocationException ex) {
								throw new RuntimeException(ex);
							}
						}
						@Override
						public void removeUpdate(DocumentEvent e) {
							Document d = e.getDocument();
							try {
								String text = d.getText(0, d.getLength());
								main.textEvent(key, text);
							} catch (BadLocationException ex) {
								throw new RuntimeException(ex);
							}
						}
						@Override
						public void changedUpdate(DocumentEvent e) {
							Document d = e.getDocument();
							try {
								String text = d.getText(0, d.getLength());
								main.textEvent(key, text);
							} catch (BadLocationException ex) {
								throw new RuntimeException(ex);
							}
						}
					});
				} else if (type.equals("label")) {
					JLabel label = new JLabel(text);
					mainPanel.add(label);
					components.put(key, label);
					label.addMouseListener(new MouseListener() {
						@Override
						public void mouseClicked(MouseEvent e) {
						}
						@Override
						public void mousePressed(MouseEvent e) {
							main.mouseEvent(key, 1);
						}
						@Override
						public void mouseReleased(MouseEvent e) {
							main.mouseEvent(key, 0);
						}
						@Override
						public void mouseEntered(MouseEvent e) {
						}
						@Override
						public void mouseExited(MouseEvent e) {
						}
					});
				} else if (type.equals("button")) {
					JButton button = new JButton(text);
					mainPanel.add(button);
					components.put(key, button);
					button.addMouseListener(new MouseListener() {
						@Override
						public void mouseClicked(MouseEvent e) {
						}
						@Override
						public void mousePressed(MouseEvent e) {
							main.mouseEvent(key, 1);
						}
						@Override
						public void mouseReleased(MouseEvent e) {
							main.mouseEvent(key, 0);
						}
						@Override
						public void mouseEntered(MouseEvent e) {
						}
						@Override
						public void mouseExited(MouseEvent e) {
						}
					});
				} else if (type.equals("table")) {
                    Map<String, Map<String, Object>> data = (Map<String, Map<String, Object>>) ((Map<String, Object>) widgets.get(key)).get("data");
                    List<String> columns = (List<String>) ((Map<String, Object>) widgets.get(key)).get("columns");
                    String primaryKeyName = (String) ((Map<String, Object>) widgets.get(key)).get("primaryKeyName");
                    boolean primaryKeyVisible = !primaryKeyName.equals("");
                    int colNum = columns.size() + (primaryKeyVisible ? 1 : 0);
                    String[] columnsData = new String[colNum];
                    String[][] tableData = new String[data.keySet().size()][colNum];
                    if(primaryKeyVisible) {
                        columnsData[0] = primaryKeyName;
                        for(int i = 1; i < colNum; i++) {
                            columnsData[i] = columns.get(i - 1);
                        }
                    } else {
                        for(int i = 0; i < colNum; i++) {
                            columnsData[i] = columns.get(i);
                        }
                    }
                    int rowCount = 0;
                    for(String dataKey : data.keySet()) {
                        Map<String, Object> rowData = data.get(dataKey);
                        if(primaryKeyVisible) {
                            tableData[rowCount][0] = dataKey;
                            for(int j = 1; j < columnsData.length; j++) {
                                Object cellValue = rowData.get(columnsData[j]);
                                if(cellValue == null) {
                                    tableData[rowCount][j] = "";
                                } else {
                                    tableData[rowCount][j] = rowData.get(columnsData[j]).toString();
                                }
                            }
                        } else {
                            for(int j = 0; j < columnsData.length; j++) {
                                Object cellValue = rowData.get(columnsData[j]);
                                if(cellValue == null) {
                                    tableData[rowCount][j] = "";
                                } else {
                                    tableData[rowCount][j] = rowData.get(columnsData[j]).toString();
                                }
                            }
                        }
                        rowCount++;
                    }
                    DefaultTableModel tableModel = new DefaultTableModel(tableData, columnsData) {
						@Override public boolean isCellEditable(int row, int column) {
							return false;
						}
					};
                    JTable table = new JTable(tableModel);
                    table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
					table.getTableHeader().setReorderingAllowed(false);
                    ListSelectionModel selectionModel = table.getSelectionModel();
                    selectionModel.addListSelectionListener(new ListSelectionListener() {
                        @Override
                        public void valueChanged(ListSelectionEvent e) {
                            if (!e.getValueIsAdjusting()) {
                                int selectedRow = table.getSelectedRow();
                                main.mouseEvent(key, selectedRow);
                                if (selectedRow != -1) {
                                    main.textEvent(key, table.getValueAt(selectedRow, 0).toString());
                                } else {
                                    main.textEvent(key, null);
                                }
                            }
                        }
                    });

                    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
                    JScrollPane scroll = new JScrollPane(table);
                    mainPanel.add(scroll);
                    components.put(key, table);
				}
			}
		} else {
			mainPanel.setLayout(null);
			for (String key : widgets.keySet()) {
				String type = (String) ((Map<String, Object>) widgets.get(key)).get("type");
				int y = (int) ((Map<String, Object>) widgets.get(key)).get("y");
				int x = (int) ((Map<String, Object>) widgets.get(key)).get("x");
				int height = (int) ((Map<String, Object>) widgets.get(key)).get("height");
				String text = (String) ((Map<String, Object>) widgets.get(key)).get("text");
				boolean visible = (boolean) ((Map<String, Object>) widgets.get(key)).get("visible");
				int width = (int) ((Map<String, Object>) widgets.get(key)).get("width");
				int state = (int) ((Map<String, Object>) widgets.get(key)).get("state");
				if (type.equals("textInput")) {
					JTextField textField = new JTextField(text,10);
					textField.setLocation(x, y);
					textField.setSize(width, height);
					mainPanel.add(textField);
					components.put(key, textField);
					textField.getDocument().addDocumentListener(new DocumentListener() {
						@Override
						public void insertUpdate(DocumentEvent e) {
							Document d = e.getDocument();
                            try {
                                String text = d.getText(0, d.getLength());
								main.textEvent(key, text);
                            } catch (BadLocationException ex) {
                                throw new RuntimeException(ex);
                            }
                        }
						@Override
						public void removeUpdate(DocumentEvent e) {
							Document d = e.getDocument();
							try {
								String text = d.getText(0, d.getLength());
								main.textEvent(key, text);
							} catch (BadLocationException ex) {
								throw new RuntimeException(ex);
							}
						}
						@Override
						public void changedUpdate(DocumentEvent e) {
							Document d = e.getDocument();
							try {
								String text = d.getText(0, d.getLength());
								main.textEvent(key, text);
							} catch (BadLocationException ex) {
								throw new RuntimeException(ex);
							}
						}
					});
				} else if (type.equals("label")) {
					JLabel label = new JLabel(text);
					label.setLocation(x, y);
					label.setSize(width, height);
					mainPanel.add(label);
					components.put(key, label);
					label.addMouseListener(new MouseListener() {
						@Override
						public void mouseClicked(MouseEvent e) {
						}
						@Override
						public void mousePressed(MouseEvent e) {
							main.mouseEvent(key, 1);
						}
						@Override
						public void mouseReleased(MouseEvent e) {
							main.mouseEvent(key, 0);
						}
						@Override
						public void mouseEntered(MouseEvent e) {
						}
						@Override
						public void mouseExited(MouseEvent e) {
						}
					});
				} else if (type.equals("button")) {
					JButton button = new JButton(text);
					button.setLocation(x, y);
					button.setSize(width, height);
					mainPanel.add(button);
					components.put(key, button);
					button.addMouseListener(new MouseListener() {
						@Override
						public void mouseClicked(MouseEvent e) {
						}
						@Override
						public void mousePressed(MouseEvent e) {
							main.mouseEvent(key, 1);
						}
						@Override
						public void mouseReleased(MouseEvent e) {
							main.mouseEvent(key, 0);
						}
						@Override
						public void mouseEntered(MouseEvent e) {
						}
						@Override
						public void mouseExited(MouseEvent e) {
						}
					});
				} else if (type.equals("table")) {
                    Map<String, Map<String, Object>> data = (Map<String, Map<String, Object>>) ((Map<String, Object>) widgets.get(key)).get("data");
                    List<String> columns = (List<String>) ((Map<String, Object>) widgets.get(key)).get("columns");
                    String primaryKeyName = (String) ((Map<String, Object>) widgets.get(key)).get("primaryKeyName");
                    boolean primaryKeyVisible = !primaryKeyName.equals("");
                    int colNum = columns.size() + (primaryKeyVisible ? 1 : 0);
                    String[] columnsData = new String[colNum];
                    String[][] tableData = new String[data.keySet().size()][colNum];
                    if(primaryKeyVisible) {
                        columnsData[0] = primaryKeyName;
                        for(int i = 1; i < colNum; i++) {
                            columnsData[i] = columns.get(i - 1);
                        }
                    } else {
                        for(int i = 0; i < colNum; i++) {
                            columnsData[i] = columns.get(i);
                        }
                    }
                    int rowCount = 0;
                    for(String dataKey : data.keySet()) {
                        Map<String, Object> rowData = data.get(dataKey);
                        if(primaryKeyVisible) {
                            tableData[rowCount][0] = dataKey;
                            for(int j = 1; j < columnsData.length; j++) {
                                Object cellValue = rowData.get(columnsData[j]);
                                if(cellValue == null) {
                                    tableData[rowCount][j] = "";
                                } else {
                                    tableData[rowCount][j] = rowData.get(columnsData[j]).toString();
                                }
                            }
                        } else {
                            for(int j = 0; j < columnsData.length; j++) {
                                Object cellValue = rowData.get(columnsData[j]);
                                if(cellValue == null) {
                                    tableData[rowCount][j] = "";
                                } else {
                                    tableData[rowCount][j] = rowData.get(columnsData[j]).toString();
                                }
                            }
                        }
                        rowCount++;
                    }
                    DefaultTableModel tableModel = new DefaultTableModel(tableData, columnsData) {
						@Override public boolean isCellEditable(int row, int column) {
							return false;
						}
					};
                    JTable table = new JTable(tableModel);
                    table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
					table.getTableHeader().setReorderingAllowed(false);
					ListSelectionModel selectionModel = table.getSelectionModel();
                    selectionModel.addListSelectionListener(new ListSelectionListener() {
                        @Override
                        public void valueChanged(ListSelectionEvent e) {
                            if (!e.getValueIsAdjusting()) {
                                int selectedRow = table.getSelectedRow();
                                main.mouseEvent(key, selectedRow);
                                if (selectedRow != -1) {
                                    main.textEvent(key, table.getValueAt(selectedRow, 0).toString());
                                } else {
                                    main.textEvent(key, null);
                                }
                            }
                        }
                    });

                    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
                    JScrollPane scroll = new JScrollPane(table);
                    scroll.setLocation(x, y);
                    scroll.setSize(width, height);
                    mainPanel.add(scroll);
                    components.put(key, table);
				}
			}
		}
		mainPanel.invalidate();
		mainPanel.validate();
		mainPanel.repaint();
	}

	public void setX(String wid, int x) {
		int y = components.get(wid).getY();
		components.get(wid).setLocation(x, y);
	}

	public void setY(String wid, int y) {
		int x = components.get(wid).getX();
		components.get(wid).setLocation(x, y);
	}

	public void setText(String wid, String text) {
		Component widget = components.get(wid);
		if (widget instanceof JButton) {
			((JButton) widget).setText(text);
		} else if (widget instanceof JLabel) {
			((JLabel) widget).setText(text);
		} else if (widget instanceof JTextField) {
			((JTextField) widget).setText(text);
		}
	}

	public void setVisible(String wid, boolean visible) {
		components.get(wid).setVisible(visible);
	}

	public void setTable(String scId, String wid, Map<String, Map<String, Object>> data) {
        if (curScreenId == null || !curScreenId.equals(scId)) return;
        if (!(components.get(wid) instanceof JTable)) return;
        JTable table = (JTable) components.get(wid);
        DefaultTableModel tableModel = (DefaultTableModel) table.getModel();
        tableModel.setRowCount(0);
        int i = 0;
        for(String dataKey : data.keySet()) {
            Map<String, Object> rowData = data.get(dataKey);
            tableModel.addRow(new Object[tableModel.getColumnCount()]);
            for (int j = 0; j < tableModel.getColumnCount(); j++) {
                String column = tableModel.getColumnName(j);
                if (rowData.get(column) == null) {
                    tableModel.setValueAt(dataKey, i, j);
                } else {
                    tableModel.setValueAt(rowData.get(column), i, j);
                }
            }
            i++;
        }
        table.setModel(tableModel);
    }

	public void setLayout(boolean layout) {
		if (layout) {
			mainPanel.setLayout(new FlowLayout());
		} else {
			mainPanel.setLayout(null);
		}
	}
	public boolean hasSaved() {
		return hasSaved;
	}
	public void setSaved(boolean saved){
		this.hasSaved = saved;
	}
}
