/*
 * $RCSfile: BitstreamReaderAgent.java,v $
 * $Revision: 1.1 $
 * $Date: 2005-02-11 05:02:00 $
 * $State: Exp $
 *
 * Class:                   BitstreamReaderAgent
 *
 * Description:             The generic interface for bit stream
 *                          transport agents.
 *
 *
 *
 * COPYRIGHT:
 *
 * This software module was originally developed by Raphaël Grosbois and
 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
 * Centre France S.A) in the course of development of the JPEG2000
 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
 * software module is an implementation of a part of the JPEG 2000
 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
 * Systems AB and Canon Research Centre France S.A (collectively JJ2000
 * Partners) agree not to assert against ISO/IEC and users of the JPEG
 * 2000 Standard (Users) any of their rights under the copyright, not
 * including other intellectual property rights, for this software module
 * with respect to the usage by ISO/IEC and Users of this software module
 * or modifications thereof for use in hardware or software products
 * claiming conformance to the JPEG 2000 Standard. Those intending to use
 * this software module in hardware or software products are advised that
 * their use may infringe existing patents. The original developers of
 * this software module, JJ2000 Partners and ISO/IEC assume no liability
 * for use of this software module or modifications thereof. No license
 * or right to this software module is granted for non JPEG 2000 Standard
 * conforming products. JJ2000 Partners have full right to use this
 * software module for his/her own purpose, assign or donate this
 * software module to any third party and to inhibit third parties from
 * using this software module for non JPEG 2000 Standard conforming
 * products. This copyright notice must be included in all copies or
 * derivative works of this software module.
 *
 * Copyright (c) 1999/2000 JJ2000 Partners.
 *  */
package jj2000.j2k.codestream.reader;
import java.awt.Point;

import jj2000.j2k.quantization.dequantizer.*;
import jj2000.j2k.wavelet.synthesis.*;
import jj2000.j2k.entropy.decoder.*;
import jj2000.j2k.codestream.*;
import jj2000.j2k.wavelet.*;
import jj2000.j2k.decoder.*;
import jj2000.j2k.image.*;
import jj2000.j2k.util.*;
import jj2000.j2k.io.*;
import jj2000.j2k.*;

import java.io.*;

import com.sun.media.imageioimpl.plugins.jpeg2000.J2KImageReadParamJava;

/**
 * This is the generic interface for bit stream reader agents. A bit stream
 * reader agent is an entity that allows reading from a bit stream and
 * requesting compressed code-blocks. It can be a simple file reader, or a
 * network connection, or anything else.
 *
 * <P>The bit stream reader agent allows to make request for compressed block
 * data in any order. The amount of data returned would normally depend on the
 * data available at the time of the request, be it from a file or from a
 * network connection.
 *
 * <P>The bit stream reader agent has the notion of a current tile, and
 * coordinates are relative to the current tile, where applicable.
 *
 * <P>Resolution level 0 is the lowest resolution level, i.e. the LL subband
 * alone.
 * */
public abstract class BitstreamReaderAgent implements CodedCBlkDataSrcDec {

    /** The decoder specifications */
    protected DecoderSpecs decSpec;

    /**
     * Whether or not the components in the current tile uses a derived
     * quantization step size (only relevant in non reversible quantization
     * mode). This field is actualized by the setTile method in
     * FileBitstreamReaderAgent.
     *
     * @see FileBitstreamReaderAgent#initSubbandsFields
     * */
    protected boolean derived[] = null;

    /**
     * Number of guard bits off all component in the current tile. This field
     * is actualized by the setTile method in FileBitstreamReaderAgent.
     *
     * @see FileBitstreamReaderAgent#initSubbandsFields
     * */
    protected int[] gb = null;

    /**
     * Dequantization parameters of all subbands and all components in the
     * current tile. The value is actualized by the setTile method in
     * FileBitstreamReaderAgent.
     *
     * @see FileBitstreamReaderAgent#initSubbandsFields
     * */
    protected StdDequantizerParams params[] = null;

    /** The prefix for bit stream reader options: 'B' */
    public final static char OPT_PREFIX = 'B';

    /** The list of parameters that is accepted by the bit stream
     * readers. They start with 'B'. */
    private static final String [][] pinfo = null;

    /**
     * The maximum number of decompostion levels for each component of the
     * current tile. It means that component c has mdl[c]+1 resolution levels
     * (indexed from 0 to mdl[c])
     * */
    protected int mdl[];

    /** The number of components */
    protected final int nc;

    /** Image resolution level to generate */
    protected int targetRes;

