/*
    Copyright (c) 2005-2024 Leisenfels GmbH. All rights reserved.
    Use is subject to license terms.
*/

package com.lf.vfslib.test.dropbox;

import com.dropbox.core.DbxAppInfo;
import com.dropbox.core.DbxAuthFinish;
import com.dropbox.core.DbxRequestConfig;
import com.dropbox.core.DbxWebAuth;
import com.lf.vfslib.VFSLib;
import com.lf.vfslib.core.VFSLibConstants;
import com.lf.vfslib.core.VFSLibSettings;
import com.lf.vfslib.dropbox.DbxFileObject;
import com.lf.vfslib.dropbox.DbxFileProvider;
import com.lf.vfslib.dropbox.DbxFileSystemConfigBuilder;
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.net.URI;
import java.util.Date;


/**
 * Example class showing the use of the Dropbox&reg; provider.
 * <p>
 *
 * @author Axel Schwolow
 * @since 1.6
 */
public class ExampleDropbox {


    /**
     * Constructor method.
     * <p>
     *
     * @param appkey           The application key from Dropbox&reg;
     * @param appsecret        The application secret from Dropbox&reg;
     * @param clientidentifier The identifier for application
     * @param accesstoken      Your access token from Dropbox&reg; (use exclusively)
     * @throws FileSystemException Error indication
     * @since 1.6
     */
    public ExampleDropbox(String appkey, String appsecret, String clientidentifier, String accesstoken) throws FileSystemException {

        System.out.println("Configuring VFSLib Dropbox provider:");
        System.out.println("    appkey             " + appkey);
        System.out.println("    appsecret          " + (appsecret != null ? appsecret.replaceFirst("^.{5}", "*****") : "(not available)"));
        System.out.println("    clientidentifier   " + (clientidentifier != null ? clientidentifier : "(using default)"));
        System.out.println("    accesstoken        " + accesstoken);

        VFSLibSettings settings = VFSLibSettings.getSharedInstance();
        if (clientidentifier == null) clientidentifier = "VFSLib/" + settings.getDeployProps().getProperty("VERSION");

        // Setup the main VFSLib instance
        VFSLib vfslib = new VFSLib();

        // We use the default file system manager here
        DefaultFileSystemManager fsmanager = (DefaultFileSystemManager) VFS.getManager();

        // Add Dropbox provider to VFS, ask user for missing arguments
        String scheme = vfslib.getSchemeDropbox();
        System.out.println("\nDropbox scheme is \"" + scheme + '\"');

        if (appkey == null) {
            appkey = JOptionPane.showInputDialog("Please enter the application key from Dropbox");
            System.out.println("The Dropbox application key is: " + appkey);
        }
        if (appsecret == null) {
            appsecret = JOptionPane.showInputDialog("Please enter the application secret from Dropbox");
            System.out.println("The Dropbox application secret is: " + appsecret.replaceFirst("^.{5}", "*****"));
        }
        DbxFileProvider provider = vfslib.addProviderDropbox(fsmanager, appkey, appsecret, clientidentifier);
        if (provider == null) {
            System.err.println("Sorry, the Dropbox provider could not be activated, exiting");
            return;
        }

        // Perform authorization to let application access a Dropbox account
        DbxAppInfo appinfo = provider.getAppInfo();
        DbxRequestConfig reqconfig = provider.getRequestConfig();

        if (accesstoken == null) {

            // Open connection to Dropbox and get authorization URL to direct the user to
            DbxWebAuth webAuth = new DbxWebAuth(reqconfig, appinfo);
            final String authorizeurl = webAuth.authorize(DbxWebAuth.newRequestBuilder().withNoRedirect().build());
            if (authorizeurl == null) {
                System.err.println("Sorry, could not connect to dropbox.com, exiting");
                return;
            }

            // Open default web browser showing the received web address
            new Thread(() -> {
                try {
                    Thread.sleep(3000);

                    // Let the system open the default Internet browser
                    if (Desktop.isDesktopSupported()) Desktop.getDesktop().browse(new URI(authorizeurl));
                } catch (Exception ignored) {
                }
            }).start();
            System.out.println("If your browser did not open the Dropbox website, please go to " + authorizeurl);

            String code = JOptionPane.showInputDialog("Please enter the code from Dropbox (please wait for browser)");
            System.out.println("The Dropbox code is: " + code);

            // Get final access token, could be stored permanently e.g. in database
            try {
                DbxAuthFinish authfinish = webAuth.finishFromCode(code);
                accesstoken = authfinish.getAccessToken();
            } catch (Exception ignored) {
            }
            if (accesstoken == null) {
                System.err.println("Sorry, could not get the Dropbox access token, exiting");
                return;
            }
            System.out.println("The Dropbox access token is: " + accesstoken);
        }

        // Pass access token over to VFSLib to access the Dropbox account
        FileSystemOptions options = new FileSystemOptions();
        DbxFileSystemConfigBuilder builder = new DbxFileSystemConfigBuilder();
        builder.setAccessToken(options, accesstoken);

        // Setup proper account name, used as user name for Dropbox URLs (failsafe is "anonymous"):
        //    dropbox://[displayname]@dropbox.com
        String username = "vfslibtest";
        //builder.setAccountDisplayName(options, username);

        // List the Dropbox root folder
        String uri = scheme + "://" + username + "@dropbox.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("    FOLDER: " + next);
                    } else System.out.println("    FILE:   " + next);
                }
            } else {
                System.out.println("    Entry " + uri + " is not a folder, exiting");
                return;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        // The following code modifies files in the Dropbox file system.
        // Please uncomment the lines and adjust values for your needs.

        // Create a Dropbox folder in root
        String tempfolder = scheme + "://" + username + "@dropbox.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 the temporary folder
        String tempfolderrenamed = scheme + "://" + username + "@dropbox.com/0123456789abcdefghijklmnopqrstuvwxyz_renamed";
        System.out.println("Renaming temporary folder " + tempfolder + " to " + tempfolderrenamed + ":");
        success = false;
        try {
            FileObject fileobj = fsmanager.resolveFile(tempfolder, options);
            FileObject fileobjrenamed = fsmanager.resolveFile(tempfolderrenamed, options);
            fileobj.moveTo(fileobjrenamed);
            success = fileobjrenamed.exists();
            System.out.println(success ? "    Successful" : "    Failed");
            if (success) tempfolder = tempfolderrenamed;
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!success) {
            System.err.println("Sorry, could not rename folder, exiting");
            return;
        }

        String content = "Hello world!";
        String encoding = "ISO-8859-1";

        // Upload a file to Dropbox. 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 Dropbox 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();
        }

        // Rename a Dropbox file
        String tempfilerenamed = tempfolder + "/README";  // README.TXT not possible currently
        System.out.println("Renaming temporary file " + tempfile + " to " + tempfilerenamed + ":");
        success = false;
        try {
            FileObject fileobj = fsmanager.resolveFile(tempfile, options);
            FileObject fileobjrenamed = fsmanager.resolveFile(tempfilerenamed, options);
            fileobj.moveTo(fileobjrenamed);
            success = fileobjrenamed.exists();
            System.out.println(success ? "    Successful" : "    Failed");
            if (success) tempfile = tempfilerenamed;
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!success) {
            System.err.println("Sorry, could not rename file, exiting");
            return;
        }

        // Download a file from Dropbox
        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 Dropbox 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 = ((DbxFileObject) 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 Dropbox 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");
        }
    }

    /**
     * Functionality for testing and debugging.
     * <p>
     * Supported arguments:
     * <code>
     * -appkey [value]             Your application key from Dropbox&reg;
     * -appsecret [value]          Your application secret from Dropbox&reg;
     * -clientidentifier [value]   The identifier for your application
     * -accesstoken [value]        Your access token from Dropbox&reg; (use exclusively)
     * </code>
     * <p>
     *
     * @param args Array of strings with console arguments
     * @since 1.6
     */
    public static void main(String[] args) {

        String appkey = null, appsecret = null, clientidentifier = null, accesstoken = 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();

            // Parse arguments
            for (int i = 0; i < args.length; i++) {
                if (args[i].equals("-appkey") && (i + 1) < args.length) appkey = args[++i];
                else if (args[i].equals("-appsecret") && (i + 1) < args.length) appsecret = args[++i];
                else if (args[i].equals("-clientidentifier") && (i + 1) < args.length) clientidentifier = args[++i];
                else if (args[i].equals("-accesstoken") && (i + 1) < args.length) accesstoken = args[++i];
            }

            new ExampleDropbox(appkey, appsecret, clientidentifier, accesstoken);
            System.exit(0);
        } catch (Exception exc) {
            try {
                Thread.sleep(1000);
            } catch (Exception ignored) {
            }
            exc.printStackTrace();
        }
        System.exit(1);
    }
}
