package no.uio.ifi.trackfind.frontend.providers;

import com.vaadin.data.provider.AbstractBackEndHierarchicalDataProvider;
import com.vaadin.data.provider.HierarchicalQuery;
import com.vaadin.server.SerializablePredicate;
import no.uio.ifi.trackfind.backend.configuration.TrackFindProperties;
import no.uio.ifi.trackfind.backend.data.TreeNode;
import no.uio.ifi.trackfind.backend.pojo.TfHub;
import no.uio.ifi.trackfind.backend.services.MetamodelService;
import no.uio.ifi.trackfind.frontend.filters.TreeFilter;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

@Service
public class TrackFindDataProvider extends AbstractBackEndHierarchicalDataProvider<TreeNode, SerializablePredicate<TreeNode>> {

    private TrackFindProperties properties;
    private MetamodelService metamodelService;

    /**
     * {@inheritDoc}
     */
    @Override
    protected Stream<TreeNode> fetchChildrenFromBackEnd(HierarchicalQuery<TreeNode, SerializablePredicate<TreeNode>> query) {
        TreeFilter treeFilter = (TreeFilter) query.getFilter().orElseThrow(RuntimeException::new);
        TfHub hub = treeFilter.getHub();
        String repository = hub.getRepository();
        String hubName = hub.getName();
        Map<String, Map<String, Object>> metamodelTree = metamodelService.getMetamodelTree(repository, hubName);
        Optional<TreeNode> parentOptional = query.getParentOptional();
        if (!parentOptional.isPresent()) {
            Stream<TreeNode> treeNodeStream = metamodelTree.keySet().parallelStream().map(c -> {
                TreeNode treeNode = new TreeNode();
                treeNode.setCategory(c);
                treeNode.setValue(c);
                treeNode.setParent(null);
                treeNode.setSeparator(properties.getLevelsSeparator());
                treeNode.setLevel(0);
                treeNode.setHasValues(false);
                treeNode.setChildren(metamodelTree.get(c).keySet());
                treeNode.setAttribute(true);
                treeNode.setArray(false);
                treeNode.setType("string");
                return treeNode;
            }).sorted();
            return treeNodeStream.filter(treeFilter);
        } else {
            TreeNode parent = parentOptional.get();
            if (!parent.isAttribute()) {
                return Stream.empty();
            }
            String category = parent.getCategory();
            Collection<String> children = parent.getChildren();
            Stream<TreeNode> treeNodeStream = children.parallelStream().map(c -> {
                TreeNode treeNode = new TreeNode();
                treeNode.setCategory(category);
                treeNode.setValue(c);
                treeNode.setParent(parent);
                treeNode.setSeparator(properties.getLevelsSeparator());
                treeNode.setLevel(parent.getLevel() + 1);
                Collection<String> grandChildren = metamodelService.getValues(repository, hubName, category, treeNode.getPath());
                if (CollectionUtils.isEmpty(grandChildren)) {
                    treeNode.setHasValues(false);
                    treeNode.setChildren(metamodelService.getAttributes(repository, hubName, category, treeNode.getPath()));
                } else {
                    treeNode.setHasValues(true);
                    treeNode.setChildren(grandChildren);
                }
                treeNode.setAttribute(CollectionUtils.isNotEmpty(treeNode.getChildren()));
                treeNode.setArray(metamodelService.getArrayOfObjectsAttributes(repository, hubName, category).contains(treeNode.getPath()));
                treeNode.setType(metamodelService.getAttributeTypes(repository, hubName, category).get(treeNode.getPath()));
                return treeNode;
            }).sorted();
            return treeNodeStream.filter(treeFilter);
        }
    }

    @Override
    public int getChildCount(HierarchicalQuery<TreeNode, SerializablePredicate<TreeNode>> query) {
        return (int) fetchChildrenFromBackEnd(query).count();
    }

    @Override
    public boolean hasChildren(TreeNode item) {
        return item.isAttribute();
    }

    @Autowired
    public void setProperties(TrackFindProperties properties) {
        this.properties = properties;
    }

    @Autowired
    public void setMetamodelService(MetamodelService metamodelService) {
        this.metamodelService = metamodelService;
    }

}

@@ -1182,32 +1182,98 @@
 Node%3E%3E query) %7B%0A
+        String levelsSeparator = properties.getLevelsSeparator();%0A
         TreeFilt
@@ -1355,16 +1355,16 @@
 ::new);%0A
-
         
@@ -2016,39 +2016,25 @@
 etSeparator(
-properties.getL
+l
 evelsSeparat
@@ -2027,34 +2027,32 @@
 (levelsSeparator
-()
 );%0A             
@@ -2645,24 +2645,275 @@
 Category();%0A
+            Collection%3CString%3E arrayOfObjectsAttributes = metamodelService.getArrayOfObjectsAttributes(repository, hubName, category);%0A            Map%3CString, String%3E attributeTypes = metamodelService.getAttributeTypes(repository, hubName, category);%0A
             
@@ -2960,24 +2960,80 @@
 Children();%0A
+            String prefix = category + levelsSeparator;%0A
             
@@ -3323,31 +3323,17 @@
 parator(
-properties.getL
+l
 evelsSep
@@ -3338,18 +3338,16 @@
 eparator
-()
 );%0A     
@@ -4040,37 +4040,17 @@
 etArray(
-metamodelService.getA
+a
 rrayOfOb
@@ -4068,39 +4068,8 @@
 utes
-(repository, hubName, category)
 .con
@@ -4084,32 +4084,52 @@
 eeNode.getPath()
+.replace(prefix, %22%22)
 ));%0A            
@@ -4153,73 +4153,22 @@
 ype(
-metamodelService.getAttributeTypes(repository, hubName, category)
+attributeTypes
 .get
@@ -4178,32 +4178,52 @@
 eeNode.getPath()
+.replace(prefix, %22%22)
 ));%0A            

Fix asterisk in search query builder