    /**
     * The subband trees for each component in the current tile. Each element
     * in the array is the root element of the subband tree for a
     * component. The number of magnitude bits in each subband (magBits member
     * variable) is not initialized.
     * */
    protected SubbandSyn subbTrees[];

    /** The image width on the hi-res reference grid */
    protected final int imgW;

    /** The image width on the hi-res reference grid */
    protected final int imgH;

    /** The horizontal coordinate of the image origin in the canvas system, on
     * the reference grid. */
    protected final int ax;

    /** The vertical coordinate of the image origin in the canvas system, on
     * the reference grid. */
    protected final int ay;

    /** The horizontal coordinate of the tiling origin in the canvas system, on
     * the reference grid. */
    protected final int px;

    /** The vertical coordinate of the tiling origin in the canvas system, on
     * the reference grid. */
    protected final int py;

    /** The horizontal offsets of the upper-left corner of the current tile
     * (not active tile) with respect to the canvas origin, in the component
     * hi-res grid, for each component. */
    protected final int offX[];

    /** The vertical offsets of the upper-left corner of the current tile (not
     * active tile) with respect to the canvas origin, in the component hi-res
     * grid, for each component. */
    protected final int offY[];

    /** The horizontal coordinates of the upper-left corner of the active
     * tile, with respect to the canvas origin, in the component hi-res grid,
     * for each component. */
    protected final int culx[];

    /** The vertical coordinates of the upper-left corner of the active tile,
     * with respect to the canvas origin, in the component hi-res grid, for
     * each component. */
    protected final int culy[];

    /** The nominal tile width, in the hi-res reference grid */
    protected final int ntW;

    /** The nominal tile height, in the hi-res reference grid */
    protected final int ntH;

    /** The number of tile in the horizontal direction */
    protected final int ntX;

    /** The number of tiles in the vertical direction */
    protected final int ntY;

    /** The total number of tiles. */
    protected final int nt;

    /** The current tile horizontal index */
    protected int ctX;

    /** The current tile vertical index */
    protected int ctY;

    /** The decoded bit stream header */
    protected final HeaderDecoder hd;

    /** Number of bytes targeted to be read */
    protected int tnbytes;

    /** Actual number of read bytes */
    protected int anbytes;

    /** Target decoding rate in bpp */
    protected float trate;

    /** Actual decoding rate in bpp */
    protected float arate;

    /**
     * Initializes members of this class. This constructor takes a
     * HeaderDecoder object. This object must be initialized by the
     * constructor of the implementing class from the header of the bit
     * stream.
     *
     * @param hd The decoded header of the bit stream from where to initialize
     * the values.
     *
     * @param decSpec The decoder specifications
     * */
    protected BitstreamReaderAgent(HeaderDecoder hd,DecoderSpecs decSpec){
        Point co;
        int i,j,max;

        this.decSpec = decSpec;
        this.hd = hd;

        // Number of components
        nc = hd.getNumComps();
        offX = new int[nc];
        offY = new int[nc];
        culx = new int[nc];
        culy = new int[nc];

        // Image size and origin
        imgW = hd.getImgWidth();
        imgH = hd.getImgHeight();
        ax = hd.getImgULX();
        ay = hd.getImgULY();

        // Tiles
        co = hd.getTilingOrigin(null);
        px = co.x;
        py = co.y;
        ntW = hd.getNomTileWidth();
        ntH = hd.getNomTileHeight();
        ntX = (ax+imgW-px+ntW-1) / ntW;
        ntY = (ay+imgH-py+ntH-1) / ntH;
	nt = ntX * ntY;
    }

    /**
     * Returns the vertical code-block partition origin. Allowable values are
     * 0 and 1, nothing else.
     * */
    public final int getCbULX() {
        return hd.getCbULX();
    }

    /**
     * Returns the vertical code-block partition origin. Allowable values are
     * 0 and 1, nothing else.
     * */
    public int getCbULY() {
        return hd.getCbULY();
    }

    /**
     * Returns the number of components in the image.
     *
     * @return The number of components in the image.
     * */
    public final int getNumComps() {
        return nc;
    }

    /**
     * Returns the component subsampling factor in the horizontal direction,
     * for the specified component. This is, approximately, the ratio of
     * dimensions between the reference grid and the component itself, see the
     * 'ImgData' interface desription for details.
     *
     * @param c The index of the component (between 0 and N-1)
     *
     * @return The horizontal subsampling factor of component 'c'
     *
     * @see jj2000.j2k.image.ImgData
     * */
    public final int getCompSubsX(int c) {
        return hd.getCompSubsX(c);
    }

