/*
 * #%L
 * SCIFIO library for reading and converting scientific file formats.
 * %%
 * Copyright (C) 2011 - 2020 SCIFIO developers.
 * %%
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * #L%
 */

package io.scif.services;

import io.scif.codec.JPEG2000CodecOptions;
import io.scif.media.imageio.plugins.jpeg2000.J2KImageReadParam;
import io.scif.media.imageio.plugins.jpeg2000.J2KImageWriteParam;
import io.scif.media.imageioimpl.plugins.jpeg2000.J2KImageReader;
import io.scif.media.imageioimpl.plugins.jpeg2000.J2KImageReaderSpi;
import io.scif.media.imageioimpl.plugins.jpeg2000.J2KImageWriter;
import io.scif.media.imageioimpl.plugins.jpeg2000.J2KImageWriterSpi;

import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.spi.IIORegistry;
import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.ImageOutputStream;
import javax.imageio.stream.MemoryCacheImageInputStream;

import org.scijava.plugin.Plugin;
import org.scijava.service.AbstractService;
import org.scijava.service.Service;

/**
 * Implementation of JAIIIOService for reading and writing JPEG-2000 data.
 */
@Plugin(type = Service.class)
public class JAIIIOServiceImpl extends AbstractService implements
	JAIIIOService
{

	// -- JAIIIOService API methods --

	@Override
	public void writeImage(final OutputStream out, final BufferedImage img,
		final JPEG2000CodecOptions options) throws IOException, ServiceException
	{
		final ImageOutputStream ios = ImageIO.createImageOutputStream(out);

		final IIORegistry registry = IIORegistry.getDefaultInstance();
		final Iterator<J2KImageWriterSpi> iter = ServiceRegistry.lookupProviders(
			J2KImageWriterSpi.class);
		registry.registerServiceProviders(iter);
		final J2KImageWriterSpi spi = registry.getServiceProviderByClass(
			J2KImageWriterSpi.class);
		final J2KImageWriter writer = new J2KImageWriter(spi);
		writer.setOutput(ios);

		final String filter = options.lossless ? J2KImageWriteParam.FILTER_53
			: J2KImageWriteParam.FILTER_97;

		final IIOImage iioImage = new IIOImage(img, null, null);
		final J2KImageWriteParam param = (J2KImageWriteParam) writer
			.getDefaultWriteParam();
		param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
		param.setCompressionType("JPEG2000");
		param.setLossless(options.lossless);
		param.setFilter(filter);
		param.setCodeBlockSize(options.codeBlockSize);
		param.setEncodingRate(options.quality);
		if (options.tileWidth > 0 && options.tileHeight > 0) {
			param.setTiling(options.tileWidth, options.tileHeight,
				options.tileGridXOffset, options.tileGridYOffset);
		}
		if (options.numDecompositionLevels != null) {
			param.setNumDecompositionLevels(options.numDecompositionLevels
				.intValue());
		}
		writer.write(null, iioImage, param);
		ios.close();
	}

	/**
	 * @deprecated use JAIIIOService#writeImage(OutputStream, BufferedImage,
	 *             JPEG2000CodecOptions)
	 */
	@Override
	@Deprecated
	public void writeImage(final OutputStream out, final BufferedImage img,
		final boolean lossless, final int[] codeBlockSize, final double quality)
		throws IOException, ServiceException
	{
		final JPEG2000CodecOptions options = JPEG2000CodecOptions
			.getDefaultOptions();
		options.lossless = lossless;
		options.codeBlockSize = codeBlockSize;
		options.quality = quality;
		writeImage(out, img, options);
	}

	@Override
	public BufferedImage readImage(final InputStream in,
		final JPEG2000CodecOptions options) throws IOException, ServiceException
	{
		final J2KImageReader reader = getReader();
		final MemoryCacheImageInputStream mciis = new MemoryCacheImageInputStream(
			in);
		reader.setInput(mciis, false, true);
		final J2KImageReadParam param = (J2KImageReadParam) reader
			.getDefaultReadParam();
		if (options.resolution != null) {
			param.setResolution(options.resolution.intValue());
		}
		return reader.read(0, param);
	}

	@Override
	public BufferedImage readImage(final InputStream in) throws IOException,
		ServiceException
	{
		return readImage(in, JPEG2000CodecOptions.getDefaultOptions());
	}

	@Override
	public Raster readRaster(final InputStream in,
		final JPEG2000CodecOptions options) throws IOException, ServiceException
	{
		final J2KImageReader reader = getReader();
		final MemoryCacheImageInputStream mciis = new MemoryCacheImageInputStream(
			in);
		reader.setInput(mciis, false, true);
		final J2KImageReadParam param = (J2KImageReadParam) reader
			.getDefaultReadParam();
		if (options.resolution != null) {
			param.setResolution(options.resolution.intValue());
		}
		return reader.readRaster(0, param);
	}

	@Override
	public Raster readRaster(final InputStream in) throws IOException,
		ServiceException
	{
		return readRaster(in, JPEG2000CodecOptions.getDefaultOptions());
	}

	/** Set up the JPEG-2000 image reader. */
	private J2KImageReader getReader() {
		final IIORegistry registry = IIORegistry.getDefaultInstance();
		final Iterator<J2KImageReaderSpi> iter = ServiceRegistry.lookupProviders(
			J2KImageReaderSpi.class);
		registry.registerServiceProviders(iter);
		final J2KImageReaderSpi spi = registry.getServiceProviderByClass(
			J2KImageReaderSpi.class);
		return new J2KImageReader(spi);
	}

}