/*
 * Copyright 2017 Anton Tananaev (anton@traccar.org)
 * Copyright 2017 Andrey Kunitsyn (andrey@traccar.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.traccar.api.resource;

import java.sql.SQLException;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.traccar.Context;
import org.traccar.api.BaseResource;
import org.traccar.helper.LogAction;
import org.traccar.model.Device;
import org.traccar.model.Permission;
import org.traccar.model.User;

@Path("permissions")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class PermissionsResource  extends BaseResource {

    private void checkPermission(Permission permission, boolean link) {
        if (!link && permission.getOwnerClass().equals(User.class)
                && permission.getPropertyClass().equals(Device.class)) {
            if (getUserId() != permission.getOwnerId()) {
                Context.getPermissionsManager().checkUser(getUserId(), permission.getOwnerId());
            } else {
                Context.getPermissionsManager().checkAdmin(getUserId());
            }
        } else {
            Context.getPermissionsManager().checkPermission(
                    permission.getOwnerClass(), getUserId(), permission.getOwnerId());
        }
        Context.getPermissionsManager().checkPermission(
                permission.getPropertyClass(), getUserId(), permission.getPropertyId());
    }

    private void refreshPermissions(List<LinkedHashMap<String, Long>> entities) {
        Set<String> ownerProperty = new HashSet<>();
        entities.stream()
            .map(e -> {
                try {
                    return new Permission(e);
                } catch (ClassNotFoundException classNotFoundException) {
                    classNotFoundException.printStackTrace();
                    return null;
                }
            })
            .filter(Objects::nonNull)
            .filter(p -> ownerProperty.add(p.getOwnerClass().getName() + p.getPropertyClass().getName()))
            .forEach(p -> Context.getPermissionsManager().refreshPermissions(p));
    }

    @POST
    public Response add(LinkedHashMap<String, Long> entity) throws SQLException, ClassNotFoundException {
        return add(Stream.of(entity).collect(Collectors.toList()));
    }

    @Path("bulk")
    @POST
    public Response add(List<LinkedHashMap<String, Long>> entities) throws SQLException, ClassNotFoundException {
        Context.getPermissionsManager().checkReadonly(getUserId());
        for (LinkedHashMap<String, Long> entity: entities) {
            Permission permission = new Permission(entity);
            checkPermission(permission, true);
            Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(),
                    permission.getPropertyClass(), permission.getPropertyId(), true);
            LogAction.link(getUserId(), permission.getOwnerClass(), permission.getOwnerId(),
                    permission.getPropertyClass(), permission.getPropertyId());
        }
        refreshPermissions(entities);
        return Response.noContent().build();
    }

    @DELETE
    public Response remove(LinkedHashMap<String, Long> entity) throws SQLException, ClassNotFoundException {
        return remove(Stream.of(entity).collect(Collectors.toList()));
    }

    @DELETE
    @Path("bulk")
    public Response remove(List<LinkedHashMap<String, Long>> entities) throws SQLException, ClassNotFoundException {
        Context.getPermissionsManager().checkReadonly(getUserId());
        for (LinkedHashMap<String, Long> entity: entities) {
            Permission permission = new Permission(entity);
            checkPermission(permission, false);
            Context.getDataManager().linkObject(permission.getOwnerClass(), permission.getOwnerId(),
                    permission.getPropertyClass(), permission.getPropertyId(), false);
            LogAction.unlink(getUserId(), permission.getOwnerClass(), permission.getOwnerId(),
                    permission.getPropertyClass(), permission.getPropertyId());
        }
        refreshPermissions(entities);
        return Response.noContent().build();
    }
}

@@ -748,22 +748,24 @@
 va.util.
-HashSe
+ArrayLis
 t;%0Aimpor
@@ -817,124 +817,8 @@
 ist;
-%0Aimport java.util.Objects;%0Aimport java.util.Set;%0Aimport java.util.stream.Collectors;%0Aimport java.util.stream.Stream;
 %0A%0Aim
@@ -2190,45 +2190,38 @@
     
-private void refreshPermissions(List%3C
+@POST%0A    public Response add(
 Link
@@ -2247,231 +2247,27 @@
 ong%3E
-%3E
  entit
-ies) %7B%0A        Set%3CString%3E ownerProperty = new HashSet%3C%3E();%0A        entities.stream()%0A            .map(e -%3E %7B%0A                try %7B%0A                    return new Permission(e);%0A                %7D catch (ClassNotFound
+y) throws SQL
 Exce
@@ -2271,18 +2271,19 @@
 xception
- c
+, C
 lassNotF
@@ -2295,17 +2295,16 @@
 xception
-)
  %7B%0A     
@@ -2310,542 +2310,117 @@
     
-     
-       classNotFoundException.printStackTrace();%0A                    return null;%0A                %7D%0A            %7D)%0A            .filter(Objects::nonNull)%0A            .filter(p -%3E ownerProperty.add(p.getOwnerClass().getName() + p.getPropertyClass().getName()))%0A            .forEach(p -%3E Context.getPermissionsManager().refreshPermissions(p)
+List%3CLinkedHashMap%3CString, Long%3E%3E list = new ArrayList%3C%3E(
 );%0A    
-%7D%0A%0A
     
-@POST%0A    public Response add(LinkedHashMap%3CString, Long%3E entity) throws SQLException, ClassNotFoundException %7B%0A        return add(Stream.of(entity).collect(Collectors.toList())
+list.add(entity);%0A        return add(list
 );%0A 
@@ -3181,34 +3181,211 @@
         
-refreshPermissions
+// we assume all permissions are of same type so we use the first one for refreshing%0A        if (!entities.isEmpty())%0A            Context.getPermissionsManager().refreshPermissions(new Permission
 (entitie
@@ -3377,32 +3377,40 @@
 mission(entities
+.get(0))
 );%0A        retur
@@ -3581,68 +3581,120 @@
     
-return remove(Stream.of(entity).collect(Collectors.toList())
+List%3CLinkedHashMap%3CString, Long%3E%3E list = new ArrayList%3C%3E();%0A        list.add(entity);%0A        return remove(list
 );%0A 
@@ -4468,35 +4468,127 @@
     
-refreshPermissions(entities
+if (!entities.isEmpty())%0A            Context.getPermissionsManager().refreshPermissions(new Permission(entities.get(0))
 );%0A 

remove stream api assume permissions are of same type