    /**
     * Returns the component subsampling factor in the vertical direction, for
     * the specified component. This is, approximately, the ratio of
     * dimensions between the reference grid and the component itself, see the
     * 'ImgData' interface desription for details.
     *
     * @param c The index of the component (between 0 and C-1)
     *
     * @return The vertical subsampling factor of component 'c'
     *
     * @see jj2000.j2k.image.ImgData
     * */
    public int getCompSubsY(int c) {
        return hd.getCompSubsY(c);
    }

    /**
     * Returns the overall width of the current tile in pixels for the given
     * (tile) resolution level. This is the tile's width without accounting
     * for any component subsampling.
     *
     * <P>Note: Tile resolution level indexes may be different from
     * tile-component resolution index. They are indeed indexed starting from
     * the lowest number of decomposition levels of each component of the
     * tile.
     *
     * <P>For an image (1 tile) with 2 components (component 0 having 2
     * decomposition levels and component 1 having 3 decomposition levels),
     * the first (tile-)component has 3 resolution levels and the second one
     * has 4 resolution levels, whereas the tile has only 3 resolution levels
     * available.
     *
     * @param rl The (tile) resolution level.
     *
     * @return The current tile's width in pixels.
     * */
    public int getTileWidth(int rl){
	// The minumum number of decomposition levels between all the
	// components
	int mindl = decSpec.dls.getMinInTile(getTileIdx());
        if(rl>mindl){
            throw new IllegalArgumentException("Requested resolution level"+
                                               " is not available for, at "+
                                               "least, one component in "+
					       "tile: "+ctX+"x"+ctY);
        }
        int ctulx,ntulx;
        int dl = mindl-rl; // Number of decomposition to obtain this
	// resolution

        // Calculate starting X of current tile at hi-res
        ctulx = (ctX == 0) ? ax : px+ctX*ntW;
        // Calculate starting X of next tile X-wise at hi-res
        ntulx = (ctX < ntX-1) ? px+(ctX+1)*ntW : ax+imgW;
	dl = 1 << dl;
        // The difference at the rl resolution level is the width
        return (ntulx+dl-1)/dl-(ctulx+dl-1)/dl;
    }

    /**
     * Returns the overall height of the current tile in pixels, for the given
     * resolution level. This is the tile's height without accounting for any
     * component subsampling.
     *
     * <P>Note: Tile resolution level indexes may be different from
     * tile-component resolution index. They are indeed indexed starting from
     * the lowest number of decomposition levels of each component of the
     * tile.
     *
     * <P>For an image (1 tile) with 2 components (component 0 having 2
     * decomposition levels and component 1 having 3 decomposition levels),
     * the first (tile-)component has 3 resolution levels and the second one
     * has 4 resolution levels, whereas the tile has only 3 resolution levels
     * available.
     *
     * @param rl The (tile) resolution level.
     *
     * @return The total current tile's height in pixels.
     * */
    public int getTileHeight(int rl){
	// The minumum number of decomposition levels between all the
	// components
	int mindl = decSpec.dls.getMinInTile(getTileIdx());
        if(rl>mindl){
            throw new IllegalArgumentException("Requested resolution level"+
                                               " is not available for, at "+
                                               "least, one component in"+
					       " tile: "+ctX+"x"+ctY);
        }

        int ctuly,ntuly;
        int dl = mindl-rl; // Number of decomposition to obtain this
	// resolution

        // Calculate starting Y of current tile at hi-res
        ctuly = (ctY == 0) ? ay : py+ctY*ntH;
        // Calculate starting Y of next tile Y-wise at hi-res
        ntuly = (ctY < ntY-1) ? py+(ctY+1)*ntH : ay+imgH;
	dl = 1 <<dl;
        // The difference at the rl level is the height
        return (ntuly+dl-1)/dl-(ctuly+dl-1)/dl;
    }

    /**
     * Returns the overall width of the image in pixels, for the given (image)
     * resolution level. This is the image's width without accounting for any
     * component subsampling or tiling.
     *
     * <P>Note: Image resolution level indexes may differ from tile-component
     * resolution index. They are indeed indexed starting from the lowest
     * number of decomposition levels of each component of each tile.
     *
     * <P>Example: For an image (1 tile) with 2 components (component 0 having
     * 2 decomposition levels and component 1 having 3 decomposition levels),
     * the first (tile-) component has 3 resolution levels and the second one
     * has 4 resolution levels, whereas the image has only 3 resolution levels
     * available.
     *
     * @param rl The image resolution level.
     *
     * @return The total image's width in pixels.
     * */
    public int getImgWidth(int rl){
	// The minimum number of decomposition levels of each
	// tile-component
	int mindl = decSpec.dls.getMin();
        if(rl>mindl){
            throw new IllegalArgumentException("Requested resolution level"+
                                               " is not available for, at "+
                                               "least, one tile-component");
        }
	// Retrieve number of decomposition levels corresponding to
	// this resolution level
	int dl = 1 << mindl - rl;
        return (ax+imgW+dl-1)/dl-(ax+dl-1)/dl;
    }

