/*
 * $RCSfile: JPEGTileCodecDescriptor.java,v $
 *
 * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
 *
 * Use is subject to license terms.
 *
 * $Revision: 1.1 $
 * $Date: 2005/02/11 04:57:54 $
 * $State: Exp $
 */package com.lightcrafts.mediax.jai.tilecodec ;

import java.awt.image.SampleModel ;
/*
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
*/
import com.lightcrafts.mediax.jai.ParameterListDescriptor;
import com.lightcrafts.mediax.jai.ParameterListDescriptorImpl;
import com.lightcrafts.mediax.jai.util.Range ;

/**
 * This class is the descriptor for the "JPEG" tile codec. "jpeg" is a
 * lossy tile codec, which involves compressing the tile data using the
 * jpeg standard. The format name for the jpeg tile codec is "jpeg". 
 * The encoded stream contains the <code>SampleModel</code> and the
 * tile's upper left corner position, thus the
 * <code>includesSampleModelInfo()</code> and 
 * <code>includesLocationInfo()</code> methods in this descriptor return
 * true.
 *
 * <p> This JPEG tile codec works well only on byte type images with 1, 
 * 3 or 4 bands.
 *
 * <p> While both the "tileDecoder" and "tileEncoder" registry modes for
 * the "jpeg" tile codec scheme have the same set of parameters, the 
 * parameters for the "tileDecoder" mode are read-only and will be
 * ignored if values are set for them in the 
 * <code>TileCodecParameterList</code> passed to the <code>TileDecoder</code>.
 * The reason for this is that the parameter values needed to control
 * the decoding process are included in the encoded stream, therefore
 * parameter values specified externally are ignored. Once the decoding
 * is completed, the parameter values in the 
 * <code>TileCodecParameterList</code> are updated to reflect the values
 * specified in the encoded stream.
 *
 * <p><table border=1>
 * <caption>Resource List</caption>
 * <tr><th>Name</th>        <th>Value</th></tr>
 * <tr><td>Vendor</td>      <td>com.lightcrafts.media.jai</td></tr>
 * <tr><td>Description</td> <td>A descriptor to describe the lossy "jpeg" codec
 *                          scheme. </td></tr>
 * <tr><td>DocURL</td>      <td>http://java.sun.com/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/tilecodec/JPEGTileCodecDescriptor.html</td></tr>
 * <tr><td>Version</td>     <td>1.2</td></tr>
 * <tr><td>quality</td>    <td>
 *                           <p>A factor that relates to the desired tradeoff
 *			     between image quality and the image data
 *			     compression ratio. The range of this parameter
 *			     is from 0.0 to 1.0. A setting of 1.0 produces the 
 *			     highest quality image at a lower compression
 *			     rate. A setting of 0.0 produces the highest 
 *			     compression ratio, with a sacrifice to image 
 *			     quality. The default value is 0.75. <p>In JPEG,
 *			     compression, there are two ways to control the 
 *			     quantization quality: one is define quantization
 *			     table for each image component; the other is 
 *			     define the quality. The later overrides the
 *			     former. If neither the quality nor quantization
 *			     tables are set, the default setting is used.
 *                           <p>When the quality is set, a group of
 *			     quantization tables are generated by rescaling 
 *			     the default quantization tables. For more
 *			     infomation, please refer to the links below.
 *                           </td></tr>
 * <tr><td>qualitySet</td>   <td>A boolean used to indicate that the 
 * 			     parameter <code>quality</code> is set or 
 *			     not.</td></tr>
 * <tr><td>horizontalSubsampling</td>    <td>The subsampling rate in the
 *				horizontal direction applied to each image
 *				component to reduce their resolution
 *				prior to encoding.</td></tr>
 * <tr><td>verticalSubsampling</td>    <td>The subsampling rate in the
 *                              vertical direction applied to each image
 *                              component to reduce their resolution
 *                              prior to encoding.</td></tr>
 * <tr><td>quantizationTableMapping</td>    <td>In JPEG compression, several 
 *				image components may share one quantization
 *				table. This is the mapping between the 
 *				image component and the quantization 
 *				tables.</td></tr>
 * <tr><td>quantizationTable0</td>    <td>A quantization table for JPEG codec
 *				is an array of 64 (8x8) expressed in zig-zag 
 *				order (see the JPEG spec section K.1). Since 
 *                              this descriptor defines a JPEG scheme where the
 *                              tile has at most 4 components, so at most 4
 *                              quantization tables will
 *				be used. This parameter is the first 
 *				quantization table. </td></tr>
 * <tr><td>quantizationTable1</td> <td>The second quantization table.</td></tr>
 * <tr><td>quantizationTable2</td> <td>The third quantization table.</td></tr>
 * <tr><td>quantizationTable3</td> <td>The fourth quantization table.</td></tr>
 * <tr><td>restartInterval</td>  <td>JPEG images use restart markers to define 
 *				multiple strips or tiles. The restart markers 
 *				are inserted periodically into the image data 
 *				to delineate image segments known as restart
 *			        intervals. To limit the effect of bitstream 
 *				errors to a single restart interval, JAI 
 *				provides methods to set the restart interval 
 *				in JPEG Minimum Coded Units (MCUs). 
 *				The default is zero (no restart interval 
 *				markers). </td></tr>
 * <tr><td>writeImageInfo</td>    <td>A boolean instructs the encoder to 
 *				write the image data to the output 
 *				stream. </td></tr>
 * <tr><td>writeTableInfo</td>    <td>A boolean instructs the encoder to 
 *				write the table data to the output 
 *				stream.</td></tr>
 * <tr><td>writeJFIFHeader</td>    <td>The JPEG File Interchange Format (JFIF) 
 *				is a minimal file format that enables JPEG 
 *				bitstreams to be exchanged between a wide 
 *				variety of platforms and applications. 
 *				The parameter instructs the encoder to write
 *				the output stream in the JFIF format.</td></tr>
 * </table></p>
 *
 * <p><table border=1>
 * <caption>Parameter List</caption>
 * <tr><th>Name</th>          <th>Class Type</th>
 *                            <th>Default Value</th></tr>
 * <tr><td>quality</td>       <td>java.lang.Float</td>
 *                            <td>0.75</td>
 * <tr><td>qualitySet</td>    <td>java.lang.Boolean</td>
 *                            <td>true</td>
 * <tr><td>horizontalSubsampling</td>          <td>integer array</td>
 *                            <td>{1,1,1}</td>
 * <tr><td>verticalSubsampling</td>          <td>integer array</td>
 *                            <td>{1,1,1}</td>
 * <tr><td>quantizationTableMapping</td>          <td>integer array</td>
 *                            <td>{0,1,1}</td>
 * <tr><td>quantizationTable0</td>          <td>integer array</td>
 *                            <td>The default Luminance table as defined in
 *                                section K.1 of the JPEG specification.</td>
 * <tr><td>quantizationTable1</td>          <td>integer array</td>
 *                            <td>The default Chrominance table as defined in
 *                                section K.1 of the JPEG specification.</td>
 * <tr><td>quantizationTable2</td>          <td>integer array</td>
 *                            <td>The default Chrominance table as defined in
 *                                section K.1 of the JPEG specification.</td>
 * <tr><td>quantizationTable3</td>          <td>integer array</td>
 *                            <td>The default Chrominance table as defined in
 *                                section K.1 of the JPEG specification.</td>
 * <tr><td>restartInterval</td>          <td>java.lang.Integer</td>
 *                            <td>0</td>
 * <tr><td>writeImageInfo</td>          <td>java.lang.Boolean</td>
 *                            <td>true</td>
 * <tr><td>writeTableInfo</td>          <td>java.lang.Boolean</td>
 *                            <td>true</td>
 * <tr><td>writeJFIFHeader</td>          <td>java.lang.Boolean</td>
 *                            <td>false</td>
 * </table></p>
 *
 * @see com.sun.image.codec.jpeg.JPEGQTable
 * @see com.sun.image.codec.jpeg.JPEGDecodeParam
 * @see com.sun.image.codec.jpeg.JPEGEncodeParam
 *
 * @since JAI 1.1
 */
