/*
    Copyright (c) 2005-2013 Leisenfels. All rights reserved.
    Use is subject to license terms.
*/

package com.lf.vfslib.test.gdrive;

import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import com.lf.commons.i18n.Locale;
import com.lf.commons.lang.JavaUtils;
import com.lf.vfslib.VFSLib;
import com.lf.vfslib.core.VFSLibSettings;
import com.lf.vfslib.gdrive.GDriveFileProvider;
import com.lf.vfslib.gdrive.GDriveFileSystemConfigBuilder;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileSystemOptions;
import org.apache.commons.vfs2.FileType;
import org.apache.commons.vfs2.VFS;
import org.apache.commons.vfs2.impl.DefaultFileSystemManager;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;


/**
 * Example class showing the use of the Google&reg; Drive provider.
 * <p/>
 * Required libraries in classpath:
 * <code>
 * commons-vfs-2.0.jar                                Original Apache VFS 2.0 library
 * commons-logging-1.1.jar                            Required for VFS 2.0
 * google-api-client-1.17.0-rc.jar                    Google API
 * google-api-services-drive-v2-rev102-1.17.0-rc.jar  Google API
 * google-http-client-1.17.0-rc.jar                   Google API
 * google-http-client-gson-1.17.0-rc.jar              Google API
 * google-http-client-jackson2-1.17.0-rc.jar          Google API
 * google-http-client-jdo-1.17.0-rc.jar               Google API
 * google-oauth-client-1.17.0-rc.jar                  Google API
 * jsr305-1.3.9.jar                                   Java Annotations
 * </code>
 * <p/>
 * @author Axel Schwolow
 * @version $Revision: 1.5 $, $Date: 2013/10/13 10:47:15 $
 * @since 1.6
 */
public class ExampleGoogleDrive {


    /**
     * Constructor method.
     * <p/>
     * @param language     The language (en|de)
     * @param licensefile  The license key file
     * @param clientid     The client ID from Google&reg;
     * @param clientsecret The client secret from Google&reg;
     * @param appname      The identifier for application
     * @param redirecturi  The redirect URI from Google&reg;
     * @param token        The access token (requires prior authorization)
     * @throws org.apache.commons.vfs2.FileSystemException
     *          Error indication
     * @since 1.6
     */
    public ExampleGoogleDrive (String language, File licensefile, String clientid, String clientsecret,
                               String appname, String redirecturi, String token) throws FileSystemException {

        System.out.println("Configuring VFSLib Google Drive provider:");
        System.out.println("    language           " + (language != null ? language : "(using English)"));
        System.out.println("    licensefile        " + licensefile);
        System.out.println("    clientid           " + clientid);
        System.out.println("    clientsecret       " + clientsecret);
        System.out.println("    appname            " + (appname != null ? appname : "(using default)"));
        System.out.println("    redirecturi        " + (redirecturi != null ? redirecturi : "(using default)"));
        System.out.println("    token              " + (token != null ? token.replaceFirst("^.{5}", "*****") : "(please authorize first)"));


        // Messages from VFSLib should be printed in the user's language (optional, default is "eng_US").
        // VFSLib supports ISO 639-2 codes (three language letters) which allows languages like "nds" (Low German).
        VFSLibSettings settings = VFSLibSettings.getSharedInstance();
        if (language == null) language = "en";
        if (!language.equals("en")) {
            settings.setUserLocale(Locale.parseLocale(language));  // Maps "de" -> "ger_DE"
        }
        if (appname == null) appname = "VFSLib/" + settings.getDeployProps().getVersion();
        if (redirecturi == null) redirecturi = "urn:ietf:wg:oauth:2.0:oob";

        // Setup the main VFSLib instance, get evaluation license key from Leisenfels website
        VFSLib vfslib = new VFSLib();
        if (licensefile == null) {
            JFileChooser chooser = new JFileChooser("Please choose a VFSLib license file");
            chooser.setFileFilter(new FileNameExtensionFilter("License Files", "txt"));
            chooser.showOpenDialog(null);
            licensefile = chooser.getSelectedFile();
            System.out.println("The VFSLib license file is: " + licensefile);
        }
        vfslib.setLicenseFile(licensefile);          // Import from file

        // We use the default file system manager here
        DefaultFileSystemManager fsmanager = (DefaultFileSystemManager) VFS.getManager();

        // Add Google Drive provider to VFS, ask user for missing arguments
        if (clientid == null) {
            clientid = JOptionPane.showInputDialog("Please enter the client ID from Google");
            System.out.println("The Google Drive client key is: " + clientid);
        }
        if (clientsecret == null) {
            clientsecret = JOptionPane.showInputDialog("Please enter the client secret from Google");
            System.out.println("The Google Drive client secret is: " + clientsecret);
        }
        GDriveFileProvider provider = vfslib.addProviderGoogleDrive(fsmanager, clientid, clientsecret, appname, redirecturi);
        if (provider == null) {
            System.err.println("Sorry, the Google Drive provider could not be activated, exiting");
            return;
        }

        String scheme = vfslib.getSchemeGoogleDrive();
        System.out.println("\nAdded Google Drive scheme \"" + scheme + '\"');

        if (token == null) {
            // Perform authorization to let application access a Google Drive account
            GoogleAuthorizationCodeFlow authflow = provider.getAuthFlow();

            // Open connection to Google Drive and get authorization URL to direct the user to
            final String authorizeurl = authflow.newAuthorizationUrl().setRedirectUri(redirecturi).build();
            if (authorizeurl == null) {
                System.err.println("Sorry, could not connect to drive.google.com, exiting");
                return;
            }

            // Open default web browser showing the received web address
            new Thread(new Runnable() {
                @Override
                public void run () {
                    try { Thread.sleep(3000); }
                    catch (Exception e) { }
                    JavaUtils.browse(authorizeurl);
                }
            }).start();
            System.out.println("If your browser did not open the Google Drive website, please go to " + authorizeurl);

            String code = JOptionPane.showInputDialog("Please enter the code from Google (please wait for browser)");
            System.out.println("The Google Drive code is: " + code);

            // Get final access token, could be stored permanently e.g. in database
            try {
                GoogleTokenResponse response = authflow.newTokenRequest(code).setRedirectUri(redirecturi).execute();
                GoogleCredential credential = new GoogleCredential().setFromTokenResponse(response);
                token = credential.getAccessToken();
            }
            catch (Exception e) { }
            if (token == null) {
                System.err.println("Sorry, could not get the Google Drive access token, exiting");
                return;
            }
            System.out.println("The Google Drive access token is: " + token);
            System.out.println("You can use this token for further testing, please specify as -token command line argument now");
        }

        // Pass access token over to VFSLib to access the Google Drive account
        FileSystemOptions options = new FileSystemOptions();
        GDriveFileSystemConfigBuilder builder = new GDriveFileSystemConfigBuilder();
        builder.setAccessToken(options, token);
        builder.setUseTrash(options, true);  // Allow deleted entries be restored

        // Setup proper account name, used as user name for Google Drive URLs (failsafe is "anonymous"):
        //    gdrive://[name]:[token]@drive.google.com
        String username = "johndoe";
        builder.setAccountDisplayName(options, username);


        // List a Google Drive folder
        String uri = scheme + "://" + username + "@drive.google.com";
        System.out.println("Listing child entries of " + uri + ":");
        try {
            FileObject fileobj = fsmanager.resolveFile(uri, options);
            if (fileobj.getType().equals(FileType.FOLDER)) {
                FileObject[] childs = fileobj.getChildren();
                for (FileObject next : childs) {
                    if (next.getType().equals(FileType.FOLDER)) {
                        System.out.println("    DIR:  " + String.valueOf(next));
                    }
                    else System.out.println("    FILE: " + String.valueOf(next));
                }
            }
            else System.out.println("    Entry " + uri + " is not a folder");
        }
        catch (Exception e) { e.printStackTrace(); }

        // The following code modifies files in the Google Drive file system.
        // Please uncomment the lines and adjust values for your needs.

        // Create a Google Drive folder
        String tempfolder = scheme + "://" + username + "@drive.google.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;
        }