    /**
     * Returns the overall height of the image in pixels, for the given
     * resolution level. This is the image's height without accounting for any
     * component subsampling or tiling.
     *
     * <P>Note: Image resolution level indexes may differ from tile-component
     * resolution index. They are indeed indexed starting from the lowest
     * number of decomposition levels of each component of each tile.
     *
     * <P>Example: For an image (1 tile) with 2 components (component 0 having
     * 2 decomposition levels and component 1 having 3 decomposition levels),
     * the first (tile-) component has 3 resolution levels and the second one
     * has 4 resolution levels, whereas the image has only 3 resolution levels
     * available.
     *
     * @param rl The image resolution level, from 0 to L.
     *
     * @return The total image's height in pixels.
     * */
    public int getImgHeight(int rl){
	int mindl = decSpec.dls.getMin();
        if(rl>mindl){
            throw new IllegalArgumentException("Requested resolution level"+
                                               " is not available for, at "+
                                               "least, one tile-component");
        }
	// Retrieve number of decomposition levels corresponding to this
	// resolution level
	int dl = 1 << mindl - rl;
        return (ay+imgH+dl-1)/dl-(ay+dl-1)/dl;
    }

    /**
     * Returns the horizontal coordinate of the image origin, the top-left
     * corner, in the canvas system, on the reference grid at the specified
     * resolution level.
     *
     * <P>Note: Image resolution level indexes may differ from tile-component
     * resolution index. They are indeed indexed starting from the lowest
     * number of decomposition levels of each component of each tile.
     *
     * <P>Example: For an image (1 tile) with 2 components (component 0 having
     * 2 decomposition levels and component 1 having 3 decomposition levels),
     * the first (tile-) component has 3 resolution levels and the second one
     * has 4 resolution levels, whereas the image has only 3 resolution levels
     * available.
     *
     * @param rl The resolution level, from 0 to L.
     *
     * @return The horizontal coordinate of the image origin in the canvas
     * system, on the reference grid.
     * */
    public int getImgULX(int rl){
	int mindl = decSpec.dls.getMin();
        if(rl>mindl){
            throw new IllegalArgumentException("Requested resolution level"+
                                               " is not available for, at "+
                                               "least, one tile-component");
        }
	// Retrieve number of decomposition levels corresponding to this
	// resolution level
	int dl = 1 << mindl - rl;
        return (ax+dl-1)/dl;
    }

    /**
     * Returns the vertical coordinate of the image origin, the top-left
     * corner, in the canvas system, on the reference grid at the specified
     * resolution level.
     *
     * <P>Note: Image resolution level indexes may differ from tile-component
     * resolution index. They are indeed indexed starting from the lowest
     * number of decomposition levels of each component of each tile.
     *
     * <P>Example: For an image (1 tile) with 2 components (component 0 having
     * 2 decomposition levels and component 1 having 3 decomposition levels),
     * the first (tile-) component has 3 resolution levels and the second one
     * has 4 resolution levels, whereas the image has only 3 resolution levels
     * available.
     *
     * @param rl The resolution level, from 0 to L.
     *
     * @return The vertical coordinate of the image origin in the canvas
     * system, on the reference grid.
     * */
    public int getImgULY(int rl){
	int mindl = decSpec.dls.getMin();
        if(rl>mindl){
            throw new IllegalArgumentException("Requested resolution level"+
                                               " is not available for, at "+
                                               "least, one tile-component");
        }
	// Retrieve number of decomposition levels corresponding to this
	// resolution level
	int dl = 1 << mindl - rl;
        return (ay+dl-1)/dl;
    }

