/*
 *  Copyright (C) 2016  Datamatica (dev@datamatica.pl)
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published
 *  by the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 * 
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package pl.datamatica.traccar.api.controllers;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import pl.datamatica.traccar.api.Application;
import pl.datamatica.traccar.api.dtos.MessageKeys;
import pl.datamatica.traccar.api.dtos.out.CommandResponseDto;
import pl.datamatica.traccar.api.dtos.out.ErrorDto;
import pl.datamatica.traccar.api.providers.ActiveDeviceProvider;
import pl.datamatica.traccar.api.providers.BackendCommandProvider;
import pl.datamatica.traccar.api.responses.ErrorResponse;
import pl.datamatica.traccar.api.responses.HttpStatuses;
import pl.datamatica.traccar.api.services.CommandService;
import pl.datamatica.traccar.api.utils.JsonUtils;
import spark.Request;
import spark.Spark;

/**
 *
 * @author Jan Usarek
 */
public class CommandsController extends ControllerBase {
    public static class Binder extends ControllerBinder {

        @Override
        public void bind() {
            
            Spark.post(rootUrl() + "/devices/:deviceId/sendCommand/:commandType", (req, res) -> {
                Long deviceId = Long.valueOf(req.params(":deviceId"));
                String commandType = req.params(":commandType");
                String params = req.body();
                Map<String, Object> commandParams = new HashMap<>();
                res.type("application/json");
                
                if (params != null) {
                    try {
                        commandParams = JsonUtils.getCommandParams(params);
                    } catch (Exception e) {
                        res.status(HttpStatuses.BAD_REQUEST);
                        return getResponseError(HttpStatuses.BAD_REQUEST, MessageKeys.ERR_COMMAND_PARSE_PARAMS_FAILED);
                    }
                }
                
                ActiveDeviceProvider adp = new ActiveDeviceProvider();
                Object activeDevice = adp.getActiveDevice(deviceId);
                if (activeDevice == null) {
                    res.status(HttpStatuses.NOT_FOUND);
                    return getResponseError(HttpStatuses.NOT_FOUND, MessageKeys.ERR_ACTIVE_DEVICE_NOT_FOUND);
                }
                
                BackendCommandProvider bcp = new BackendCommandProvider();
                Object backendCommand = null;
                try {
                    backendCommand = bcp.getBackendCommand(deviceId, commandType);
                } catch (Exception e) {
                    res.status(HttpStatuses.BAD_REQUEST);
                    return getResponseError(HttpStatuses.BAD_REQUEST, MessageKeys.ERR_CREATE_COMMAND_OBJECT_FAILED);
                }

                CommandService cs = new CommandService();
                
                if (commandParams.size() > 0) {
                    // Change timezone parameter from hours to seconds
                    if (commandParams.get("timezone") != null) {
                        long timezoneHours = Long.valueOf(commandParams.get("timezone").toString());
                        long timezoneSeconds = timezoneHours * 3600;
                        commandParams.replace("timezone", timezoneSeconds);
                    }
                    
                    try {
                        backendCommand
                            .getClass()
                            .getMethod("setAttributes", Map.class)
                            .invoke(backendCommand, commandParams);
                    } catch (Exception e) {
                        res.status(HttpStatuses.BAD_REQUEST);
                        return getResponseError(HttpStatuses.BAD_REQUEST, MessageKeys.ERR_SET_COMMAND_ATTRIBUTES_FAILED);
                    }
                }
                
                Map<String, Object> result = cs.sendCommand(activeDevice, backendCommand);
                
                if (result == null || result.get("success") == null) {
                    res.status(HttpStatuses.BAD_REQUEST);
                    return getResponseError(HttpStatuses.BAD_REQUEST, MessageKeys.ERR_SEND_COMMAND_FAILED);
                }
                
                if ((boolean) result.get("success")) {
                    CommandResponseDto commandResponse = new CommandResponseDto(result.get("response").toString());
                    return commandResponse;
                } else {
                    if (result.get("reason") == "timeout") {
                        res.status(HttpStatuses.TIMED_OUT);
                        return getResponseError(HttpStatuses.TIMED_OUT, MessageKeys.ERR_COMMAND_RESPONSE_TIMEOUT);
                    } else {
                        res.status(HttpStatuses.BAD_REQUEST);
                        return getResponseError(HttpStatuses.BAD_REQUEST, MessageKeys.ERR_SEND_COMMAND_FAILED);
                    }
                }
            }, gson::toJson);

        }
        
        private ErrorResponse getResponseError(int status, String messageKey) {
            return new ErrorResponse(status, Collections.singletonList(new ErrorDto(messageKey)));
        }

        private CommandsController createController(Request req) {
            RequestContext context = req.attribute(Application.REQUEST_CONTEXT_KEY);
            CommandsController cc = new CommandsController(context);
            return cc;
        }
        
        public String baseUrl() {
            return resourcesUrl() + "/sendCommand";
        }
    }

    public CommandsController(RequestContext rc) {
        super(rc);
    }
    
}

@@ -838,24 +838,47 @@
 il.HashMap;%0A
+import java.util.List;%0A
 import java.
@@ -1234,66 +1234,8 @@
 er;%0A
-import pl.datamatica.traccar.api.responses.ErrorResponse;%0A
 impo
@@ -1634,28 +1634,16 @@
 ind() %7B%0A
-            
 %0A       
@@ -1909,24 +1909,41 @@
 req.body();%0A
+                %0A
             
@@ -1995,24 +1995,95 @@
 ashMap%3C%3E();%0A
+                %0A                res.status(HttpStatuses.BAD_REQUEST);%0A
             
@@ -2347,118 +2347,30 @@
   re
-s.status(HttpStatuses.BAD_REQUEST);%0A                        return getResponseError(HttpStatuses.BAD_REQUEST, 
+turn getResponseError(
 Mess
@@ -2756,32 +2756,8 @@
 ror(
-HttpStatuses.NOT_FOUND, 
 Mess
@@ -3121,114 +3121,30 @@
   re
-s.status(HttpStatuses.BAD_REQUEST);%0A                    return getResponseError(HttpStatuses.BAD_REQUEST, 
+turn getResponseError(
 Mess
@@ -4067,118 +4067,30 @@
   re
-s.status(HttpStatuses.BAD_REQUEST);%0A                        return getResponseError(HttpStatuses.BAD_REQUEST, 
+turn getResponseError(
 Mess
@@ -4395,114 +4395,30 @@
   re
-s.status(HttpStatuses.BAD_REQUEST);%0A                    return getResponseError(HttpStatuses.BAD_REQUEST, 
+turn getResponseError(
 Mess
@@ -4899,32 +4899,8 @@
 ror(
-HttpStatuses.TIMED_OUT, 
 Mess
@@ -4997,118 +4997,30 @@
   re
-s.status(HttpStatuses.BAD_REQUEST);%0A                        return getResponseError(HttpStatuses.BAD_REQUEST, 
+turn getResponseError(
 Mess
@@ -5159,29 +5159,30 @@
 private 
-ErrorResponse
+List%3CErrorDto%3E
  getResp
@@ -5195,20 +5195,8 @@
 ror(
-int status, 
 Stri
@@ -5234,34 +5234,8 @@
 turn
- new ErrorResponse(status,
  Col
@@ -5282,17 +5282,16 @@
 ageKey))
-)
 ;%0A      
@@ -5286,32 +5286,40 @@
 ey));%0A        %7D%0A
+        
 %0A        private

Change structure of error JSON according to API documentation.