/*
	cursus - Race series management program
	Copyright 2011  Simon Arlott

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package eu.lp0.cursus.ui.component;

import javax.persistence.PersistenceException;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Objects;

import eu.lp0.cursus.db.DatabaseSession;
import eu.lp0.cursus.db.dao.AbstractDAO;
import eu.lp0.cursus.db.data.AbstractEntity;
import eu.lp0.cursus.util.Constants;
import eu.lp0.cursus.util.DatabaseError;

public abstract class DatabaseColumnModel<T extends AbstractEntity, V> implements DatabaseTableCellEditor.Column<T, V> {
	private final Logger log = LoggerFactory.getLogger(getClass());
	protected final DatabaseWindow win;
	private final AbstractDAO<T> dao;
	private final String name;
	private final boolean editable;

	public DatabaseColumnModel(String name) {
		this.win = null;
		this.dao = null;
		this.name = name;
		this.editable = false;
	}

	public DatabaseColumnModel(String name, DatabaseWindow win, AbstractDAO<T> dao) {
		this.win = win;
		this.dao = dao;
		this.name = name;
		this.editable = true;
	}

	public String getName() {
		return name;
	}

	public boolean isCellEditable() {
		return editable;
	}

	public void setupModel(JTable table, DatabaseTableModel<T> model, TableRowSorter<? extends TableModel> sorter, TableColumn col) {
		col.setCellRenderer(createCellRenderer());
		if (isCellEditable()) {
			col.setCellEditor(createCellEditor());
		}
	}

	protected TableCellRenderer createCellRenderer() {
		return new StringDatabaseTableCellRenderer<T>(this);
	}

	@Override
	public V loadValue(T row, boolean editing) {
		return getValue(row, editing);
	}

	@Override
	public boolean saveEditedValue(T row, V newValue) {
		assert (SwingUtilities.isEventDispatchThread());

		V oldValue = getValue(row, true);
		if (Objects.equal(oldValue, newValue)) {
			return true;
		}

		win.getDatabase().startSession();
		try {
			DatabaseSession.begin();

			if (!setValue(row, newValue)) {
				log.error(String.format("Unable to set row=%s#%d, column=%s, oldValue=%s, newValue=%s", row.getClass().getSimpleName(), row.getId(), //$NON-NLS-1$
						getName(), oldValue, newValue));
				return false;
			}

			T item = dao.get(row);
			if (!setValue(item, newValue)) {
				log.error(String.format("Unable to update row=%s#%d, column=%s, oldValue=%s, newValue=%s", row.getClass().getSimpleName(), row.getId(), //$NON-NLS-1$
						getName(), oldValue, newValue));

				if (!setValue(row, oldValue)) {
					log.error(String.format("Unable to revert row=%s#%d, column=%s, oldValue=%s, newValue=%s", row.getClass().getSimpleName(), row.getId(), //$NON-NLS-1$
							getName(), oldValue, newValue));
				}
				return false;
			}
			dao.persist(item);

			DatabaseSession.commit();
			return true;
		} catch (PersistenceException e) {
			log.error(String.format("Unable to save changes: row=%s#%d, column=%s, oldValue=%s, newValue=%s", row.getClass().getSimpleName(), row.getId(), //$NON-NLS-1$
					getName(), oldValue, newValue), e);
			win.reloadCurrentTabs();
			DatabaseError.errorSaving(win.getFrame(), Constants.APP_NAME, e);
			return false;
		} finally {
			win.getDatabase().endSession();
		}
	}

	protected abstract V getValue(T row, boolean editing);

	protected abstract boolean setValue(T row, V value);

	protected abstract TableCellEditor createCellEditor();
}
@@ -2785,16 +2785,43 @@
 %09%09try %7B%0A
+%09%09%09boolean reload = false;%0A
 %09%09%09Datab
@@ -2841,16 +2841,42 @@
 gin();%0A%0A
+%09%09%09T item = dao.get(row);%0A
 %09%09%09if (!
@@ -2884,19 +2884,20 @@
 etValue(
-row
+item
 , newVal
@@ -2942,19 +2942,22 @@
 able to 
-set
+update
  row=%25s#
@@ -3123,32 +3123,27 @@
 %09%09%7D%0A
-%0A
 %09%09%09
-T item = dao.get(row
+dao.persist(item
 );%0A%09
@@ -3150,36 +3150,35 @@
 %09%09if (!setValue(
-item
+row
 , newValue)) %7B%0A%09
@@ -3207,38 +3207,35 @@
 rmat(%22Unable to 
-update
+set
  row=%25s#%25d, colu
@@ -3370,320 +3370,133 @@
 ));%0A
-%0A
 %09%09%09%09
-if (!setValue(row, oldValue)) %7B%0A%09%09%09%09%09log.error(String.format(%22Unable to revert row=%25s#%25d, column=%25s, oldValue=%25s, newValue=%25s%22, row.getClass().getSimpleName(), row.getId(), //$NON-NLS-1$%0A%09%09%09%09%09%09%09getName(), oldValue, newValue));%0A%09%09%09%09%7D%0A%09%09%09%09return false;%0A%09%09%09%7D%0A%09%09%09dao.persist(item
+reload = true;%0A%09%09%09%7D%0A%0A%09%09%09DatabaseSession.commit();%0A%09%09%09dao.detach(item);%0A%09%09%09if (reload) %7B%0A%09%09%09%09win.reloadCurrentTabs(
 );%0A
-%0A
 %09%09%09
-DatabaseSession.commit();
+%7D
 %0A%09%09%09

reorder updates so that a revert of the model is never required (reverting removal a RaceAttendee will create a new transient RaceAttendee that then didn't match the database, so updates to its type would fail)