/*
 * 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.database;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.traccar.BaseProtocol;
import org.traccar.Context;
import org.traccar.helper.Log;
import org.traccar.model.Command;
import org.traccar.model.Typed;
import org.traccar.model.Position;

public class CommandsManager  extends ExtendedObjectManager<Command> {

    private final Map<Long, Queue<Command>> deviceQueues = new ConcurrentHashMap<>();

    public CommandsManager(DataManager dataManager) {
        super(dataManager, Command.class);
    }

    public boolean checkDeviceCommand(long deviceId, long commandId) {
        return !getAllDeviceItems(deviceId).contains(commandId);
    }

    public boolean sendCommand(long commandId, long deviceId) throws Exception {
        return sendCommand(getById(commandId), deviceId);
    }

    public boolean sendCommand(Command command, long deviceId) throws Exception {
        boolean sent = true;
        if (command.getTextChannel()) {
            Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId);
            String phone = Context.getIdentityManager().getById(deviceId).getPhone();
            if (lastPosition != null) {
                BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
                protocol.sendTextCommand(phone, command);
            } else if (command.getType().equals(Command.TYPE_CUSTOM)) {
                if (Context.getSmppManager() != null) {
                    Context.getSmppManager().sendMessageSync(phone, command.getString(Command.KEY_DATA), true);
                } else {
                    throw new RuntimeException("SMPP client is not enabled");
                }
            } else {
                throw new RuntimeException("Command " + command.getType() + " is not supported");
            }
        } else {
            ActiveDevice activeDevice = Context.getConnectionManager().getActiveDevice(deviceId);
            if (activeDevice != null) {
                activeDevice.sendCommand(command);
            } else {
                sent = !getDeviceQueue(deviceId).add(command);
            }
        }
        return sent;
    }

    public Collection<Long> getSupportedCommands(long deviceId) {
        List<Long> result = new ArrayList<>();
        Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId);
        for (long commandId : getAllDeviceItems(deviceId)) {
            Command command = getById(commandId);
            if (lastPosition != null) {
                BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
                if ((command.getTextChannel()
                        ? protocol.getSupportedTextCommands() : protocol.getSupportedDataCommands())
                        .contains(command.getType())) {
                    result.add(commandId);
                }
            } else if (command.getType().equals(Command.TYPE_CUSTOM)) {
                result.add(commandId);
            }
        }
        return result;
    }

    public Collection<Typed> getCommandTypes(long deviceId, boolean textChannel) {
        List<Typed> result = new ArrayList<>();
        Position lastPosition = Context.getIdentityManager().getLastPosition(deviceId);
        if (lastPosition != null) {
            BaseProtocol protocol = Context.getServerManager().getProtocol(lastPosition.getProtocol());
            Collection<String> commands;
            commands = textChannel ? protocol.getSupportedTextCommands() : protocol.getSupportedDataCommands();
            for (String commandKey : commands) {
                result.add(new Typed(commandKey));
            }
        } else {
            result.add(new Typed(Command.TYPE_CUSTOM));
        }
        return result;
    }

    public Collection<Typed> getAllCommandTypes() {
        List<Typed> result = new ArrayList<>();
        Field[] fields = Command.class.getDeclaredFields();
        for (Field field : fields) {
            if (Modifier.isStatic(field.getModifiers()) && field.getName().startsWith("TYPE_")) {
                try {
                    result.add(new Typed(field.get(null).toString()));
                } catch (IllegalArgumentException | IllegalAccessException error) {
                    Log.warning(error);
                }
            }
        }
        return result;
    }

    private Queue<Command> getDeviceQueue(long deviceId) {
        if (!deviceQueues.containsKey(deviceId)) {
            deviceQueues.put(deviceId, new ConcurrentLinkedQueue<Command>());
        }
        return deviceQueues.get(deviceId);
    }

    public void sendQueuedCommands(ActiveDevice activeDevice) {
        Queue<Command> deviceQueue = deviceQueues.get(activeDevice.getDeviceId());
        if (deviceQueue != null) {
            Command command = deviceQueue.poll();
            while (command != null) {
                activeDevice.sendCommand(command);
                command = deviceQueue.poll();
            }
        }
    }

}

@@ -870,32 +870,8 @@
 ap;%0A
-import java.util.Queue;%0A
 impo
@@ -916,59 +916,8 @@
 Map;
-%0Aimport java.util.concurrent.ConcurrentLinkedQueue;
 %0A%0Aim
@@ -1208,21 +1208,20 @@
 p%3CLong, 
-Queue
+List
 %3CCommand
@@ -5254,21 +5254,20 @@
 private 
-Queue
+List
 %3CCommand
@@ -5398,29 +5398,17 @@
 new 
-ConcurrentLinkedQueue
+ArrayList
 %3CCom
@@ -5549,21 +5549,20 @@
         
-Queue
+List
 %3CCommand
@@ -5662,32 +5662,37 @@
 ) %7B%0A            
+for (
 Command command 
@@ -5683,33 +5683,33 @@
 Command command 
-=
+:
  deviceQueue.pol
@@ -5708,51 +5708,8 @@
 ueue
-.poll();%0A            while (command != null
 ) %7B%0A
@@ -5763,54 +5763,8 @@
 d);%0A
-                command = deviceQueue.poll();%0A
     

Use List instead of Queue