        String content = "Hello world!";
        String encoding = "ISO-8859-1";

        // Upload a file to Google Drive. 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);
            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;
        }

        // Rename a Google Drive 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 Google Drive
        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 = new String(bostream.toByteArray(), 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;
        }

        // Remove a Google Drive 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);
            success = fileobj.delete();
            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>
     * -language [value]           Language "en" or "de"
     * -licensefile [file]         License file
     * -clientid [value]           Your client ID from Google &reg;
     * -clientsecret [value]       Your client secret from Google&reg;
     * -appname [value]            The identifier for your application
     * -redirecturi [value]        The redirect URI from Google&reg;
     * -token [value]              The access token (requires prior authorization)
     * </code>
     * <p/>
     * @param args Array of strings with console arguments
     * @since 1.6
     */
    public static void main (String[] args) {

        String language = null, clientid = null, clientsecret = null, redirecturi = null, token = null;
        String appname = null;
        File licensefile = 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("-language") && (i + 1) < args.length) language = args[++i];
                else if (args[i].equals("-licensefile") && (i + 1) < args.length) licensefile = new File(args[++i]);
                else if (args[i].equals("-clientid") && (i + 1) < args.length) clientid = args[++i];
                else if (args[i].equals("-clientsecret") && (i + 1) < args.length) clientsecret = args[++i];
                else if (args[i].equals("-redirecturi") && (i + 1) < args.length) redirecturi = args[++i];
                else if (args[i].equals("-token") && (i + 1) < args.length) token = args[++i];
                else if (args[i].equals("-appname") && (i + 1) < args.length) appname = args[++i];
            }

            new ExampleGoogleDrive(language, licensefile, clientid, clientsecret, appname, redirecturi, token);
            System.exit(0);
        }
        catch (Exception exc) {
            try { Thread.sleep(1000); }
            catch (Exception e) { }
            exc.printStackTrace();
        }
        System.exit(1);
    }
}
