/*
    Copyright (c) 2005-2024 Leisenfels GmbH. All rights reserved.
    Use is subject to license terms.
*/

package com.lf.vfslib.test.s3;

import com.lf.vfslib.VFSLib;
import com.lf.vfslib.core.VFSLibConstants;
import com.lf.vfslib.s3.S3FileObject;
import com.lf.vfslib.s3.S3FileProvider;
import com.lf.vfslib.s3.S3FileSystemConfigBuilder;
import org.apache.commons.vfs2.*;
import org.apache.commons.vfs2.impl.DefaultFileSystemManager;
import org.apache.commons.vfs2.util.RandomAccessMode;

import javax.swing.*;
import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;


/**
 * Example class showing the use of the Amazon&reg; S3 provider.
 * <p>
 *
 * @author Axel Schwolow
 * @since 1.6
 */
public class ExampleAmazonS3 {


    /**
     * Constructor method.
     * <p>
     *
     * @param accesskeyid The access key ID from Amazon&reg;
     * @param secretkey   The secret key from Amazon&reg;
     * @param bucket      The bucket name to use
     * @param region      The AWS region to use
     * @throws org.apache.commons.vfs2.FileSystemException Error indication
     * @since 1.6
     */
    public ExampleAmazonS3(String accesskeyid, String secretkey, String region, String bucket) throws FileSystemException {

        System.out.println("Configuring VFSLib Amazon S3 provider:");
        System.out.println("    accesskeyid        " + accesskeyid);
        System.out.println("    secretkey          " + (secretkey != null ? secretkey.replaceFirst("^.{5}", "*****") : "(not available)"));
        System.out.println("    region             " + region);
        System.out.println("    bucket             " + bucket);

        // Add Amazon S3 provider to VFS, ask user for missing arguments
        if (accesskeyid == null) {
            accesskeyid = JOptionPane.showInputDialog("Please enter the access key ID from Amazon");
            System.out.println("The Amazon S3 access key ID is: " + accesskeyid);
        }
        if (secretkey == null) {
            secretkey = JOptionPane.showInputDialog("Please enter the secret key from Amazon");
            System.out.println("The Amazon S3 secret key is: " + secretkey.replaceFirst("^.{5}", "*****"));
        }
        if (bucket == null) {
            bucket = JOptionPane.showInputDialog("Please enter the name of the Amazon S3 bucket");
            System.out.println("The Amazon S3 bucket is: " + bucket);
        }
        if (region == null) {
            region = JOptionPane.showInputDialog("Please enter the AWS region");
            System.out.println("The AWS region is: " + region);
        }

        // Setup the main VFSLib instance
        VFSLib vfslib = new VFSLib();

        // We use the default file system manager here
        DefaultFileSystemManager fsmanager = (DefaultFileSystemManager) VFS.getManager();

        S3FileProvider provider = vfslib.addProviderAmazonS3(fsmanager);
        if (provider == null) {
            System.err.println("Sorry, the Amazon S3 provider could not be activated, exiting");
            return;
        }

        String scheme = vfslib.getSchemeAmazonS3();
        System.out.println("\nAmazon S3 scheme is \"" + scheme + '\"');

        // Pass bucket name over to VFSLib to access the Amazon S3 account
        FileSystemOptions options = new FileSystemOptions();
        S3FileSystemConfigBuilder builder = new S3FileSystemConfigBuilder();

        // Setup proper account name, used as user name for Amazon S3 URLs (failsafe is "anonymous"):
        //    s3://[displayname]@aws.amazon.com
        String username = "johndoe";
        builder.setAccountDisplayName(options, username);
        builder.setAccessKeyID(options, accesskeyid);
        builder.setSecretKey(options, secretkey);
        builder.setAWSRegion(options, region);
        builder.setBucketName(options, bucket);

        // List the Amazon S3 root folder
        String uri = scheme + "://" + username + "@aws.amazon.com";
        System.out.println("Listing child entries of " + uri + ":");
        try {
            FileObject fileobj = fsmanager.resolveFile(uri, options);
            if (fileobj.getType().equals(FileType.FOLDER)) {
                FileObject[] children = fileobj.getChildren();
                for (FileObject next : children) {
                    if (next.getType().equals(FileType.FOLDER)) {
                        System.out.println("    DIR:  " + next);
                    } else System.out.println("    FILE: " + next);
                }
            } else System.out.println("    Entry " + uri + " is not a folder");
        } catch (Exception e) {
            e.printStackTrace();
        }

        // The following code modifies files in the Amazon S3 file system.
        // Please uncomment the lines and adjust values for your needs.

        // Create an Amazon S3 folder in root
        String tempfolder = scheme + "://" + username + "@aws.amazon.com/0123456789abcdefghijklmnopqrstuvwxyz";
        System.out.println("Creating temporary folder " + tempfolder + ":");
        boolean success = false;
        try {
            FileObject fileobj = fsmanager.resolveFile(tempfolder, options);
            if (!fileobj.exists()) {
                fileobj.createFolder();
                success = fileobj.exists();
                System.out.println(success ? "    Successful" : "    Failed");
            } else {
                System.out.println("    Entry " + tempfolder + " does already exist, exiting");
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!success) {
            System.err.println("Sorry, could not create folder, exiting");
            return;
        }

        // Rename is currently not supported by Amazon S3 (also not by S3 Management Console).
        // The only way is to create new file/folder, copy contents (!), and remove old file/folder afterwards.
        // This is not customer friendly but a perfect way to turn the cloud provider company into a cash cow.

        String content = "Hello world!";
        String encoding = "ISO-8859-1";

        // Upload a file to Amazon S3. Get file type, last modified, and size.
        String tempfile = tempfolder + "/readme.txt";
        System.out.println("Uploading temporary file " + tempfile + ":");
        success = false;
        try {
            FileObject fileobj = fsmanager.resolveFile(tempfile, options);
            fileobj.getContent().setAttribute(VFSLibConstants.ATTR_CONTENT_LENGTH, content.getBytes(encoding).length);
            OutputStream ostream = fileobj.getContent().getOutputStream();
            ostream.write(content.getBytes(encoding));
            ostream.flush();
            ostream.close();

            success = fileobj.exists();
            System.out.println(success ? "    Successful" : "    Failed");

            System.out.println("    TYPE:  " + fileobj.getType().getName());
            System.out.println("    MOD:   " + new Date(fileobj.getContent().getLastModifiedTime()));  // Currently not supported for folders
            System.out.println("    SIZE:  " + fileobj.getContent().getSize());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!success) {
            System.err.println("Sorry, could not upload file, exiting");
            return;
        }

        // List the temporary S3 subfolder
        System.out.println("Listing child entries of " + tempfolder + ":");
        try {
            FileObject fileobj = fsmanager.resolveFile(tempfolder, options);
            if (fileobj.getType().equals(FileType.FOLDER)) {
                FileObject[] children = fileobj.getChildren();
                for (FileObject next : children) {
                    if (next.getType().equals(FileType.FOLDER)) {
                        System.out.println("    FOLDER: " + next);
                    } else System.out.println("    FILE:   " + next);
                }
            } else {
                System.out.println("    Entry " + tempfolder + " is not a folder, exiting");
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Download a file from S3
        System.out.println("Downloading temporary file " + tempfile + ":");
        success = false;
        try {
            FileObject fileobj = fsmanager.resolveFile(tempfile, options);
            ByteArrayOutputStream bostream = new ByteArrayOutputStream();
            InputStream istream = fileobj.getContent().getInputStream();
            int len;
            byte[] buffer = new byte[1024];
            while ((len = istream.read(buffer)) != -1) {
                bostream.write(buffer, 0, len);
            }
            istream.close();
            bostream.flush();
            bostream.close();
            String loaded = bostream.toString(encoding);

            success = loaded.equals(content);
            System.out.println(success ? "    Successful (content=" + loaded + ")" : "    Failed");
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!success) {
            System.err.println("Sorry, could not download file, exiting");
            return;
        }

        // Read contents of a file from S3 randomly
        System.out.println("Random read temporary file " + tempfile + ":");
        success = false;
        try {
            FileObject fileobj = fsmanager.resolveFile(tempfile, options);
            // This feature is not part of the interface, cast file object
            RandomAccessContent randomAccessContent = ((S3FileObject) fileobj).getRandomAccessContent(RandomAccessMode.READ);
            randomAccessContent.seek(6); // "world!"
            ByteArrayOutputStream bostream = new ByteArrayOutputStream();
            InputStream istream = randomAccessContent.getInputStream();
            int len;
            byte[] buffer = new byte[1024];
            while ((len = istream.read(buffer)) != -1) {
                bostream.write(buffer, 0, len);
            }
            istream.close();
            bostream.flush();
            bostream.close();
            String loaded = bostream.toString(encoding);

            success = loaded.equals("world!");
            System.out.println(success ? "    Successful (content=" + loaded + ")" : "    Failed");
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!success) {
            System.err.println("Sorry, could not download file, exiting");
            return;
        }

        // Remove a S3 file and folder
        System.out.println("Removing temporary entries:");
        success = false;
        try {
            // Remove file
            FileObject fileobj = fsmanager.resolveFile(tempfile, options);
            System.out.print("    " + tempfile);
            success = fileobj.delete();
            System.out.println(success ? "  Successful" : "    Failed");

            // Remove folder
            fileobj = fsmanager.resolveFile(tempfolder, options);
            System.out.print("    " + tempfolder);
            fileobj.deleteAll(); // Including children
            success = !fileobj.exists();
            System.out.println(success ? "  Successful" : "    Failed");
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!success) {
            System.err.println("Sorry, could not remove file/folder, exiting");
            return;
        }
    }

    /**
     * Functionality for testing and debugging.
     * <p>
     * Supported arguments:
     * <code>
     * -accesskeyid [value]        Your access key ID from Amazon&reg;
     * -secretkey [value]          Your secret key from Amazon&reg;
     * -region [value]             The AWS region to use
     * -bucket [value]             The bucket name to use
     * </code>
     * <p>
     *
     * @param args Array of strings with console arguments
     * @since 1.6
     */
    public static void main(String[] args) {

        String accesskeyid = null, secretkey = null, region = null, bucket = null;

        try {
            if (GraphicsEnvironment.isHeadless()) {
                System.err.println("\nSorry, this example requires a graphical environment, exiting");
                System.exit(1);
            }

            // Disable annoying VFS log messages like:
            // 20.09.2013 13:48:31 org.apache.commons.vfs2.VfsLog info
            // INFO: Using "C:\DOCUME~1\User1\LOCALS~1\Temp\vfs_cache" as temporary files store.
            System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.NoOpLog");
            System.out.println();

            // Disable annoying AWS API log messages like:
            // log4j2 – Failed to load class “org.slf4j.impl.StaticLoggerBinder”
            // Solution: add log4j-slf4j2-impl dependency in test scope

            // Parse arguments
            for (int i = 0; i < args.length; i++) {
                if (args[i].equals("-accesskeyid") && (i + 1) < args.length) accesskeyid = args[++i];
                else if (args[i].equals("-secretkey") && (i + 1) < args.length) secretkey = args[++i];
                else if (args[i].equals("-region") && (i + 1) < args.length) region = args[++i];
                else if (args[i].equals("-bucket") && (i + 1) < args.length) bucket = args[++i];
            }

            new ExampleAmazonS3(accesskeyid, secretkey, region, bucket);
            System.exit(0);
        } catch (Exception exc) {
            try {
                Thread.sleep(1000);
            } catch (Exception ignored) {
            }
            exc.printStackTrace();
        }
        System.exit(1);
    }
}