    /**
     * Returns the width in pixels of the specified tile-component for the
     * given (tile-component) resolution level.
     *
     * @param t The tile index
     *
     * @param c The index of the component, from 0 to N-1.
     *
     * @param rl The resolution level, from 0 to L.
     *
     * @return The width in pixels of component <tt>c</tt> in tile <tt>t</tt>
     * for resolution level <tt>rl</tt>.
     * */
    public final int getTileCompWidth(int t,int c,int rl) {
        int tIdx = getTileIdx();
        if(t!=tIdx) {
            throw new Error("Asking the tile-component width of a tile "+
                            "different  from the current one.");
        }
        // Calculate starting X of next tile X-wise at reference grid hi-res
        int ntulx = (ctX < ntX-1) ? px+(ctX+1)*ntW : ax+imgW;
        // Convert reference grid hi-res to component grid hi-res
        ntulx = (ntulx+hd.getCompSubsX(c)-1)/hd.getCompSubsX(c);
        int dl = 1 << mdl[c]-rl;
        // Starting X of current tile at component grid hi-res is culx[c]
        // The difference at the rl level is the width
        return (ntulx+dl-1)/dl-(culx[c]+dl-1)/dl;
    }

    /**
     * Returns the height in pixels of the specified tile-component for the
     * given (tile-component) resolution level.
     *
     * @param t The tile index.
     *
     * @param c The index of the component, from 0 to N-1.
     *
     * @param rl The resolution level, from 0 to L.
     *
     * @return The height in pixels of component <tt>c</tt> in the current
     * tile.
     * */
    public final int getTileCompHeight(int t,int c,int rl) {
        int tIdx = getTileIdx();
        if(t!=tIdx) {
            throw new Error("Asking the tile-component width of a tile "+
                            "different  from the current one.");
        }
        // Calculate starting Y of next tile Y-wise at reference grid hi-res
        int ntuly = (ctY < ntY-1) ? py+(ctY+1)*ntH : ay+imgH;
        // Convert reference grid hi-res to component grid hi-res
        ntuly = (ntuly+hd.getCompSubsY(c)-1)/hd.getCompSubsY(c);
        int dl = 1 << mdl[c]-rl; // Revert level indexation (0 is hi-res)
        // Starting Y of current tile at component grid hi-res is culy[c]
        // The difference at the rl level is the height
        return (ntuly+dl-1)/dl-(culy[c]+dl-1)/dl;
    }


    /**
     * Returns the width in pixels of the specified component in the overall
     * image, for the given (component) resolution level.
     *
     * <P>Note: Component resolution level indexes may differ from
     * tile-component resolution index. They are indeed indexed starting from
     * the lowest number of decomposition levels of same component of each
     * tile.
     *
     * <P>Example: For an image (2 tiles) with 1 component (tile 0 having 2
     * decomposition levels and tile 1 having 3 decomposition levels), the
     * first tile(-component) has 3 resolution levels and the second one has 4
     * resolution levels, whereas the component has only 3 resolution levels
     * available.
     *
     * @param c The index of the component, from 0 to N-1.
     *
     * @param rl The resolution level, from 0 to L.
     *
     * @return The width in pixels of component <tt>c</tt> in the overall
     * image.
     * */
    public final int getCompImgWidth(int c,int rl){
	// indexation (0 is hi-res)
        // Calculate image starting x at component hi-res grid
        int sx = (ax+hd.getCompSubsX(c)-1)/hd.getCompSubsX(c);
        // Calculate image ending (excluding) x at component hi-res grid
        int ex = (ax+imgW+hd.getCompSubsX(c)-1)/hd.getCompSubsX(c);
	int dl = 1 << decSpec.dls.getMinInComp(c)-rl;
        // The difference at the rl level is the width
        return (ex+dl-1)/dl-(sx+dl-1)/dl;
    }

    /**
     * Returns the height in pixels of the specified component in the overall
     * image, for the given (component) resolution level.
     *
     * <P>Note: Component resolution level indexes may differ from
     * tile-component resolution index. They are indeed indexed starting from
     * the lowest number of decomposition levels of same component of each
     * tile.
     *
     * <P>Example: For an image (2 tiles) with 1 component (tile 0 having 2
     * decomposition levels and tile 1 having 3 decomposition levels), the
     * first tile(-component) has 3 resolution levels and the second one has 4
     * resolution levels, whereas the component has only 3 resolution levels
     * available.
     *
     * @param c The index of the component, from 0 to N-1.
     *
     * @param rl The resolution level, from 0 to L.
     *
     * @return The height in pixels of component <tt>c</tt> in the overall
     * image.
     * */
    public final int getCompImgHeight(int c,int rl){
	// indexation (0 is hi-res)
        // Calculate image starting x at component hi-res grid
        int sy = (ay+hd.getCompSubsY(c)-1)/hd.getCompSubsY(c);
        // Calculate image ending (excluding) x at component hi-res grid
        int ey = (ay+imgH+hd.getCompSubsY(c)-1)/hd.getCompSubsY(c);
	int dl = 1 << decSpec.dls.getMinInComp(c)-rl;
        // The difference at the rl level is the width
        return (ey+dl-1)/dl-(sy+dl-1)/dl;
    }

