package workers;

import javafx.concurrent.Task;
import javafx.embed.swing.SwingFXUtils;
import model.SOMConfig;
import model.SOMImage;
import model.SOMLayout;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.io.File;
import java.io.IOException;

public class ScaleWorker extends Thread {

    public static final int SOURCE = 0;
    public static final int PREVIEW = 1;
    public static final int THUMBNAIL = 2;

    SOMConfig config = SOMConfig.getInstance();

    public static SOMImage scale(SOMImage somImage1, final int type) {

        final SOMImage somImage = somImage1;
        Task task = new Task() {
            @Override
            protected SOMImage call() throws Exception {
                // Do the scale work
                return prepareandStart(somImage, type);
            }
        };

        // open Thread
        Thread thread = new Thread(task);
        // start Thread
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //return the somImage1
        return somImage1;
    }

    // Overload of scale() that lets you enter Arrays of Pictures
    public static SOMImage[] scale(SOMImage[] somImages, int type) {
        // loop through Array of SOMImages
        for (SOMImage image : somImages) {
            //update the image
            image = scale(image, type);
        }
        // return the imagearray
        return somImages;
    }

    private static SOMImage prepareandStart(SOMImage somImage, int type) {


        // the image before scaling
//        Image sourceImage;
        // the image after scaling
//        Image resultImage;

        // type of image to be used to scale down
        int source = SOURCE;

        // check for scaled versions of the image
        somImage.updateImageState();

        // preview
        if (type == PREVIEW) {
            // check if the requested image already exists
            if (somImage.isPrvthere())
                // exit the scaler
                return somImage;

            // The requested image does not exist,
        }

        // thumbnail
        if (type == THUMBNAIL) {
            // check if the requested image already exists
            if (somImage.isTmbthere()) {
                return somImage;
            }
            // The requested image does not exist,
            // check for Preview to scale faster
            if (somImage.isPrvthere()) {
                source = PREVIEW;
            }

        }

        // In any other case we need to scale the
        // full sized image, which is default

        somImage = generateScaledImage(somImage, type, source);

        return somImage;
    }