public class JPEGTileCodecDescriptor extends TileCodecDescriptorImpl {

    private static final int[] lumQuantizationTable = {
	16,   11,  12,  14,  12,  10,  16,  14,
	13,   14,  18,  17,  16,  19,  24,  40,
	26,   24,  22,  22,  24,  49,  35,  37,
	29,   40,  58,  51,  61,  60,  57,  51,
	56,   55,  64,  72,  92,  78,  64,  68,
	87,   69,  55,  56,  80, 109,  81,  87,
	95,   98, 103, 104, 103,  62,  77, 113,
	121, 112, 100, 120,  92, 101, 103,  99
    };

    private static final int[] chromQuantizationTable = {                    
	17,  18,  18,  24,  21,  24,  47,  26,
	26,  47,  99,  66,  56,  66,  99,  99,
	99,  99,  99,  99,  99,  99,  99,  99,
	99,  99,  99,  99,  99,  99,  99,  99,
	99,  99,  99,  99,  99,  99,  99,  99,
	99,  99,  99,  99,  99,  99,  99,  99,
	99,  99,  99,  99,  99,  99,  99,  99,
	99,  99,  99,  99,  99,  99,  99,  99
    };
  
    // Parameter names
    private static final String[] paramNames = {
	"quality",
	"qualitySet",
	"horizontalSubsampling",
	"verticalSubsampling",
	"quantizationTableMapping",
	"quantizationTable0",
	"quantizationTable1",
	"quantizationTable2",
	"quantizationTable3",
	"restartInterval",
	"writeImageInfo",
	"writeTableInfo",
	"writeJFIFHeader"} ;

