package com.gentics.mesh.graphql.filter;

import com.gentics.mesh.core.data.GraphFieldContainer;
import com.gentics.mesh.core.rest.common.FieldTypes;
import com.gentics.mesh.core.rest.schema.FieldSchema;
import com.gentics.mesh.core.rest.schema.SchemaModel;
import com.gentics.mesh.graphql.context.GraphQLContext;
import com.gentics.mesh.graphqlfilter.filter.BooleanFilter;
import com.gentics.mesh.graphqlfilter.filter.DateFilter;
import com.gentics.mesh.graphqlfilter.filter.FilterField;
import com.gentics.mesh.graphqlfilter.filter.MainFilter;
import com.gentics.mesh.graphqlfilter.filter.StringFilter;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FieldFilter extends MainFilter<GraphFieldContainer> {
    private static final String NAME_PREFIX = "FieldFilter.";

    // TODO Remove this after all types are supported
    private static final Set<String> availableTypes = Stream.of(
        FieldTypes.STRING, FieldTypes.HTML, FieldTypes.DATE, FieldTypes.BOOLEAN
    ).map(FieldTypes::toString).collect(Collectors.toSet());

    public static FieldFilter filter(GraphQLContext context, SchemaModel container) {
        return context.getOrStore(NAME_PREFIX + container.getName(), () -> new FieldFilter(container));
    }

    private final SchemaModel schema;

    private FieldFilter(SchemaModel container) {
        super(container.getName() + "FieldFilter", "Filters by fields");
        this.schema = container;
    }

    @Override
    protected List<FilterField<GraphFieldContainer, ?>> getFilters() {
        return schema.getFields().stream()
            // filters fields where the Filter is not implemented
            // TODO remove this after all types are supported
            .filter(field -> availableTypes.contains(field.getType()))
            .map(this::createFieldFilter)
            .collect(Collectors.toList());
    }

    private FilterField<GraphFieldContainer, ?> createFieldFilter(FieldSchema fieldSchema) {
        String schemaName = schema.getName();
        String name = fieldSchema.getName();
        String description = "Filters by the field " + name;
        FieldTypes type = FieldTypes.valueByName(fieldSchema.getType());
        switch (type) {
            case STRING:
                return new FieldMappedFilter<>(name, description, StringFilter.filter(), node -> node.getString(name).getString(), schemaName);
            case HTML:
                return new FieldMappedFilter<>(name, description, StringFilter.filter(), node -> node.getHtml(name).getHTML(), schemaName);
            case DATE:
                return new FieldMappedFilter<>(name, description, DateFilter.filter(), node -> node.getDate(name).getDate(), schemaName);
            case BOOLEAN:
                return new FieldMappedFilter<>(name, description, BooleanFilter.filter(), node -> node.getBoolean(name).getBoolean(), schemaName);
            // TODO correctly implement other types
            case NUMBER:
            case BINARY:
            case LIST:
            case NODE:
            case MICRONODE:
                return new FieldMappedFilter<>(name, description, StringFilter.filter(), node -> "bogus", schemaName);
            default:
                throw new RuntimeException("Unexpected type " + type);
        }
    }
}

@@ -86,24 +86,273 @@
 dContainer;%0A
+import com.gentics.mesh.core.data.node.field.BooleanGraphField;%0Aimport com.gentics.mesh.core.data.node.field.DateGraphField;%0Aimport com.gentics.mesh.core.data.node.field.HtmlGraphField;%0Aimport com.gentics.mesh.core.data.node.field.StringGraphField;%0A
 import com.g
@@ -893,16 +893,52 @@
 il.Set;%0A
+import java.util.function.Function;%0A
 import j
@@ -2662,32 +2662,42 @@
 lter(), node -%3E 
+getOrNull(
 node.getString(n
@@ -2700,17 +2700,36 @@
 ng(name)
-.
+, StringGraphField::
 getStrin
@@ -2729,17 +2729,16 @@
 etString
-(
 ), schem
@@ -2857,32 +2857,42 @@
 lter(), node -%3E 
+getOrNull(
 node.getHtml(nam
@@ -2897,17 +2897,33 @@
 ame)
-.
+, HtmlGraphField::
 getHTML
-(
 ), s
@@ -3044,32 +3044,42 @@
 lter(), node -%3E 
+getOrNull(
 node.getDate(nam
@@ -3080,25 +3080,41 @@
 te(name)
-.
+, DateGraphField::
 getDate
-(
 ), schem
@@ -3245,16 +3245,26 @@
 node -%3E 
+getOrNull(
 node.get
@@ -3276,17 +3276,37 @@
 an(name)
-.
+, BooleanGraphField::
 getBoole
@@ -3307,17 +3307,16 @@
 tBoolean
-(
 ), schem
@@ -3725,11 +3725,235 @@
  %7D%0A    %7D
+%0A%0A    private static %3CT, R%3E R getOrNull(T nullableValue, Function%3CT, R%3E mapper) %7B%0A        if (nullableValue == null) %7B%0A            return null;%0A        %7D else %7B%0A            return mapper.apply(nullableValue);%0A        %7D%0A    %7D
 %0A%7D%0A

Add null checks to field fetchers