package com.thousandmemories.photon.core;

import com.yammer.dropwizard.jersey.caching.CacheControl;
import com.yammer.dropwizard.logging.Log;
import com.yammer.metrics.Metrics;
import com.yammer.metrics.annotation.Timed;
import com.yammer.metrics.core.Timer;
import com.yammer.metrics.core.TimerContext;

import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URLConnection;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;


@Path("/{name}")
public class PhotoResource {
    private static final Log LOG = Log.forClass(PhotoResource.class);
    private static final Timer readTimer = Metrics.newTimer(PhotoResource.class, "read", TimeUnit.MILLISECONDS, TimeUnit.SECONDS);


    private final PhotoProvider photoProvider;

    public PhotoResource(PhotoProvider photoProvider) {
        this.photoProvider = photoProvider;
    }

    @GET
    @Timed
    @CacheControl(immutable = true)
    public Response getPhoto(@PathParam("name") String name,
                             @MatrixParam("w") Integer width,
                             @MatrixParam("r") RotationParam rotateAngle,
                             @MatrixParam("c") RectangleParam crop) throws Exception {
        InputStream resultStream;

        InputStream imageStream;
        try {
            imageStream = new BufferedInputStream(photoProvider.getPhotoInputStream(name));
        } catch (FileNotFoundException fnfe) {
            throw new WebApplicationException(Response.Status.NOT_FOUND);
        }

        String mimeType = URLConnection.guessContentTypeFromStream(imageStream);
        if (mimeType == null) {
            throw new WebApplicationException(501); // Not implemented
        }

        if (width != null || rotateAngle != null || crop != null) {
            BufferedImage image;
            TimerContext readContext = readTimer.time();
            try {
                image = ImageIO.read(imageStream);
            } finally {
                imageStream.close();
                readContext.stop();
            }

            if (crop != null) {
                image = com.thousandmemories.photon.core.Processor.crop(image, crop.get());
            }

            if (rotateAngle != null) {
                image = com.thousandmemories.photon.core.Processor.rotate(image, rotateAngle.get());
            }

            if (width != null) {
                image = com.thousandmemories.photon.core.Processor.fitToWidth(image, width);
            }

            Iterator<ImageWriter> i = ImageIO.getImageWritersByMIMEType(mimeType);
            if (!i.hasNext()) {
                mimeType = "image/jpeg";
                i = ImageIO.getImageWritersByMIMEType(mimeType);
            }

            ImageWriter writer = i.next();

            ByteArrayOutputStream os = new ByteArrayOutputStream();
            writer.setOutput(new MemoryCacheImageOutputStream(os));
            writer.write(image);
            image.flush();
            image = null;
            resultStream = new ByteArrayInputStream(os.toByteArray());
        } else {
            resultStream = imageStream;
        }

        return Response.
                ok(resultStream, mimeType).
                build();

    }
}
