package dhm.imageserver.transformer;

import dhm.imageserver.ImageTransformer;
import dhm.imageserver.util.ImageUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import javax.ws.rs.core.MultivaluedMap;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * An image transformer that scales the image to the specified width and/or height.
 * The width and height are retrieved from the parameters map with the keys 'w' and 'h'.
 * If both width and height are provided, the image is resized to the specified dimensions; if only one of
 * these parameters is provided, the unspecified parameter is computed to preserve the original
 * aspect ratio.
 */
public class ResizeTransformer implements ImageTransformer {
    private static Logger logger = LoggerFactory.getLogger(ResizeTransformer.class);

    public static final String PARAM_W = "w";
    public static final String PARAM_H = "h";

    @Override
    public byte[] transform(byte[] imgData, MultivaluedMap<String, String> params) {
        Integer width = getIntParam(PARAM_W, params);
        Integer height = getIntParam(PARAM_H, params);
        try {
            return (width != null || height != null) ? scale(imgData, width, height) : imgData;
        } catch (IOException e) {
            logger.error("Size transformation failed", e);
            return imgData;
        }
    }

    private Integer getIntParam(String key, MultivaluedMap<String,String> params) {
        String s = params.getFirst(key);
        if (s == null) {
            return null;
        }
        try {
            return Integer.valueOf(s);
        } catch (NumberFormatException e) {
            logger.warn("Ignoring invalid value for argument {0}. \"{1}\" is not a number.", key, s);
            return null;
        }
    }

    private byte[] scale(byte[] imgData, Integer newWidth, Integer newHeight) throws IOException {
        ImageReader ir = ImageUtil.imageReader(imgData);
        BufferedImage img = ir.read(0);

        ImageWriter iw = ImageIO.getImageWriter(ir);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        ImageOutputStream ios = null;
        try {
            ios = ImageIO.createImageOutputStream(bos);
            iw.setOutput(ios);
            resize(img,
                    newWidth != null ? newWidth : (int)((double)newHeight * (double) img.getWidth() / (double) img.getHeight()),
                    newHeight != null ? newHeight : (int) ((double) newWidth * (double) img.getHeight() / (double) img.getWidth()),
                iw);
        } finally {
            if (ios != null) {
                ios.close();
            }
        }
        return bos.toByteArray();
    }

    private void resize(BufferedImage img, int w, int h, ImageWriter iw) throws IOException {
        iw.write(ImageUtil.resize(img, w, h));
    }

}
