package com.theorchard.javaowsassetbulkscript.logic;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;

import com.amazonaws.AmazonClientException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.BasicSessionCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.GetObjectRequest;
import com.amazonaws.services.s3.model.ListObjectsRequest;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.amazonaws.services.s3.model.S3ObjectSummary;
import com.amazonaws.services.s3.transfer.Download;
import com.amazonaws.services.s3.transfer.TransferManager;
import com.amazonaws.services.s3.transfer.TransferManagerBuilder;
import com.amazonaws.services.s3.transfer.Upload;
import com.amazonaws.services.s3.transfer.model.UploadResult;

public class AmazonS3Connector {
	private AWSCredentials credentials = null;
	private AWSCredentialsProvider credentialsProvider = null;
    private String bucketName;
    private TransferManager transferManager;
    
    private HashMap<String, File> fileList;

	private StringBuffer report;
	
    public AmazonS3Connector(String bucket, final String pubkey, final String secretkey, final String token) {
    	fileList = new HashMap<String, File>();
		bucketName = bucket;
		report = new StringBuffer();
		credentials = new BasicSessionCredentials(pubkey, secretkey, token);


		credentialsProvider = new AWSCredentialsProvider() {

			@Override
			public void refresh() {

			}

			@Override
			public AWSCredentials getCredentials() {
				return credentials;
			}
		};
	}

	public AmazonS3Connector(String bucket, final String pubkey, final String secretkey) {
		fileList = new HashMap<>();
		bucketName = bucket;
		report = new StringBuffer();
		credentials = new BasicAWSCredentials(pubkey, secretkey);
		credentialsProvider = new AWSCredentialsProvider() {

			@Override
			public void refresh() {

			}

			@Override
			public AWSCredentials getCredentials() {
				return credentials;
			}
		};
	}

	public void createAmazonS3Bucket() {
        try {
            if (!transferManager.getAmazonS3Client().doesBucketExistV2(bucketName)) {
                transferManager.getAmazonS3Client().createBucket(bucketName);
            }
        } catch (AmazonClientException ace) {
            ace.printStackTrace();
        }
    }
	
	public void establishConnection(String region) throws Exception {
        AmazonS3 s3 = AmazonS3ClientBuilder.standard()
        .withRegion(region)
        .withCredentials(credentialsProvider)
        .build();

        transferManager = TransferManagerBuilder.standard().withS3Client(s3).build();
        
        createAmazonS3Bucket();
	}

	public void closeConnection() {
		if (transferManager != null) {
			transferManager.shutdownNow(true);
		}
	}
	
	private boolean processUpload(Upload upload) {
		boolean uploadSuccess = true;
        UploadResult result = null;
        try {
            result = upload.waitForUploadResult();
		} catch (InterruptedException ie) {
			ie.printStackTrace();
		} catch (Exception e) {
			report.append(e.toString()+"\n");
			e.printStackTrace();
			uploadSuccess = false;
		}
		System.out.println(result);
		return uploadSuccess;
	}
	
	private boolean processDownload(Download download) {
		boolean downloadSuccess = true;
		try {
			download.waitForCompletion();
		} catch (InterruptedException ie) {
			
		} catch (Exception e) {
			report.append(e.toString()+"\n");
			e.printStackTrace();
			downloadSuccess = false;
		}
		return downloadSuccess;
	}
	
	public boolean uploadFile(String fileName, byte[] file, String path) {
		return uploadFile(fileName, new ByteArrayInputStream(file), path);
	}
	
	public boolean uploadFile(String fileName, InputStream input, String path) {
		return processUpload(transferManager.upload(bucketName, path+"/"+fileName, input, null));
	}
	
	public boolean uploadFile(String fileName, File file, String path) {
		String key = path + "/" + fileName;
		key = key.replaceAll("/+", "/");
		while(key.startsWith("/")) {
			key = key.substring(1);
		}
		PutObjectRequest putRequest = new PutObjectRequest(bucketName, key, file);
		return processUpload(transferManager.upload(putRequest));
	}
	
	public boolean uploadFiles(String path) {
		boolean uploadSuccess = true;
		
		for (Entry<String, File> entry : fileList.entrySet()) {
			uploadSuccess &= uploadFile(entry.getKey(), entry.getValue(), path);
		}
		
		return uploadSuccess;
	}

	public void addFiles(HashMap<String, File> files) {
		fileList.putAll(files);
	}
	
	public void addFile(String targetFileName, File srcFile) {
		fileList.put(targetFileName, srcFile);
	}
	
	public void delete(String fileName, String path) throws Exception {
		AmazonS3 as3 = transferManager.getAmazonS3Client();
		as3.deleteObject(bucketName,path+"/"+fileName);
	}
	
	public boolean exists(String fileName, String path) {
		AmazonS3 as3 = transferManager.getAmazonS3Client();
		return as3.doesObjectExist(bucketName,path+"/"+fileName);
	}

	public StringBuffer getReport() {
		return report;
	}
	
	public List<String> listFiles(String path, String regex) {
		AmazonS3 as3 = transferManager.getAmazonS3Client();
		ListObjectsRequest listReq = new ListObjectsRequest().withBucketName(bucketName).withPrefix((path+"/"+regex).replaceAll("//", "/"));
		ObjectListing listing = as3.listObjects(listReq);
		List<String> objects = new ArrayList<String>();
		
		for (S3ObjectSummary objectSummary : listing.getObjectSummaries()) {
			objects.add(objectSummary.getKey());
		}
		
		return objects;
	}
	
	public long getFileSize(String path, String fileName) {
		AmazonS3 as3 = transferManager.getAmazonS3Client();
		ListObjectsRequest listReq = new ListObjectsRequest().withBucketName(bucketName).withPrefix(path+"/"+fileName);
		ObjectListing listing = as3.listObjects(listReq);
		
		for (S3ObjectSummary objectSummary : listing.getObjectSummaries()) {
			if (objectSummary.getKey().contains(fileName)) {
				return objectSummary.getSize();
			}
		}
		
		return -1L;
	}
	
	public boolean download(String path, String fileName, File outFile) {
		String key = path + "/" + fileName;
		key = key.replaceAll("/+", "/");
		GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key);
		return processDownload(transferManager.download(getObjectRequest, outFile));
	}
}
