/**
 * Copyright 2014 Google Inc. All rights reserved.
 *
 * 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 com.google.samples.apps.iosched.server.gcm.api;

import com.google.samples.apps.iosched.server.gcm.AuthHelper;
import com.google.samples.apps.iosched.server.gcm.AuthHelper.AuthInfo;
import com.google.samples.apps.iosched.server.gcm.BaseServlet;
import com.google.samples.apps.iosched.server.gcm.db.DeviceStore;
import com.google.samples.apps.iosched.server.gcm.db.models.Device;
import com.google.samples.apps.iosched.server.gcm.device.MessageSender;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Logger;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/** Send message to all registered devices */
@SuppressWarnings("serial")
public class SendMessageServlet extends BaseServlet {
    private static final Logger LOG = Logger.getLogger(SendMessageServlet.class.getName());
    private static final String SELF = "self";


    /** Actions that can be executed by non admins. */
    private static final HashSet<String> UNPRIVILEGED_ACTIONS = new HashSet<String>(
        Arrays.asList(new String[]{"sync_schedule", "test", "sync_user"}));

    @Override
    protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws IOException {
      // Allow CORS requests from any domain. The response below allows
      // preflight requests to be correctly responded.
      resp.addHeader("Access-Control-Allow-Origin", "*");
      resp.addHeader("Access-Control-Allow-Headers", "authorization");
      resp.setStatus(200);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {

        // Extract URL components
        String result = req.getPathInfo();
        if (result == null) {
            send(resp, 400, "Bad request (check request format)");
            return;
        }
        String[] components = result.split("/");
        if (components.length != 3) {
            send(resp, 400, "Bad request (check request format)");
            return;
        }
        String target = components[1];
        String action = components[2];

        // Let's see what this user is authorized to do
        AuthInfo authInfo = AuthHelper.processAuthorization(req);

        // If no auth info or non-admin trying to run non-whitelisted actions, no access.
        if (authInfo == null || action == null ||
            (!UNPRIVILEGED_ACTIONS.contains(action) && !authInfo.permAdmin)) {
          send(resp, 403, "Not authorized");
          return;
        }

        // Extract extraData
        String payload = readBody(req);

        // Request decoding complete. Log request parameters
        LOG.info("Authorized User: " + authInfo.clientName +
                "\nTarget: " + target +
                "\nAction: " + action +
                "\nExtra Data: " + payload);

        MessageSender sender = new MessageSender(getServletConfig());

        // what's the audience of the message?
        if ("global".equals(target)) {
            // Only admins can spam the world
            if (!authInfo.permAdmin) {
              LOG.info("Attempt to send global message, but no admin perm.");
              send(resp, 403, "Not authorized");
              return;
            }

            List<Device> allDevices = DeviceStore.getAllDevices();
            if (allDevices == null || allDevices.isEmpty()) {
                send(resp, 404, "No devices registered");
            } else {
                int resultCount = allDevices.size();
                LOG.info("Selected " + resultCount + " devices");
                sender.multicastSend(allDevices, action, payload);
                send(resp, 200, "Message queued: " + resultCount + " devices");
            }
        } else {
            // Send message to one device
            // If target is SELF, the GCM Group ID will be the auth key in header
            if (SELF.equals(target)) {
              // do we have permission to send message to self?
              if (!authInfo.permSendSelfMessage) {
                LOG.info("Attempt to send self message, but no self message perm.");
                send(resp, 403, "Not authorized");
                return;
              }
              // the target is the auth key (it represents the group)
              target = authInfo.authKey;
            } else {
              // sending message to a specific user. Only admin can do it.
              if (!authInfo.permAdmin) {
                LOG.info("Attempt to send message to specific target, but no admin perm.");
                send(resp, 403, "Not authorized");
                return;
              }
            }
            List<Device> userDevices = DeviceStore.findDevicesByGcmGroupId(target);
            if (userDevices == null || userDevices.isEmpty()) {
                send(resp, 404, "User not found");
            } else {
                int resultCount = userDevices.size();
                LOG.info("Selected " + resultCount + " devices");
                sender.multicastSend(userDevices, action, payload);
                send(resp, 200, "Message queued: " + resultCount + " devices");
            }
        }
    }

    private String readBody(HttpServletRequest req) throws IOException {
        ServletInputStream inputStream = req.getInputStream();
        java.util.Scanner s = new java.util.Scanner(inputStream).useDelimiter("\\A");
        return s.hasNext() ? s.next() : "";
    }

}