    private static SOMImage generateScaledImage(SOMImage somImage, int target, int source) {
        // the source image size
        double sourceWidth = 0;
        double sourceHeight = 0;

        // the scaled sources width and height
        // before cropping
        int scaledSourceHeight;
        int scaledSourceWidth;

        // the later image size
        int targetWidth = 0;
        int targetHeight = 0;

        // Aspect ratio before cropping
        double sourceAspect;
        // The ImageViews' cropped aspect ratio
        double targetAspect;

        // Source File
        File sourceFile = somImage.getSrc();
        // Target File
        File targetFile = null;

        // ImageIcons
        ImageIcon sourceImageIcon;
        ImageIcon targetImageIcon;

        // JAVAFX Images
        javafx.scene.image.Image targetImage;

        // Buffered Images
        BufferedImage scaledSourceBufferedImage;
        BufferedImage croppedBufferedImage;

        // Graphics for Buffered Images
        Graphics2D scaledSourceGraphics;
        Graphics croppedGraphics;

        // get the target IMAGE SIZES
        // and the target FILE
        switch (target) {

            // for the preview
            case PREVIEW:
                targetWidth = SOMLayout.screenWidth;
                targetHeight = SOMLayout.screenHeight;

                targetFile = somImage.getPrv();

                // for the Thumbnail
                break;
            case THUMBNAIL:
                targetWidth = SOMLayout.getColumn(1);
                targetHeight = SOMLayout.getRow(1);

                targetFile = somImage.getTmb();
                break;
        }

        // get the SOURCE FILE
        switch (source) {
            case SOURCE:
                sourceFile = somImage.getSrc();
                break;
            case PREVIEW:
                sourceFile = somImage.getPrv();
                break;
        }

        switch (SOMConfig.SCALING_ALGORITHM) {

            // GRAPHICS 2D ----------------------------------------------------------
            case SOMConfig.SCALING_GRAPHICS_2D: {
                // Generate an ImageIcon from the Source File
                sourceImageIcon = new ImageIcon(sourceFile.getPath());

                // the sources original width and height
                sourceWidth = sourceImageIcon.getIconWidth();
                sourceHeight = sourceImageIcon.getIconHeight();

                // Calculate the aspect ratio before cropping
                sourceAspect = (sourceWidth) / (sourceHeight);

                // Calculate the ImageViews' cropped aspect ratio
                targetAspect = ((double) targetWidth) / ((double) targetHeight);

                // if picture is higher than frame
                if (sourceAspect < targetAspect) {
                    scaledSourceHeight = (int) Math.ceil(targetWidth / sourceAspect);
                    scaledSourceWidth = targetWidth;
                }

                // if Picture is narrower (or equal)
                else {
                    scaledSourceWidth = (int) Math.ceil(targetHeight * sourceAspect);
                    scaledSourceHeight = targetHeight;
                }

                // Probably way faster than ImageIcon
                System.out.println("Scale with Graphics2D:");

                // Print the File Path
                System.out.println(sourceFile.toString());

                // Print the Sizes
                System.out.println("Size now: " + sourceWidth + "px X " + sourceHeight + "px");
                System.out.println("Size between: " + scaledSourceWidth + "px X " + scaledSourceHeight + "px");
                System.out.println("Size after: " + targetWidth + "px X " + targetHeight + "px");

                // SCALE -----------------------------------------------------------
                // THE -------------------------------------------------------------
                // SOURCE ----------------------------------------------------------

                // Get the ImageIcon
                Image sourceImageIconImage = sourceImageIcon.getImage();
                System.out.println("Got the ImageIcon");

                // Open a buffered Image in the specified size
                // (with no data at this point --> black image)
                scaledSourceBufferedImage = new BufferedImage(
                        scaledSourceWidth,
                        scaledSourceHeight,
                        BufferedImage.TYPE_INT_RGB
                );
                System.out.println("Got the BufferedImage");

                // Get the Graphics2D
                scaledSourceGraphics = scaledSourceBufferedImage.createGraphics();
                System.out.println("Got the Graphics2D");

                // Set the Rendering hints
                scaledSourceGraphics.setRenderingHint(
                        RenderingHints.KEY_INTERPOLATION,
                        RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR
                );
                System.out.println("Rendering Hints Set");

                // Actually Scale the Image
                scaledSourceGraphics.drawImage(sourceImageIconImage, 0, 0, scaledSourceWidth, scaledSourceHeight, null);
                System.out.println("IMAGE SCALED");

                scaledSourceGraphics.dispose();
                // System Resources Released
                // TODO Can we use this for perormance??

                System.out.println("Got the target Image Icon");


                // Calculate the Offsets
                int wOffset;
                int hOffset;

                // CROP ------------------------------------------------------------
                // THE -------------------------------------------------------------
                // IMAGE -----------------------------------------------------------

                if (sourceAspect > targetAspect) {
                    hOffset = 0;
                    wOffset = (scaledSourceWidth - targetWidth) / 2;
                } else {
                    hOffset = (scaledSourceHeight - targetHeight) / 2;
                    wOffset = 0;
                }

                System.out.println("Got the Offset for cropping");

                System.out.println("Get the Crop Image Filter");

                CropImageFilter cropimgf = new CropImageFilter(
                        // Crop by moving the image half the way
                        // it is wider to the left
                        wOffset,
                        // Crop by moving the image half the way
                        // it is higher to the top
                        hOffset,

                        targetWidth,
                        targetHeight
                );

                System.out.println("Ready");

                System.out.println("Get the Filtered image Source");

                FilteredImageSource producer = new FilteredImageSource(
                        scaledSourceBufferedImage.getSource(),
                        cropimgf
                );

                System.out.println("Ready");

                System.out.println("Get the target Image");
                Image image = Toolkit.getDefaultToolkit().createImage(
                        producer
                );
                System.out.println("Ready");

                System.out.println("Get the target image Icon");
                targetImageIcon = new ImageIcon( // TODO Veeeery slow on raspberry
                        image
                );
                System.out.println("Ready");
                // TODO Check if there is something in the Image Icon on RPi
                System.out.println("IMAGE CROPPED");

                // save the smaller image
                croppedBufferedImage = new BufferedImage(
                        targetWidth,
                        targetHeight,
                        BufferedImage.TYPE_INT_RGB
                );

                System.out.println("Got the BufferedImage after CROPPING");
                croppedGraphics = croppedBufferedImage.getGraphics();
                System.out.println("Got the Graphics");

                croppedGraphics.drawImage(targetImageIcon.getImage(), 0, 0, null);

                System.out.println("Graphics drawn");

                // WRITE ---------------------------------------------------------------
                // THE -----------------------------------------------------------------
                // IMAGE ---------------------------------------------------------------

                System.out.println("Writing Thumb");

                System.out.println("Trying to write image to " + targetFile.toString());
                System.out.println(croppedBufferedImage.toString());
                // TODO Breaking up at this point on the RPi --> Look at older ScaleWorker

                writeJPEG(targetFile, croppedBufferedImage);

                System.out.println("Image Writer returned: "
//                        + iw
                );


                // UPDATE ---------------------------------------------------------------
                // IMAGE ----------------------------------------------------------------
                // STATE ----------------------------------------------------------------

                somImage.updateImageState();

                System.out.println("Image State Updated");

//                Clean up
                croppedGraphics.dispose();
                scaledSourceGraphics.dispose();

                image.flush();
                sourceImageIconImage.flush();

                System.gc();

                break;
            }
            case SOMConfig.SCALING_IMAGE_ICON: {
//                // IMAGE ICON ----------------------------------------------------------
//                // Generate an ImageIcon from the Source File
//                sourceImage = new javafx.scene.image.Image(sourceFile.toString());
//                // the sources original width and height
//                sourceWidth = sourceImage.getWidth();
//                sourceHeight = sourceImage.getHeight();
//
//                // Calculate the aspect ratio before cropping
//                sourceAspect = (sourceWidth) / (sourceHeight);
//                // Calculate the ImageViews' cropped aspect ratio
//                targetAspect = ((double) targetWidth) / ((double) targetHeight);
//
//                // if picture is higher than frame
//                if (sourceAspect < targetAspect) {
//                    scaledSourceHeight = (int) Math.ceil(targetWidth / sourceAspect);
//                    scaledSourceWidth = targetWidth;
//                }
//
//                // if Picture is narrower (or equal)
//                else {
//                    scaledSourceWidth = (int) Math.ceil(targetHeight * sourceAspect);
//                    scaledSourceHeight = targetHeight;
//                }
//
//                //Way too slow for the RPi
//                System.out.println("Scale with ImageIcon");
//
//                // Scale to right size,
//                // no cropping
//                targetImageIcon = new ImageIcon(
//                        sourceImageIcon.getImage().getScaledInstance(
//                                scaledSourceWidth, scaledSourceHeight,
//                                Image.SCALE_FAST
//                        ));
//
//
//                // Calculate the Offsets
//                int wOffset;
//                int hOffset;
//
//                // ----------
//                // Crop Image
//                if (sourceAspect > targetAspect) {
//                    hOffset = 0;
//                    wOffset = (scaledSourceWidth - targetWidth) / 2;
//                } else {
//                    hOffset = (scaledSourceHeight - targetHeight) / 2;
//                    wOffset = 0;
//                }
//
//                System.out.println("Got the Offset for cropping");
//
//                System.out.println("Get the Crop Image Filter");
//                CropImageFilter cropimgf = new CropImageFilter(
//                        // Crop by moving the image half the way
//                        // it is wider to the left
//                        wOffset,
//                        // Crop by moving the image half the way
//                        // it is higher to the top
//                        hOffset,
//
//                        targetWidth,
//                        targetHeight
//                );
//                System.out.println("Ready");
//
//                System.out.println("Get the Filtered image Source");
//                FilteredImageSource producer = new FilteredImageSource(
//                        sourceImageIcon.getImage().getSource(),
//                        cropimgf
//                );
//                System.out.println("Ready");
//
//                System.out.println("Get the target Image");
//                Image image = Toolkit.getDefaultToolkit().createImage(
//                        producer
//                );
//                System.out.println("Ready");
//
//                System.out.println("Get the target image Icon");
//                targetImageIcon = new ImageIcon( // TODO Veeeery slow on raspberry
//                        image
//                );
//                System.out.println("Ready");
//                // TODO Check if there is something in the Image Icon on RPi
//                System.out.println("IMAGE CROPPED");
//
//                // save the smaller image
//                BufferedImage bufferedImage = new BufferedImage(
//                        targetWidth,
//                        targetHeight,
//                        BufferedImage.TYPE_INT_RGB
//                );
//
//                System.out.println("Got the BufferedImage after CROPPING");
//                croppedGraphics = bufferedImage.getGraphics();
//                System.out.println("Got the Graphics");
//
//                croppedGraphics.drawImage(targetImageIcon.getImage(), 0, 0, null);
//
//                System.out.println("Graphics drawn");
//
//                System.out.println("Writing Thumb");
//
//                System.out.println("Trying to write image to " + targetFile.toString());
//                System.out.println(bufferedImage.toString());
//                // TODO Breaking up at this point on the RPi --> Look at older ScaleWorker
//
//                writeJPEG(targetFile, bufferedImage);
//
//                System.out.println("Image Writer returned: "
////                        + iw
//                );
//                somImage.updateImageState();
//
//                System.out.println("Image State Updated");
//
//                somImage.updateImageState();
                break;
            }
            // JAVAFX ----------------------------------------------------------
            case SOMConfig.SCALING_JAVAFX: {
                // Scale with javafx
                // FIXME breaks up
                targetImage = new javafx.scene.image.Image(sourceFile.toString(), targetWidth, targetHeight, true, false, true);

                BufferedImage bi = SwingFXUtils.fromFXImage(targetImage, null);
                writeJPEG(targetFile, bi);

                somImage.updateImageState();

                System.out.println("Image State Updated");

                somImage.updateImageState();
                break;
            }
        }


        return somImage;
    }

    private static void writeJPEG(File targetFile, BufferedImage bi) {
        try {
            boolean iw = ImageIO.write(bi, "JPEG", targetFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
