package me.hfox.morphix.mapping.field;

import com.mongodb.BasicDBList;
import me.hfox.morphix.Morphix;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

public class CollectionMapper extends FieldMapper<Collection> {

    protected Type type;
    protected FieldMapper<?> mapper;

    public CollectionMapper(Class<?> parent, Field field, Morphix morphix) {
        super(Collection.class, parent, field, morphix);
    }

    public CollectionMapper(CollectionMapper parent) {
        super(Collection.class, parent.parent, null, parent.morphix);
        this.type = ((ParameterizedType) parent.type).getActualTypeArguments()[0];
    }

    @Override
    protected void discover() {
        super.discover();
        if (field != null) {
            type = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0];
        }

        mapper = null;
        if (type instanceof Class) {
            Class<?> cls = (Class) type;
            if (Collection.class.isAssignableFrom(cls)) {
                mapper = new CollectionMapper(this);
            } else {
                mapper = FieldMapper.create(cls, parent, null, morphix);
            }
        } else if (type instanceof ParameterizedType) {
            ParameterizedType param = (ParameterizedType) type;
            Class<?> cls = (Class) param.getRawType();
            if (Map.class.isAssignableFrom(cls)) {
                mapper = new MapMapper(this);
            }
        }

        if (mapper == null) {
            throw new RuntimeException("Could not find suitable Mapper for " + type);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public Collection marshal(Object obj) {
        if (obj == null) {
            return null;
        }

        Collection collection = new ArrayList<>();
        if (obj instanceof BasicDBList) {
            BasicDBList list = (BasicDBList) obj;
            for (Object object : list) {
                collection.add(mapper.marshal(object));
            }
        }

        return collection;
    }

    @Override
    public BasicDBList unmarshal(Object obj) {
        BasicDBList list = new BasicDBList();

        if (obj instanceof Collection) {
            Collection collection = (Collection) obj;
            for (Object object : collection) {
                list.add(mapper.unmarshal(object));
            }
        }

        return list;
    }

}

@@ -761,32 +761,235 @@
 ts()%5B0%5D;%0A    %7D%0A%0A
+    public CollectionMapper(MapMapper parent, ParameterizedType type) %7B%0A        super(Collection.class, parent.parent, null, parent.morphix);%0A        this.type = type.getActualTypeArguments()%5B0%5D;%0A    %7D%0A%0A
     @Override%0A  
@@ -1282,144 +1282,8 @@
 pe;%0A
-            if (Collection.class.isAssignableFrom(cls)) %7B%0A                mapper = new CollectionMapper(this);%0A            %7D else %7B%0A    
     
@@ -1347,30 +1347,16 @@
 rphix);%0A
-            %7D%0A
         
@@ -1483,62 +1483,251 @@
     
-Class%3C?%3E cls = (Class) param.getRawType();%0A           
+if (param.getRawType() instanceof Class) %7B%0A                Class%3C?%3E cls = (Class) param.getRawType();%0A                if (Collection.class.isAssignableFrom(cls)) %7B%0A                    mapper = new CollectionMapper(this);%0A                %7D else
  if 
@@ -1770,32 +1770,36 @@
                 
+    
 mapper = new Map
@@ -1804,32 +1804,50 @@
 apMapper(this);%0A
+                %7D%0A
             %7D%0A  

Update CollectionMapper