    /**
     * Changes the current tile, given the new indexes. An
     * IllegalArgumentException is thrown if the indexes do not correspond to
     * a valid tile.
     *
     * @param x The horizontal indexes the tile.
     *
     * @param y The vertical indexes of the new tile.
     * */
    public abstract void setTile(int x, int y);

    /**
     * Advances to the next tile, in standard scan-line order (by rows then
     * columns). An NoNextElementException is thrown if the current tile is
     * the last one (i.e. there is no next tile).
     * */
    public abstract void nextTile();

    /**
     * Returns the indexes of the current tile. These are the horizontal and
     * vertical indexes of the current tile.
     *
     * @param co If not null this object is used to return the information. If
     * null a new one is created and returned.
     *
     * @return The current tile's indexes (vertical and horizontal indexes).
     * */
    public final Point getTile(Point co) {
        if (co != null) {
            co.x = ctX;
            co.y = ctY;
            return co;
        }
        else {
            return new Point(ctX,ctY);
        }
    }

    /**
     * Returns the index of the current tile, relative to a standard scan-line
     * order.
     *
     * @return The current tile's index (starts at 0).
     * */
    public final int getTileIdx() {
        return ctY*ntX+ctX;
    }

    /** 
     * Returns the horizontal coordinate of the upper-left corner of the
     * specified resolution in the given component of the current tile.
     *  
     * @param c The component index.
     *  
     * @param rl The resolution level index.
     * */
    public final int getResULX(int c,int rl) {
        int dl = mdl[c]-rl;
        if(dl<0){
            throw new IllegalArgumentException("Requested resolution level"+
                                               " is not available for, at "+
                                               "least, one component in "+
                                               "tile: "+ctX+"x"+ctY);
        }
        int tx0 = (int)Math.max(px+ctX*ntW,ax);
        int tcx0 = (int)Math.ceil(tx0/(double)getCompSubsX(c));
        return (int)Math.ceil(tcx0/(double)(1<<dl));
    }

    /**
     * Returns the vertical coordinate of the upper-left corner of the
     * specified component in the given component of the current tile.
     *
     * @param c The component index.
     *
     * @param rl The resolution level index.
     * */
    public final int getResULY(int c,int rl) {
        int dl = mdl[c]-rl;
        if(dl<0){
            throw new IllegalArgumentException("Requested resolution level"+
                                               " is not available for, at "+
                                               "least, one component in "+
                                               "tile: "+ctX+"x"+ctY);
        }
        int ty0 = (int)Math.max(py+ctY*ntH,ay);
        int tcy0 = (int)Math.ceil(ty0/(double)getCompSubsY(c));
        return (int)Math.ceil(tcy0/(double)(1<<dl));
    }

    /**
     * Returns the number of tiles in the horizontal and vertical directions.
     *
     * @param co If not null this object is used to return the information. If
     * null a new one is created and returned.
     *
     * @return The number of tiles in the horizontal (Point.x) and vertical
     * (Point.y) directions.
     * */
    public final Point getNumTiles(Point co) {
        if (co != null) {
            co.x = ntX;
            co.y = ntY;
            return co;
        }
        else {
            return new Point(ntX,ntY);
        }
    }

    /**
     * Returns the total number of tiles in the image.
     *
     * @return The total number of tiles in the image.
     * */
    public final int getNumTiles() {
        return ntX*ntY;
    }

    /**
     * Returns the subband tree, for the specified tile-component. This method
     * returns the root element of the subband tree structure, see Subband and
     * SubbandSyn. The tree comprises all the available resolution levels.
     *
     * <p>Note: this method is not able to return subband tree for a tile
     * different than the current one.</p>
     *
     * <p>The number of magnitude bits ('magBits' member variable) for each
     * subband is not initialized.</p>
     *
     * @param t The tile index
     *
     * @param c The index of the component, from 0 to C-1.
     *
     * @return The root of the tree structure.
     * */
    public final SubbandSyn getSynSubbandTree(int t,int c) {
        if(t!=getTileIdx()) {
            throw new IllegalArgumentException("Can not request subband"+
                                               " tree of a different tile"+
                                               " than the current one");
        }
        if(c<0 || c>=nc) {
            throw new IllegalArgumentException("Component index out of range");
        }
        return subbTrees[c];
    }


