/*
* Copyright 2014-2015 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.amazonaws.codesamples.lowlevel;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.amazonaws.services.dynamodbv2.model.DeleteItemRequest;
import com.amazonaws.services.dynamodbv2.model.GetItemRequest;
import com.amazonaws.services.dynamodbv2.model.GetItemResult;
import com.amazonaws.services.dynamodbv2.model.PutItemRequest;


public class LowLevelItemBinaryExample {
    
    static AmazonDynamoDBClient client = new AmazonDynamoDBClient(new ProfileCredentialsProvider());
    static String tableName = "Reply";
    static SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
    
    public static void main(String[] args) throws IOException {
        try {
    
            // Format the primary key values
            String threadId = "Amazon DynamoDB#DynamoDB Thread 2";
            
            dateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
            String replyDateTime = dateFormatter.format(new Date());
                
            // Add a new reply with a binary attribute type
            createItem(threadId, replyDateTime);
            
            // Retrieve the reply with a binary attribute type
            retrieveItem(threadId, replyDateTime);
            
            // clean up by deleting the item
            deleteItem(threadId, replyDateTime);
        } catch (Exception e) {
            System.err.println("Error running the binary attribute type example: " + e);
            e.printStackTrace(System.err);
        }
    }

    
    public static void createItem(String threadId, String replyDateTime) throws IOException {
        // Craft a long message
        String messageInput = "Long message to be compressed in a lengthy forum reply";
        
        // Compress the long message
        ByteBuffer compressedMessage = compressString(messageInput.toString());
        
        // Add a the reply
        Map<String, AttributeValue> replyInput = new HashMap<String, AttributeValue>();
        replyInput.put("Id", new AttributeValue().withS(threadId));
        replyInput.put("ReplyDateTime", new AttributeValue().withS(replyDateTime));
        replyInput.put("Message", new AttributeValue().withS("Long message follows"));
        replyInput.put("ExtendedMessage", new AttributeValue().withB(compressedMessage));
        replyInput.put("PostedBy", new AttributeValue().withS("User A"));
        
        PutItemRequest putReplyRequest = new PutItemRequest().withTableName(tableName).withItem(replyInput);
        
        client.putItem(putReplyRequest);
    }
        
    public static void retrieveItem(String threadId, String replyDateTime) throws IOException {
        HashMap<String, AttributeValue> key = new HashMap<String, AttributeValue>();
        key.put("Id", new AttributeValue().withS(threadId));
        key.put("ReplyDateTime", new AttributeValue().withS(replyDateTime));
        
        GetItemRequest getReplyRequest = new GetItemRequest()
            .withTableName(tableName)
            .withKey(key)
            .withConsistentRead(true);
        
        GetItemResult getReplyResult = client.getItem(getReplyRequest);
        
        // Decompress the reply message and print
        Map<String, AttributeValue> reply = getReplyResult.getItem();
        String message = decompressString(reply.get("ExtendedMessage").getB());
        System.out.println("Reply message:\n"
            + " Id: " + reply.get("Id").getS() + "\n" 
            + " ReplyDateTime: " + reply.get("ReplyDateTime").getS() + "\n" 
            + " PostedBy: " + reply.get("PostedBy").getS() + "\n"
            + " Message: " + reply.get("Message").getS() + "\n"
            + " ExtendedMessage (decompressed): " + message);
    }
      
    public static void deleteItem(String threadId, String replyDateTime) {
        HashMap<String, AttributeValue> key = new HashMap<String, AttributeValue>();
        key.put("Id", new AttributeValue().withS(threadId));
        key.put("ReplyDateTime", new AttributeValue().withS(replyDateTime));

        DeleteItemRequest deleteReplyRequest = new DeleteItemRequest()
            .withTableName(tableName)
            .withKey(key);
        client.deleteItem(deleteReplyRequest);
    }
    
    private static ByteBuffer compressString(String input) throws IOException {
        // Compress the UTF-8 encoded String into a byte[]
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        GZIPOutputStream os = new GZIPOutputStream(baos);
        os.write(input.getBytes("UTF-8"));
        os.finish();
        byte[] compressedBytes = baos.toByteArray();
        
        // The following code writes the compressed bytes to a ByteBuffer.
        // A simpler way to do this is by simply calling ByteBuffer.wrap(compressedBytes);  
        // However, the longer form below shows the importance of resetting the position of the buffer 
        // back to the beginning of the buffer if you are writing bytes directly to it, since the SDK 
        // will consider only the bytes after the current position when sending data to DynamoDB.  
        // Using the "wrap" method automatically resets the position to zero.
        ByteBuffer buffer = ByteBuffer.allocate(compressedBytes.length);
        buffer.put(compressedBytes, 0, compressedBytes.length);
        buffer.position(0); // Important: reset the position of the ByteBuffer to the beginning
        return buffer;
    }
    
    private static String decompressString(ByteBuffer input) throws IOException {
        byte[] bytes = input.array();
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        GZIPInputStream is = new GZIPInputStream(bais);
        
        int chunkSize = 1024;
        byte[] buffer = new byte[chunkSize];
        int length = 0;
        while ((length = is.read(buffer, 0, chunkSize)) != -1) {
            baos.write(buffer, 0, length);
        }
        
        return new String(baos.toByteArray(), "UTF-8");
    }
}