package workers;

import GUI.SOMGUI;
import javafx.concurrent.Task;
import model.SOMConfig;
import model.SOMEvent;
import model.SOMImage;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;

import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static java.nio.file.StandardWatchEventKinds.*;

public class FilesWorker {

    private static final SOMEvent event = SOMEvent.getInstance();

    public FilesWorker() {

    }

    /**
     * Returns true if a File is an image and returns false if not.
     *
     * @param path
     * @return
     */
    public static boolean isImage(Path path) {
        File f = path.toFile();
        String mimeType = new MimetypesFileTypeMap().getContentType(f);
        String type = mimeType.split("/")[0];
        return type.equals("image");
    }

    /**
     * Returns the Path to the Source (direct from Camera) images directory, or
     * if a File of one of the other Folders is given the Source File' Path
     *
     * @return
     */
    public static File getSourcePath() {
        File src = new File(SOMConfig.PICTURES_PATH.toString() + "/src");

        // TODO catch possible fail
        src.mkdirs();
        return src;
    }

    /**
     * Returns the Path to the Preview Images (Full Screen) images directory, or
     * if a File of one of the other Folders is given the Preview File' Path
     *
     * @return
     */
    public static File getPreviewPath() {
        File prv = new File(SOMConfig.PICTURES_PATH.toString() + "/prv");

        // TODO catch possible fail
        prv.mkdirs();
        return prv;
    }

    /**
     * Returns the Path to the Thumbnail (Gallery) images directory, or if a
     * File of one of the other Folders is given the Thumbnail File' Path
     *
     * @return
     */
    public static File getThumbPath() {
        File tmb = new File(SOMConfig.PICTURES_PATH.toString() + "/tmb");

        // TODO catch possible fail
        tmb.mkdirs();
        return tmb;
    }

    /**
     * Returns the Path to the Final images directory, or if a File of one of
     * the other Folders is given the Final Files' Path
     *
     * @return
     */
    public static File getFinalPath() {
        File fnl = new File(SOMConfig.PICTURES_PATH.toString() + "/fnl");

        // TODO catch possible fail
        fnl.mkdirs();
        return fnl;
    }

    /**
     * Returns the Path to the Temporary (pre-source) images directory, or if a
     * File of one of the other Folders is given the Thumbnail File' Path
     *
     * @return
     */
    public static File getTempPath() {
        File tmp = new File(SOMConfig.PICTURES_PATH.toString() + "/tmp");

        // TODO catch possible fail
        tmp.mkdirs();
        return tmp;
    }