    /**
     * Creates a bit stream reader of the correct type that works on the
     * provided RandomAccessIO, with the special parameters from the parameter
     * list.
     *
     * @param in The RandomAccessIO source from which to read the bit stream.
     *
     * @param hd Header of the codestream.
     *
     * @param j2krparam The parameters applicable to the
     * bit stream read (other parameters may also be present).
     *
     * @param decSpec The decoder specifications
     *
     * @param cdstrInfo Whether or not to print information found in
     * codestream. 
     *
     * @param hi Reference to the HeaderInfo instance.
     *
     * @exception IOException If an I/O error occurs while reading initial
     * data from the bit stream.
     * @exception IllegalArgumentException If an unrecognised bit stream
     * reader option is present.
     * */
    public static BitstreamReaderAgent createInstance(RandomAccessIO in,
                                                      HeaderDecoder hd,
                                                      J2KImageReadParamJava j2krparam,
                                                      DecoderSpecs decSpec,
                                                      boolean cdstrInfo,
                                                      HeaderInfo hi)
        throws IOException {
        // Check header length
/*
	if (in.getPos() != hd.getTotalHeaderLength() + hd.initPos) {
            throw new IllegalArgumentException("Invalid header length");
        }
*/
        return new FileBitstreamReaderAgent(hd,in,decSpec,j2krparam,cdstrInfo,hi);
    }


    /**
     * Returns the parameters that are used in this class and implementing
     * classes. It returns a 2D String array. Each of the 1D arrays is for a
     * different option, and they have 3 elements. The first element is the
     * option name, the second one is the synopsis and the third one is a long
     * description of what the parameter is. The synopsis or description may
     * be 'null', in which case it is assumed that there is no synopsis or
     * description of the option, respectively. Null may be returned if no
     * options are supported.
     *
     * @return the options name, their synopsis and their explanation, or null
     * if no options are supported.
     * */
    public static String[][] getParameterInfo() {
        return pinfo;
    }

    /**
     * Returns the precinct partition width for the specified tile-component
     * and (tile-component) resolution level.
     *
     * @param t the tile index
     *
     * @param c The index of the component (between 0 and N-1)
     *
     * @param rl The resolution level, from 0 to L.
     *
     * @return the precinct partition width for the specified component,
     * resolution level and tile.
     * */
    public final int getPPX(int t,int c,int rl){
        return decSpec.pss.getPPX(t,c,rl);
    }

    /**
     * Returns the precinct partition height for the specified tile-component
     * and (tile-component) resolution level.
     *
     * @param t The tile index
     *
     * @param c The index of the component (between 0 and N-1)
     *
     * @param rl The resolution level, from 0 to L.
     *
     * @return The precinct partition height in the specified component, for
     * the specified resolution level, for the current tile.
     * */
    public final int getPPY(int t,int c,int rl){
        return decSpec.pss.getPPY(t,c,rl);
    }

