// Path: src/main/java/org/kontalk/misc/Callback.java
// public final class Callback<V> {
//     private static final Logger LOGGER = Logger.getLogger(Callback.class.getName());
// 
//     public final V value;
//     public final Optional<Exception> exception;
// 
//     public Callback() {
//         this.value = null;
//         this.exception = Optional.empty();
//     }
// 
//     public Callback(V response) {
//         this.value = response;
//         this.exception = Optional.empty();
//     }
// 
//     public Callback(Exception ex) {
//         this.value = null;
//         this.exception = Optional.of(ex);
//     }
// 
//     public interface Handler<V> {
//         void handle(Callback<V> callback);
//     }
// 
//     public static class Synchronizer {
//         private final CountDownLatch mLatch = new CountDownLatch(1);
// 
//         public void sync() {
//             mLatch.countDown();
//         }
// 
//         public void waitForSync() {
//             boolean succ = false;
//             try {
//                 succ = mLatch.await(5, TimeUnit.SECONDS);
//             } catch (InterruptedException ex) {
//                 LOGGER.log(Level.WARNING, "interrupted", ex);
//             }
//             if (!succ) {
//                 LOGGER.warning("await failed, timeout reached");
//             }
//         }
//     }
// }


import javax.imageio.ImageIO;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.tika.mime.MimeType;
import org.apache.tika.mime.MimeTypeException;
import org.apache.tika.mime.MimeTypes;
import org.kontalk.misc.Callback;
            return new byte[0];
        }
        if (!succ) {
            LOGGER.warning("no image writer found, format: "+format);
        }
        return out.toByteArray();
    }

    /**
     * Scale image down to max pixels preserving ratio.
     * Blocking
     */
    public static BufferedImage scale(BufferedImage image, int maxPixels) {
        int iw = image.getWidth();
        int ih = image.getHeight();

        double scale = Math.sqrt(maxPixels / (iw * ih * 1.0));

        return toBufferedImage(scaleImage(image, (int) (iw * scale), (int) (ih * scale)));
    }

    /**
     * Scale image down to minimum width/height, preserving ratio.
     * Blocking.
     */
    public static BufferedImage scale(Image image, int width, int height) {
        return toBufferedImage(scaleAsync(image, width, height));
    }

    private static BufferedImage toBufferedImage(Image image) {
// Path: src/main/java/org/kontalk/misc/Callback.java
// public final class Callback<V> {
//     private static final Logger LOGGER = Logger.getLogger(Callback.class.getName());
// 
//     public final V value;
//     public final Optional<Exception> exception;
// 
//     public Callback() {
//         this.value = null;
//         this.exception = Optional.empty();
//     }
// 
//     public Callback(V response) {
//         this.value = response;
//         this.exception = Optional.empty();
//     }
// 
//     public Callback(Exception ex) {
//         this.value = null;
//         this.exception = Optional.of(ex);
//     }
// 
//     public interface Handler<V> {
//         void handle(Callback<V> callback);
//     }
// 
//     public static class Synchronizer {
//         private final CountDownLatch mLatch = new CountDownLatch(1);
// 
//         public void sync() {
//             mLatch.countDown();
//         }
// 
//         public void waitForSync() {
//             boolean succ = false;
//             try {
//                 succ = mLatch.await(5, TimeUnit.SECONDS);
//             } catch (InterruptedException ex) {
//                 LOGGER.log(Level.WARNING, "interrupted", ex);
//             }
//             if (!succ) {
//                 LOGGER.warning("await failed, timeout reached");
//             }
//         }
//     }
// }


// Path: src/main/java/org/kontalk/util/MediaUtils.java
import javax.imageio.ImageIO;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.tika.mime.MimeType;
import org.apache.tika.mime.MimeTypeException;
import org.apache.tika.mime.MimeTypes;
import org.kontalk.misc.Callback;

            return new byte[0];
        }
        if (!succ) {
            LOGGER.warning("no image writer found, format: "+format);
        }
        return out.toByteArray();
    }

    /**
     * Scale image down to max pixels preserving ratio.
     * Blocking
     */
    public static BufferedImage scale(BufferedImage image, int maxPixels) {
        int iw = image.getWidth();
        int ih = image.getHeight();

        double scale = Math.sqrt(maxPixels / (iw * ih * 1.0));

        return toBufferedImage(scaleImage(image, (int) (iw * scale), (int) (ih * scale)));
    }

    /**
     * Scale image down to minimum width/height, preserving ratio.
     * Blocking.
     */
    public static BufferedImage scale(Image image, int width, int height) {
        return toBufferedImage(scaleAsync(image, width, height));
    }

    private static BufferedImage toBufferedImage(Image image) {
        final Callback.Synchronizer syncer = new Callback.Synchronizer();