    // Parameter class names
    private static final Class[] paramClasses = {
	java.lang.Float.class,
	java.lang.Boolean.class,
	int[].class,
	int[].class,
	int[].class,
	int[].class,
	int[].class,
	int[].class,
	int[].class,
	java.lang.Integer.class,
	java.lang.Boolean.class,
	java.lang.Boolean.class,
	java.lang.Boolean.class} ;

    // Parameter default values.
    private static final Object[] paramDefaults = {
	new Float(0.75f) ,
	new Boolean(true) ,
	new int[] {1, 1, 1},
	new int[] {1, 1, 1},
	new int[] {0, 1, 1},
	lumQuantizationTable,
	chromQuantizationTable,
	chromQuantizationTable,
	chromQuantizationTable,
	new Integer(0),
	new Boolean(true),
	new Boolean(true),
	new Boolean(false)
    };

    /* XXX: If Set can be specified for non EnumeratedParameter types.
    // Valid values for the boolean valued parameters
    private static java.util.Set set = 
        Collections.synchronizedSet(new HashSet());

    static {
	set.add(Boolean.TRUE);
	set.add(Boolean.FALSE);
    }
    */

    // Parameters' valid value ranges
    private static final Object[] validParamValues = {
	new Range(java.lang.Float.class, new Float(0.0f), new Float(1.0f)) ,
	null, // set,
	null,
	null,
	null,
	null,
	null,
	null,
	null,
	new Range(java.lang.Integer.class, new Integer(0), null),
	null, // set,
	null, // set,
	null  // set
    };
    
    private static ParameterListDescriptor paramListDescriptor = 
        new ParameterListDescriptorImpl(null,
					paramNames,
					paramClasses,
					paramDefaults,
					validParamValues);

    /**
     * Creates a <code>JPEGTileCodecDescriptor</code> 
     */
    public JPEGTileCodecDescriptor() {
	super("jpeg", true, true) ;
    }

    /**
     * Returns a <code>TileCodecParameterList</code> valid for the 
     * specified modeName and compatible with the supplied
     * <code>TileCodecParameterList</code>. For example, given a
     * <code>TileCodecParameterList</code> used to encode a tile with
     * the modeName being specified as "tileDecoder", this method will return
     * a <code>TileCodecParameterList</code> sufficient to decode that
     * same tile. 
     *
     * <p> If the supplied modeName is one of the valid mode names as
     * ascertained from the <code>getSupportedNames()</code> method, 
     * this method returns a <code>TileCodecParameterList</code> that 
     * contains values that are compatible for the supplied mode name.
     *
     * @param modeName       The registry mode to return a valid parameter 
     *                       list for.
     * @param otherParamList The parameter list for which a compatible 
     *                       parameter list for the complementary modeName is
     *                       to be found.
     *
     * @throws IllegalArgumentException if <code>modeName</code> is null.
     * @throws IllegalArgumentException if <code>modeName</code> is not
     * one of the modes valid for this descriptor, i.e those returned
     * from the getSupportedNames() method.
     * @throws IllegalArgumentException if <code>otherParamList</code> is null.
     */
    public TileCodecParameterList getCompatibleParameters(
				       String modeName,
				       TileCodecParameterList otherParamList) {
	if (modeName == null) {
	    throw new IllegalArgumentException(
				JaiI18N.getString("TileCodecDescriptorImpl1"));
	}

	if (otherParamList == null) {
	    throw new IllegalArgumentException(
			        JaiI18N.getString("TileCodecDescriptorImpl3"));
	}
	
	String name = getName();
	if (!otherParamList.getFormatName().equals(name)) {
	    throw new IllegalArgumentException(
					     JaiI18N.getString("TileCodec2"));
	}

	if (otherParamList.isValidForMode(modeName))
	    return otherParamList;

	if (modeName.equalsIgnoreCase("tileDecoder")) {
	    return new TileCodecParameterList(
			          name,
				  new String[]{"tileDecoder"},
				  otherParamList.getParameterListDescriptor());
	} else if (modeName.equalsIgnoreCase("tileEncoder")) {	    
	    return new TileCodecParameterList(
				  name,
				  new String[]{"tileEncoder"},
				  otherParamList.getParameterListDescriptor());
	} else {
	   throw new IllegalArgumentException(JaiI18N.getString("TileCodec1"));
	}
    }

