/*
 * This file is part of a module with proprietary Enterprise Features.
 *
 * Licensed to Crate.io Inc. ("Crate.io") under one or more contributor
 * license agreements.  See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 *
 * Unauthorized copying of this file, via any medium is strictly prohibited.
 *
 * To use this file, Crate.io must have given you permission to enable and
 * use such Enterprise Features and you must have a valid Enterprise or
 * Subscription Agreement with Crate.io.  If you enable or use the Enterprise
 * Features, you represent and warrant that you have a valid Enterprise or
 * Subscription Agreement with Crate.io.  Your use of the Enterprise Features
 * if governed by the terms and conditions of your Enterprise or Subscription
 * Agreement with Crate.io.
 */

package io.crate.metadata.sys;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import io.crate.action.sql.SessionContext;
import io.crate.analyze.WhereClause;
import io.crate.analyze.user.Privilege;
import io.crate.auth.user.User;
import io.crate.metadata.ColumnIdent;
import io.crate.metadata.RelationName;
import io.crate.metadata.Routing;
import io.crate.metadata.RoutingProvider;
import io.crate.metadata.RowGranularity;
import io.crate.metadata.expressions.RowCollectExpressionFactory;
import io.crate.metadata.table.ColumnRegistrar;
import io.crate.metadata.table.StaticTableInfo;
import io.crate.types.DataTypes;
import org.elasticsearch.cluster.ClusterState;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static io.crate.execution.engine.collect.NestableCollectExpression.forFunction;

public class SysPrivilegesTableInfo extends StaticTableInfo {

    private static final RelationName IDENT = new RelationName(SysSchemaInfo.NAME, "privileges");
    private static final RowGranularity GRANULARITY = RowGranularity.DOC;

    private static class Columns {
        static final ColumnIdent GRANTEE = new ColumnIdent("grantee");
        static final ColumnIdent GRANTOR = new ColumnIdent("grantor");
        static final ColumnIdent STATE = new ColumnIdent("state");
        static final ColumnIdent TYPE = new ColumnIdent("type");
        static final ColumnIdent CLASS = new ColumnIdent("class");
        static final ColumnIdent IDENT = new ColumnIdent("ident");
    }

    private static final ImmutableList<ColumnIdent> PRIMARY_KEY = ImmutableList.of(
        Columns.GRANTEE, Columns.STATE, Columns.TYPE, Columns.CLASS, Columns.IDENT);

    public static class PrivilegeRow {
        private final String grantee;
        private final Privilege privilege;

        PrivilegeRow(String grantee, Privilege privilege) {
            this.grantee = grantee;
            this.privilege = privilege;
        }
    }


    public SysPrivilegesTableInfo() {
        super(IDENT, new ColumnRegistrar(IDENT, GRANULARITY)
                .register(Columns.GRANTEE, DataTypes.STRING)
                .register(Columns.GRANTOR, DataTypes.STRING)
                .register(Columns.STATE, DataTypes.STRING)
                .register(Columns.TYPE, DataTypes.STRING)
                .register(Columns.CLASS, DataTypes.STRING)
                .register(Columns.IDENT, DataTypes.STRING),
            PRIMARY_KEY);
    }

    @Override
    public RowGranularity rowGranularity() {
        return GRANULARITY;
    }

    @Override
    public Routing getRouting(ClusterState state,
                              RoutingProvider routingProvider,
                              WhereClause whereClause,
                              RoutingProvider.ShardSelection shardSelection,
                              SessionContext sessionContext) {
        return Routing.forTableOnSingleNode(IDENT, state.getNodes().getLocalNodeId());
    }

    public static Map<ColumnIdent, RowCollectExpressionFactory<PrivilegeRow>> expressions() {
        return ImmutableMap.<ColumnIdent, RowCollectExpressionFactory<PrivilegeRow>>builder()
            .put(Columns.GRANTEE, () -> forFunction(r -> r.grantee))
            .put(Columns.GRANTOR, () -> forFunction(r -> r.privilege.grantor()))
            .put(Columns.STATE, () -> forFunction(r -> r.privilege.state().toString()))
            .put(Columns.TYPE, () -> forFunction(r -> r.privilege.ident().type().toString()))
            .put(Columns.CLASS, () -> forFunction(r -> r.privilege.ident().clazz().toString()))
            .put(Columns.IDENT, () -> forFunction(r -> r.privilege.ident().ident()))
            .build();
    }

    public static Iterable<PrivilegeRow> buildPrivilegesRows(Iterable<User> users) {
        List<PrivilegeRow> privileges = new ArrayList<>();
        for (User user : users) {
            for (Privilege privilege : user.privileges()) {
                privileges.add(new PrivilegeRow(user.name(), privilege));
            }
        }
        return privileges;
    }
}

@@ -1579,25 +1579,19 @@
 va.util.
-ArrayList
+Map
 ;%0Aimport
@@ -1605,34 +1605,28 @@
 til.
-List;%0Aimport java.util.Map
+stream.StreamSupport
 ;%0A%0Ai
@@ -4670,152 +4670,153 @@
     
-List%3CPrivilegeRow%3E privileges = new ArrayList%3C%3E();%0A        for (User user : users) %7B%0A            for (Privilege privilege : user.privileges()) %7B
+return () -%3E StreamSupport.stream(users.spliterator(), false)%0A            .flatMap(u -%3E StreamSupport.stream(u.privileges().spliterator(), false)
 %0A   
@@ -4832,23 +4832,18 @@
     
-privileges.add(
+.map(p -%3E 
 new 
@@ -4856,19 +4856,16 @@
 egeRow(u
-ser
 .name(),
@@ -4870,19 +4870,10 @@
 ), p
-rivilege
 ))
-;
 %0A   
@@ -4885,45 +4885,20 @@
     
-%7D%0A        %7D%0A        return privileges
+).iterator()
 ;%0A  

Create privilege rows lazy