package jrex.ui.model;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.table.AbstractTableModel;

/**
 * A mutable table model backed by a Guava table.
 *
 * @author Mark George <mark.george@otago.ac.nz>
 */
public final class MutableTableModel extends AbstractTableModel {

	private Table<Integer, Integer, String> table = HashBasedTable.create();
	private List<String> headers;

	public MutableTableModel() {
		this.headers = null;
	}

	public MutableTableModel(List<String> headers) {
		this.headers = headers;
	}

	@Override
	public int getRowCount() {
		return table.rowKeySet().size();
	}

	@Override
	public int getColumnCount() {
		return table.columnKeySet().size();
	}

	@Override
	public String getColumnName(int columnIndex) {
		return headers.get(columnIndex);
	}

	@Override
	public Class<?> getColumnClass(int columnIndex) {
		return String.class;
	}

	@Override
	public boolean isCellEditable(int rowIndex, int columnIndex) {
		return true;
	}

	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		return table.get(rowIndex, columnIndex);
	}

	@Override
	public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
		table.put(rowIndex, columnIndex, aValue.toString());
		fireTableCellUpdated(rowIndex, columnIndex);
	}

	public void addRow(List<String> values) {
		int rowCount = getRowCount();
		for (int i = 0; i < values.size(); i++) {
			table.put(rowCount, i, values.get(i));
		}
		fireTableRowsInserted(rowCount, rowCount);
	}

	public void addRow(String[] values) {
		addRow(Arrays.asList(values));
	}

	public void deleteRow(int rowIndex) {
		// this gets messy no matter what backend we use since we need to reorder the row indexes

		// delete the row
		table.row(rowIndex).clear();

		Set<Integer> rowKeySet = table.rowKeySet();

		// create a new table
		Table<Integer, Integer, String> newTable = HashBasedTable.create();

		int idx = 0;

		for (Integer key : rowKeySet) {
			Map<Integer, String> row = table.row(key);
			newTable.put(idx, 0, row.get(0));
			newTable.put(idx, 1, row.get(1));
			idx++;
		}

		table = newTable;

		fireTableDataChanged();
	}

	public Table<Integer, Integer, String> getTable() {
		return table;
	}

	public void moveUp(int rowIndex) {
		if (rowIndex < 0 || rowIndex == 0) {
			return;
		}

		Map<Integer, String> selected = table.row(rowIndex);

		Map<Integer, String> previous = table.row(rowIndex - 1);
		Map<Integer, String> clone = new HashMap<>(selected);

		selected.put(0, previous.get(0));
		selected.put(1, previous.get(1));

		previous.put(0, clone.get(0));
		previous.put(1, clone.get(1));

		fireTableDataChanged();
	}

	public void moveDown(int rowIndex) {
		if (rowIndex < 0 || rowIndex == getRowCount() - 1) {
			return;
		}

		Map<Integer, String> selected = table.row(rowIndex);
		Map<Integer, String> next = table.row(rowIndex + 1);
		Map<Integer, String> clone = new HashMap<>(selected);

		selected.put(0, next.get(0));
		selected.put(1, next.get(1));

		next.put(0, clone.get(0));
		next.put(1, clone.get(1));

		fireTableDataChanged();
	}

	public List<String> getHeaders() {
		return headers;
	}

	public void setHeaders(List<String> headers) {
		this.headers = headers;
	}

	public List<List<String>> getRows() {

		List<List<String>> rows = new ArrayList<>();

		Map<Integer, Map<Integer, String>> rowMap = table.rowMap();
		for (Map.Entry<Integer, Map<Integer, String>> entry : rowMap.entrySet()) {
			rows.add(new ArrayList(entry.getValue().values()));
		}

		return rows;
	}

}