    /**
     * Initialises subbands fields, such as code-blocks dimension and number
     * of magnitude bits, in the subband tree. The nominal code-block
     * width/height depends on the precincts dimensions if used. The way the
     * number of magnitude bits is computed depends on the quantization type
     * (reversible, derived, expounded).
     *
     * @param c The component index
     *
     * @param sb The subband tree to be initialised.
     * */
    protected void initSubbandsFields(int c,SubbandSyn sb){
        int t = getTileIdx();
        int rl = sb.resLvl;
        int cbw, cbh;

        cbw = decSpec.cblks.getCBlkWidth(ModuleSpec.SPEC_TILE_COMP,t,c);
        cbh = decSpec.cblks.getCBlkHeight(ModuleSpec.SPEC_TILE_COMP,t,c);

        if( !sb.isNode ){
            if( hd.precinctPartitionUsed() ){
                // The precinct partition is used
                int ppxExp, ppyExp, cbwExp, cbhExp;

                // Get exponents
                ppxExp = MathUtil.log2(getPPX(t,c,rl));
                ppyExp = MathUtil.log2(getPPY(t,c,rl));
                cbwExp = MathUtil.log2(cbw);
                cbhExp = MathUtil.log2(cbh);

                switch (sb.resLvl) {
                    case 0:
                        sb.nomCBlkW = ( cbwExp<ppxExp ?
                            (1<<cbwExp) : (1<<ppxExp) );
                        sb.nomCBlkH = ( cbhExp<ppyExp ?
                            (1<<cbhExp) : (1<<ppyExp) );
                        break;

                    default:
                        sb.nomCBlkW = ( cbwExp<ppxExp-1 ?
                            (1<<cbwExp) : (1<<(ppxExp-1)) );
                        sb.nomCBlkH = ( cbhExp<ppyExp-1 ?
                            (1<<cbhExp) : (1<<(ppyExp-1)) );
                        break;
                }
            }
            else {
                sb.nomCBlkW = cbw;
                sb.nomCBlkH = cbh;
            }

            // Number of code-blocks
            if(sb.numCb == null) sb.numCb = new Point();
            if (sb.w==0 || sb.h==0) {
                sb.numCb.x = 0;
                sb.numCb.y = 0;
            } else {
                int cb0x = getCbULX();
                int cb0y = getCbULY();
                int tmp;

                // Projects code-block partition origin to subband. Since the
                // origin is always 0 or 1, it projects to the low-pass side
                // (throught the ceil operator) as itself (i.e. no change) and
                // to the high-pass side (through the floor operator) as 0,
                // always.
                int acb0x = cb0x;
                int acb0y = cb0y;

                switch (sb.sbandIdx) {
                case Subband.WT_ORIENT_LL:
                    // No need to project since all low-pass => nothing to do
                    break;
                case Subband.WT_ORIENT_HL:
                    acb0x = 0;
                    break;
                case Subband.WT_ORIENT_LH:
                    acb0y = 0;
                    break;
                case Subband.WT_ORIENT_HH:
                    acb0x = 0;
                    acb0y = 0;
                    break;
                default:
                    throw new Error("Internal JJ2000 error");
                }
                if(sb.ulcx-acb0x<0 || sb.ulcy-acb0y<0) {
                    throw new IllegalArgumentException("Invalid code-blocks "+
                                                       "partition origin or "+
                                                       "image offset in the "+
                                                       "reference grid.");
                }

                // NOTE: when calculating "floor()" by integer division the
                // dividend and divisor must be positive, we ensure that by
                // adding the divisor to the dividend and then substracting 1
                // to the result of the division

                tmp = sb.ulcx-acb0x+sb.nomCBlkW;
                sb.numCb.x = (tmp+sb.w-1)/sb.nomCBlkW - (tmp/sb.nomCBlkW-1);

                tmp = sb.ulcy-acb0y+sb.nomCBlkH;
                sb.numCb.y = (tmp+sb.h-1)/sb.nomCBlkH - (tmp/sb.nomCBlkH-1);
            }

            if(derived[c]){
                sb.magbits = gb[c]+(params[c].exp[0][0]-(mdl[c]-sb.level))-1;
            }
            else {
                sb.magbits = gb[c]+params[c].exp[sb.resLvl][sb.sbandIdx]-1;
            }
        }
        else {
            initSubbandsFields(c,(SubbandSyn)sb.getLL());
            initSubbandsFields(c,(SubbandSyn)sb.getHL());
            initSubbandsFields(c,(SubbandSyn)sb.getLH());
            initSubbandsFields(c,(SubbandSyn)sb.getHH());
        }
    }

    /**
     * Returns the image resolution level to reconstruct from the
     * codestream. This value cannot be computed before every main and tile
     * headers are read.
     *
     * @return The image  resolution level
     * */
    public int getImgRes(){
	return targetRes;
    }

    /**
     * Return the target decoding rate in bits per pixel.
     *
     * @return Target decoding rate in bpp.
     * */
    public float getTargetRate(){
        return trate;
    }

    /**
     * Return the actual decoding rate in bits per pixel.
     *
     * @return Actual decoding rate in bpp.
     * */
    public float getActualRate() {
        arate = anbytes*8f/hd.getMaxCompImgWidth()/hd.getMaxCompImgHeight();
        return arate;
    }

    /**
     * Return the target number of read bytes.
     *
     * @return Target decoding rate in bytes.
     * */
    public int getTargetNbytes(){
        return tnbytes;
    }

    /**
     * Return the actual number of read bytes.
     *
     * @return Actual decoding rate in bytes.
     * */
    public int getActualNbytes(){
        return anbytes;
    }

    /** Returns the horizontal offset of tile partition */
    public int getTilePartULX() {
        return hd.getTilingOrigin(null).x;
    }

    /** Returns the vertical offset of tile partition */
    public int getTilePartULY() {
        return hd.getTilingOrigin(null).y;
    }

    /** Returns the nominal tile width */
    public int getNomTileWidth() {
        return hd.getNomTileWidth();
    }

    /** Returns the nominal tile height */
    public int getNomTileHeight() {
        return hd.getNomTileHeight();
    }
}
