package org.skyve.metadata.view.model.list;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.skyve.CORE;
import org.skyve.domain.Bean;
import org.skyve.domain.MapBean;
import org.skyve.domain.PersistentBean;
import org.skyve.domain.TransientBean;
import org.skyve.metadata.customer.Customer;
import org.skyve.metadata.model.document.Document;
import org.skyve.metadata.module.Module;
import org.skyve.persistence.DocumentQuery;
import org.skyve.util.Binder;

/**
 * A class to extend to make a quick model based on a collection or an association within the edited bean.
 * The referenceBinding can be compound.
 * Update() and remove() are not implemented so make sure your listGrid is read-only or implement them yourself.
 * 
 * @author mike
 *
 * @param <T>	The type of the bean under edit.
 */
public abstract class ReferenceListModel<T extends Bean> extends InMemoryListModel<T> {
	private static final long serialVersionUID = 6928933794461259542L;

	private String referenceBinding;
	
	/**
	 * A simple or compound binding expression to the collection to list with respect to the bean under edit.
	 * @param module	The reference's document's module.
	 * @param drivingDocument	The reference's document
	 * @param referenceBinding	The binding to the reference with respect to the edited bean - can be compound.
	 * @throws Exception
	 */
	public ReferenceListModel(Module module, Document drivingDocument, String referenceBinding)
	throws Exception {
		this.referenceBinding = referenceBinding;
		setDrivingDocument(module, drivingDocument);
	}
	
	/**
	 * Convenience constructor
	 * @param drivingDocument	The reference's document
	 * @param referenceBinding	The binding to the reference with respect to the edited bean - can be compound.
	 * @throws Exception
	 */
	public ReferenceListModel(Document drivingDocument, String referenceBinding)
	throws Exception {
		this(CORE.getCustomer().getModule(drivingDocument.getOwningModuleName()), drivingDocument, referenceBinding);
	}
	
	/**
	 * Convenience constructor
	 * @param moduleName	The reference's document's module name
	 * @param drivingDocumentName	The reference's document name
	 * @param referenceBinding	The binding to the reference with respect to the edited bean - can be compound.
	 * @throws Exception
	 */
	public ReferenceListModel(String moduleName, String drivingDocumentName, String referenceBinding)
	throws Exception {
		Customer c = CORE.getCustomer();
		Module m = c.getModule(moduleName);
		Document d = m.getDocument(c, drivingDocumentName);
		this.referenceBinding = referenceBinding;
		setDrivingDocument(m, d);
	}
	
	/**
	 * Return 1 association row or all collection rows.
	 */
	@Override
	public List<Bean> getRows() throws Exception {
		T bean = getBean();
		if (bean != null) {
			Object value = Binder.get(bean, referenceBinding);
			if (value instanceof List) {
				@SuppressWarnings("unchecked")
				List<Bean> values = (List<Bean>) value;
				// Make a defensive copy of the actual list here as it will be mutated by the model
				List<Bean> result = new ArrayList<>(values.size());
				for (Bean element : values) {
					result.add(defendTransientBean(element));
				}
				return result;
			}

			if (value instanceof Bean) {
				return Collections.singletonList(defendTransientBean((Bean) value));
			}
		}

		return Collections.emptyList();
	}
	
	private static Bean defendTransientBean(Bean bean) {
		if (bean instanceof TransientBean) {
			Map<String, Object> properties = new TreeMap<>();
			properties.put(DocumentQuery.THIS_ALIAS, bean);
			properties.put(PersistentBean.LOCK_NAME, null);
			properties.put(PersistentBean.TAGGED_NAME, Boolean.FALSE);
			properties.put(PersistentBean.FLAG_COMMENT_NAME, null);
			return new MapBean(bean.getBizModule(), bean.getBizDocument(), properties);
		}
		return bean;
	}
	
	/**
	 * Not implemented.
	 */
	@Override
	public Bean update(String bizId, SortedMap<String, Object> properties) throws Exception {
		throw new IllegalStateException("NOT IMPLEMENTED");
	}

	/**
	 * Not implemented.
	 */
	@Override
	public void remove(String bizId) throws Exception {
		throw new IllegalStateException("NOT IMPLEMENTED");
	}
}

@@ -3337,22 +3337,36 @@
 ) %7B%0A%09%09%09%09
-return
+// Note we can't use
  Collect
@@ -3387,43 +3387,187 @@
 List
-(defendTransientBean((Bean) value))
+ here as that is immutable and grid implementations may add a summary row to this list.%0A%09%09%09%09List%3CBean%3E result = new ArrayList%3C%3E(1);%0A%09%09%09%09result.add((Bean) value);%0A%09%09%09%09return result
 ;%0A%09%09

Replace use of Collections.singletonList in ReferenceListModel as grid implementations may add a summary row to the returned collection.