/*
 * Copyright 2010-2019 Amazon.com, Inc. or its affiliates. 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.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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.amazon.sqs.javamessaging.acknowledge;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;

import javax.jms.JMSException;

import com.amazon.sqs.javamessaging.AmazonSQSMessagingClientWrapper;
import com.amazon.sqs.javamessaging.SQSMessagingClientConstants;
import com.amazon.sqs.javamessaging.SQSMessageConsumerPrefetch.MessageManager;
import com.amazon.sqs.javamessaging.message.SQSMessage;
import com.amazonaws.services.sqs.model.ChangeMessageVisibilityBatchRequest;
import com.amazonaws.services.sqs.model.ChangeMessageVisibilityBatchRequestEntry;

/**
 * Used to negative acknowledge of group of messages.
 * <P>
 * Negative acknowledge resets the visibility timeout of a message, so that the
 * message can be immediately available to consume. This is mostly used on
 * <code>recover</code> and <code>close</code> methods.
 * <P>
 * Negative acknowledge can potentially cause duplicate deliveries.
 */
public class NegativeAcknowledger extends BulkSQSOperation {

    private static final int NACK_TIMEOUT = 0;

    private final AmazonSQSMessagingClientWrapper amazonSQSClient;

    public NegativeAcknowledger(AmazonSQSMessagingClientWrapper amazonSQSClient) {
        this.amazonSQSClient = amazonSQSClient;
    }
    
    /**
     * Bulk action for negative acknowledge on the list of messages of a
     * specific queue.
     * 
     * @param messageQueue
     *            Container for the list of message managers.
     * @param queueUrl
     *            The queueUrl of the messages, which they received from.
     * @throws JMSException
     *             If <code>action</code> throws.
     */
    public void bulkAction(ArrayDeque<MessageManager> messageQueue, String queueUrl) throws JMSException {
        List<String> receiptHandles = new ArrayList<String>();
        while (!messageQueue.isEmpty()) {
            receiptHandles.add(((SQSMessage) (messageQueue.pollFirst().getMessage())).getReceiptHandle());

            // If there is more than 10 stop can call action
            if (receiptHandles.size() == SQSMessagingClientConstants.MAX_BATCH) {
                action(queueUrl, receiptHandles);
                receiptHandles.clear();
            }
        }
        action(queueUrl, receiptHandles);
    }
    
    /**
     * Action call block for negative acknowledge for the list of receipt
     * handles. This action can be applied on multiple messages for the same
     * queue.
     * 
     * @param queueUrl
     *            The queueUrl of the queue, which the receipt handles belong.
     * @param receiptHandles
     *            The list of handles, which is be used to negative acknowledge
     *            the messages via using
     *            <code>changeMessageVisibilityBatch</code>.
     * @throws JMSException
     *             If <code>changeMessageVisibilityBatch</code> throws.
     */
    @Override
    public void action(String queueUrl, List<String> receiptHandles) throws JMSException {

        if (receiptHandles == null || receiptHandles.isEmpty()) {
            return;
        }

        List<ChangeMessageVisibilityBatchRequestEntry> nackEntries = new ArrayList<ChangeMessageVisibilityBatchRequestEntry>(
                receiptHandles.size());
        int batchId = 0;
        for (String messageReceiptHandle : receiptHandles) {
            ChangeMessageVisibilityBatchRequestEntry changeMessageVisibilityBatchRequestEntry = new ChangeMessageVisibilityBatchRequestEntry(
                    Integer.toString(batchId), messageReceiptHandle).withVisibilityTimeout(NACK_TIMEOUT);
            nackEntries.add(changeMessageVisibilityBatchRequestEntry);
            batchId++;
        }
        amazonSQSClient.changeMessageVisibilityBatch(new ChangeMessageVisibilityBatchRequest(
                queueUrl, nackEntries));
    }

}