    /**
     * Returns the default parameters for the specified modeName as an
     * instance of the <code>TileCodecParameterList</code>. If the supplied
     * modeName is one of the valid mode names as ascertained from the 
     * <code>getSupportedNames()</code> method, this method returns the
     * default parameters for that mode.
     *
     * @param modeName  The mode to return the default parameters for.
     *
     * @throws IllegalArgumentException if <code>modeName</code> is null.
     * @throws IllegalArgumentException if <code>modeName</code> is not
     * one of the modes valid for this descriptor, i.e those returned
     * from the getSupportedNames() method.
     */
    public TileCodecParameterList getDefaultParameters(String modeName){
        if (modeName == null)
	    throw new IllegalArgumentException(
	        JaiI18N.getString("TileCodecDescriptorImpl1")) ;

	String validNames[] = getSupportedModes();
	boolean valid = false;

	for (int i=0; i<validNames.length; i++) {
	    if (modeName.equalsIgnoreCase(validNames[i])) {
		valid = true;
		break;
	    }
	}

	if (valid == false) {
	    throw new IllegalArgumentException(
					    JaiI18N.getString("TileCodec1"));
	}

	return new TileCodecParameterList("jpeg",
					  new String[] {"tileDecoder",
							"tileEncoder"},
					  paramListDescriptor);
    }

    /**
     * Returns the default parameters for the specified modeName as an
     * instance of the <code>TileCodecParameterList</code>, adding a 
     * "sampleModel" parameter with the specified value to the parameter
     * list. If the supplied modeName is one of the valid mode names as
     * ascertained from the <code>getSupportedNames()</code> method, this
     * method returns the default parameters for that mode.
     *
     * <p> This method should be used when includesSampleModelInfo()
     * returns false. If includesSampleModelInfo() returns true, the
     * supplied <code>SampleModel</code> is ignored. 
     *
     * <p>For the JPEG codec, includesSampleModelInfo() returns true, so 
     * the supplied <code> SampleModel</code> is ignored.
     *
     * @param modeName  The mode to return the default parameters for.
     * @param sm    The <code>SampleModel</code> used to create the 
     *              default decoding parameter list.
     *
     * @throws IllegalArgumentException if <code>modeName</code> is null.
     * @throws IllegalArgumentException if <code>modeName</code> is not
     * one of the modes valid for this descriptor, i.e those returned
     * from the getSupportedNames() method.
     */
    public TileCodecParameterList getDefaultParameters(String modeName, 
						       SampleModel sm){
	return getDefaultParameters(modeName);
    }

    /**
     * Returns the <code>ParameterListDescriptor</code> that describes
     * the associated parameters (NOT sources). If the supplied modeName
     * is one of the valid mode names as ascertained from the 
     * <code>getSupportedNames()</code> method, this method returns a
     * non-null <code>ParameterListDescriptor</code> with the appropriate
     * parameters. 
     *
     * @param modeName  The mode to return the ParameterListDescriptor for.
     *
     * @throws IllegalArgumentException if <code>modeName</code> is null.
     * @throws IllegalArgumentException if <code>modeName</code> is not
     * one of the modes valid for this descriptor, i.e those returned
     * from the getSupportedNames() method.
     */
    public ParameterListDescriptor getParameterListDescriptor(String modeName){
	if(modeName == null)
	    throw new IllegalArgumentException( 
		JaiI18N.getString("TileCodecDescriptorImpl1")) ;

	String validNames[] = getSupportedModes();
	boolean valid = false;

	for (int i=0; i<validNames.length; i++) {
	    if (modeName.equalsIgnoreCase(validNames[i])) {
		valid = true;
		break;
	    }
	}

	if (valid == false) {
	    throw new IllegalArgumentException(
					    JaiI18N.getString("TileCodec1"));
	}

	return paramListDescriptor;
    }
}
