package etomica.meta.wrappers;

import etomica.meta.properties.Property;
import etomica.meta.SimulationModel;

import java.util.*;

/**
 * Surrogate for an instance of an object that is part of a simulation, providing greater control over which
 * fields are made accessible in the simulation model.
 * @param <T> the class of the wrapped object
 */
public abstract class Wrapper<T> {
    protected final T wrapped;
    protected final Class wrappedClass;
    protected final long wrappedId;
    protected final SimulationModel simModel;

    protected final List<Property> properties = new ArrayList<>();
    protected final List<Property> childProps = new ArrayList<>();
    protected final List<Property> valueProps = new ArrayList<>();

    //SimulationModel is needed for information about wrapper id, both for this instance and regarding child instances
    public Wrapper(T wrapped, SimulationModel simModel) {
        this.wrapped = wrapped;
        this.wrappedClass = wrapped.getClass();
        this.simModel = simModel;
        wrappedId = simModel.getNewId();

    }

    /**
     *
     * @return the class of the object contained in this wrapper
     */
    public Class getWrappedClass() {
        return wrappedClass;
    }

    public long getWrappedId() {
        return wrappedId;
    }

    public String toString() {
        return "#"+Long.toString(wrappedId);
    }

    public List<Property> getProperties() {
        return properties;
    }

    /**
     *
     * @return a Map from the property name to the current value held in the wrapped instance for all properties.
     * Values of child properties are given by their wrapper id
     */
    public Map<String, Object> getValues() {
        Map<String, Object> values = new HashMap<>();
        for (Property prop : valueProps) {
            if (prop.isIndexedProperty()) {
                if(!prop.canCount()) {
                    continue;
                } else {
                    int count = prop.invokeCount();
                    Object[] propValues = new Object[count];
                    for (int i = 0; i < count; i++) {
                        propValues[i] = prop.invokeReader(i);
                    }

                    values.put(prop.getName(), propValues);
                }

            } else {
                values.put(prop.getName(), prop.invokeReader());
            }
        }

        for (Property prop : childProps) {
            if(prop.isIndexedProperty()) continue;//revisit this
            Wrapper wrapper = simModel.getWrapper(prop.invokeReader());
            if(wrapper != null) values.put(prop.getName(), wrapper.toString());
        }

        return values;
    }

    /**
     *
     * @return a list of all the properties that are themselves objects
     */
    public List<Property> getChildProps() {
        return childProps;
    }

/*
    @SuppressWarnings("unchecked")
    public Map<String, Wrapper<?>> getChildren() {
        Map<String, Wrapper<?>> children = new HashMap<>();
        for (InstanceProperty prop : childProps) {
            System.out.printf("%s -> %s (%s)\n", this.wrappedClass.getSimpleName(), prop.getName(), prop.getPropertyType().getSimpleName());

            if (prop.isIndexedProperty()) {
                if (!prop.canCount()) {
                    continue;
                }

                int count = prop.invokeCount();
                Object[] propValues = new Object[count];
                for (int i = 0; i < count; i++) {
                    propValues[i] = prop.invokeReader(i);
                }
                children.put(prop.getName(), new ArrayWrapper(propValues));
            } else if (List.class.isAssignableFrom(prop.getPropertyType())) {
                List childList = (List) prop.invokeReader();
                for (Object o : childList) {
                    children.put(prop.getName(), WrapperIndex.getWrapper(o));
                }
            } else {
                children.put(prop.getName(), WrapperIndex.getWrapper(prop.invokeReader()));
            }
        }

        return children;
    }
*/

}

@@ -25,16 +25,53 @@
 ppers;%0A%0A
+import etomica.meta.SimulationModel;%0A
 import e
@@ -107,45 +107,85 @@
 ty;%0A
+%0A
 import 
-etomica.meta.SimulationModel;%0A
+java.util.ArrayList;%0Aimport java.util.HashMap;%0Aimport java.util.List;
 %0Aimp
@@ -198,17 +198,19 @@
 va.util.
-*
+Map
 ;%0A%0A/**%0A 
@@ -1384,91 +1384,8 @@
  %7D%0A%0A
-    public String toString() %7B%0A        return %22#%22+Long.toString(wrappedId);%0A    %7D%0A%0A
     
@@ -2438,16 +2438,17 @@
       if
+ 
 (prop.is
@@ -2470,32 +2470,300 @@
 ()) 
-continue;//revisit this%0A
+%7B%0A                if (!prop.canCount()) %7B%0A                    continue;%0A                %7D%0A                else %7B%0A                    int count = prop.invokeCount();%0A                    Object%5B%5D propValues = new Object%5Bcount%5D;%0A                    for (int i = 0; i %3C count; i++) %7B%0A            
     
@@ -2818,32 +2818,33 @@
 op.invokeReader(
+i
 ));%0A            
@@ -2847,18 +2847,31 @@
     
+      
+      
 if
+ 
 (wrappe
-r
+d
  != 
@@ -2875,16 +2875,99 @@
 != null)
+ propValues%5Bi%5D = wrapper.getWrappedId();%0A                    %7D%0A%0A                   
  values.
@@ -2962,32 +2962,38 @@
      values.put(
+%22#%22 + 
 prop.getName(), 
@@ -2996,28 +2996,296 @@
 (), 
-wrapper.toString());
+propValues);%0A                %7D%0A            %7D%0A            else %7B%0A                Wrapper wrapper = simModel.getWrapper(prop.invokeReader());%0A                if (wrapper != null) %7B%0A                    values.put(%22#%22 + prop.getName(), wrapper.getWrappedId());%0A                %7D%0A            %7D
 %0A   

Add ability to handle object indexed properties Move hash(#) from object value to property name