    private static boolean internet() {

        Socket sock = new Socket();
        InetSocketAddress socketAddress = new InetSocketAddress("google.com", 80);

        try {
            sock.connect(socketAddress, 3000);
            System.out.println("Internet available");
            return true;
        } catch (IOException e) {
            System.out.println("Internet NOT available");
            return false;
        } finally {
            try {
                sock.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void upload() {
        try {

            // Get all Files in the image Folder
            List<File> images = allFilesForUpload();
            // Lists for either dirs or pictures
            List<File> dirs = new ArrayList<File>();
            List<File> pics = new ArrayList<File>();

            // Check for internet connection
            if (internet()) {

                // Count Files and Dirs

                // DIRS
                String[] splits = SOMConfig.getEventPicDir().split("/");
                String currentdir = "";

                for (String split : splits) {
                    currentdir = currentdir + "/" + split;
                    dirs.add(new File(currentdir));

                    System.out.println("DIR added:" + currentdir);
                }

                // FILES
                for (int i = 0; i < images.size(); i++) {

                    // get the current file
                    File file = images.get(i);

                    System.out.println(file);

                    // get pics
                    if (file.isFile()
                            && file.toString().contains("src")) {
                        pics.add(file);
                    }
                }

//                REPORT TO CONSOLE
                System.out.println(dirs.size() + " Directories");
                System.out.println(pics.size() + " Pics");

                int uploadeddirs = 0;
                int uploadedfiles = 0;

                // GET THE UI
                SOMGUI.changetoupload();

                // UPLOAD DIRECTORIES
                List<File> limitdirs = new ArrayList<File>();

                for (File dir : dirs) {

                    uploadeddirs++;

                    System.out.println("Queueing Directories " + dir.toString());
                    limitdirs.add(dir);
                    if (limitdirs.size() >= SOMConfig.PARALLEL_UPLOADS
                            // Upload even if limit is not reached
                            || (dirs.size() - uploadeddirs) < SOMConfig.PARALLEL_UPLOADS) {

                        System.out.println("Launch Dir upload");
                        uploadDir(limitdirs);

                        System.out.println("Clear Queue");
                        limitdirs.clear();
                    }
                }

                // UPLOAD FILES
                List<File> limitpics = new ArrayList<File>();
                for (File pic : pics
                        ) {

                    uploadedfiles++;

                    System.out.println("Queueing File " + pic.toString());
                    limitpics.add(pic);

                    if (limitpics.size() >= SOMConfig.PARALLEL_UPLOADS
                            // Upload even if limit is not reached
                            || (pics.size() - uploadedfiles) < SOMConfig.PARALLEL_UPLOADS) {

                        System.out.println("Launch File upload");
                        uploadFile(limitpics);

                        System.out.println("Clear File Queue");
                        limitpics.clear();
                    }
                }

                SOMGUI.changetopicture();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void uploadFile(final List<File> files) throws Exception {
        final Thread[] threads = new Thread[files.size()];

        for (int i = 0; i < threads.length; i++) {

            System.out.println("LAUNCH Upload Process: " + i);

            final File file = files.get(i);
            final String target = "https://cloud.selfomat.de/remote.php/webdav/" + SOMConfig.getEventPicDir() + "/" + file.getName();

            System.out.println(file.toString());
            System.out.println("----------->");
            System.out.println(target);
            System.out.println("");
            System.out.println("");

            final int pictureCounter = i + 1;
            Task<Boolean> task = new Task<Boolean>() {

                @Override
                protected Boolean call() throws Exception {
                    // build the upload process
                    // TODO remove -k option (this option skips certificate verification)
                    ProcessBuilder builder = new ProcessBuilder(
                            "bash",
                            "SELFOMAT/sh/upload.sh",
                            file.toString(),
                            target,
                            SOMConfig.CLOUD_CREDENTIALS
                    );

                    // make the process traceable
                    builder.redirectErrorStream(true);
                    // launch upload process
                    Process process = builder.start();

                    // wait for the process to end
                    if (process.waitFor() == 0) {
                        // process ended successful
                        System.out.println("file uploaded: " + file);

                        // TODO get the length of all threads
                        // TODO bind to UI
                        updateProgress(pictureCounter, threads.length);

                        return true;

                    } else {
                        // process ended failing
                        // TODO Catch failed uploads
                        System.out.println("process ended failing " + file);
                        Thread.sleep(1000);
                        return false;
                    }
                }
            };

            SOMGUI.uploadCtl.pictureProgressBar.progressProperty().bind(task.progressProperty());

            threads[i] = new Thread(task);
            threads[i].setDaemon(true);
            threads[i].start();
        }
        int i = 0;
        while (i < threads.length) {
            threads[i].join();
            i++;
        }
//TODO Catch fails
    }

    private static void uploadDir(final List<File> dirs) throws Exception {

        final Thread[] threads = new Thread[dirs.size()];
        for (int i = 0; i < threads.length; i++) {

            System.out.println("launch mkdir Process: " + i);

            final File dir = dirs.get(i);
            final int directoryCounter = i + 1;
            Task<Boolean> task = new Task<Boolean>() {

                @Override
                protected Boolean call() throws Exception {
                    // build the Upload process with curl
                    // TODO remove -k option (this option skips certificate verification)
                    ProcessBuilder builder = new ProcessBuilder("curl",
                            "-u",
                            "upload:geheim",
                            "-X",
                            "MKCOL",
                            "https://cloud.selfom.at/remote.php/webdav/" + SOMConfig.getEventPicDir(),
//                            dir.toString().replace("/src", ""),
                            "-k");

                    // make the process traceable
                    builder.redirectErrorStream(true);

                    // launch upload process
                    Process process = builder.start();

                    // wait for the process to end
                    if (process.waitFor() == 0) {
                        // Process ended successful
                        System.out.println("directory made: " + dir);

                        //Set upload Progress
                        // TODO get the length of all threads
                        // TODO bind to UI
                        updateProgress(directoryCounter, threads.length);
                        return true;

                    } else {
                        // Process ended failing
                        System.out.println("process ended failing " + dir);
                        return false;
                    }
                }
            };

            SOMGUI.uploadCtl.folderProgressBar.progressProperty().bind(task.progressProperty());

            // Start the Task
            threads[i] = new Thread(task);
            threads[i].setDaemon(true);
            threads[i].start();
        } // End of loop

        // Join the tasks
        int i = 0;
        while (i < threads.length) {
            threads[i].join();
            i++;
        }

        //TODO Catch upload fails
    }

    private static List<File> allFilesForUpload() {

        File dir = SOMConfig.PICTURES_PATH;

        Collection filesAndDirs = FileUtils.listFilesAndDirs(dir, TrueFileFilter.INSTANCE, DirectoryFileFilter.DIRECTORY);

        return new ArrayList<File>(filesAndDirs);
    }

    // TODO static??
    public static SOMImage waitForImageAndCapture() {
        //Behandelt
        try {

            // Set the Source Path to be watched
            Path dir = getSourcePath().toPath();

            // REGISTER WATCH SERVICE
            WatchService watcher = FileSystems.getDefault().newWatchService();
            dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);

            System.out.println("Watch Service registered for dir: "
                    + dir.getFileName());

            // TRIGGER THE CAPTURE
            System.out.println("START CAPTURE THREAD");
            SOMImage newImage = CaptureWorker.now();

            while (true) {

                WatchKey key;

                try {
                    key = watcher.take();
                } catch (InterruptedException ex) {
                    return newImage;
                }

                for (WatchEvent<?> event : key.pollEvents()) {
                    WatchEvent.Kind<?> kind = event.kind();

                    @SuppressWarnings("unchecked")
                    WatchEvent<Path> ev = (WatchEvent<Path>) event;
                    Path fileName = ev.context();

                    System.out.println(kind.name() + ": " + fileName);
                    // check for a file being created
                    if (
                        // Make sure there is an Image created
                            kind == ENTRY_CREATE
                                    // Check that filename is the same as expected image
                                    && fileName.toString().equals(
                                    newImage.getSrc().getName())) {

                        //Set the Progress Indicator to 0.5
                        SOMGUI.captureCtl.pictureLoadIndicator.setProgress(0.5);

                        // START THE "PICTURE HAS ARRIVED" ACTION
                        System.out.println("The image is there!!!");

                        // ADD THE IMAGE TO THE EVENT
                        FilesWorker.event.addImage(newImage);

                        // Set last Image
                        System.out.println("Set the taken Picture to UI");
                        FilesWorker.event.createAndSetImage(FilesWorker.event.getLastImage());

                        // Hide the Progress Indicator
                        SOMGUI.captureCtl.pictureLoadIndicator.setVisible(false);
                        // Set back the Progress Indicator
                        SOMGUI.captureCtl.pictureLoadIndicator.setProgress(-1);

                        //Close Watcher
                        watcher.close();
                    } else if (
                        // Make sure there is an Image created
                            kind == ENTRY_CREATE
                                    // Check that filename is the same as expected image
                                    && !fileName.toString().equals(
                                    newImage.getSrc().getName())) {
                        // TODO Catch wrong pictures, only for testing
                        // Hide the Progress Indicator
                        SOMGUI.captureCtl.pictureLoadIndicator.setVisible(false);
                        // Set back the Progress Indicator
                        SOMGUI.captureCtl.pictureLoadIndicator.setProgress(-1);

                        //Close Watcher
                        watcher.close();
                    }

                }


                boolean valid = key.reset();
                if (!valid) {
                    break;
                }
            }

            // return the new image
            newImage.updateImageState();
            return newImage;

        } catch (IOException ex) {
            System.err.println(ex);
            return null;
        }

    }
}
