/*
 * $RCSfile: BitToByteOutput.java,v $
 * $Revision: 1.1 $
 * $Date: 2005-02-11 05:02:07 $
 * $State: Exp $
 *
 * Class:                   BitToByteOutput
 *
 * Description:             Adapter to perform bit based output on a byte
 *                          based one.
 *
 *
 *
 * 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.entropy.encoder;

import jj2000.j2k.io.*;

/**
 * This class provides an adapter to perform bit based output on byte based
 * output objects that inherit from a 'ByteOutputBuffer' class. This class
 * implements the bit stuffing policy needed for the 'selective arithmetic
 * coding bypass' mode of the entropy coder. This class also delays the output
 * of a trailing 0xFF, since they are synthetized be the decoder.
 * */
class BitToByteOutput {

    /** Whether or not predictable termination is requested. This value is
     * important when the last byte before termination is an 0xFF  */
    private boolean isPredTerm = false;

    /** The alternating sequence of 0's and 1's used for byte padding */
    static final int PAD_SEQ = 0x2A;

    /** Flag that indicates if an FF has been delayed */
    boolean delFF = false;

    /** The byte based output */
    ByteOutputBuffer out;

    /** The bit buffer */
    int bbuf;

    /** The position of the next bit to put in the bit buffer. When it is 7
     * the bit buffer 'bbuf' is empty. The value should always be between 7
     * and 0 (i.e. if it gets to -1, the bit buffer should be immediately
     * written to the byte output). */
    int bpos = 7;

    /** The number of written bytes (excluding the bit buffer) */
    int nb = 0;

    /**
     * Instantiates a new 'BitToByteOutput' object that uses 'out' as the
     * underlying byte based output.
     *
     * @param out The underlying byte based output
     * */
    BitToByteOutput(ByteOutputBuffer out) {
        this.out = out;
    }

    /**
     * Writes to the bit stream the symbols contained in the 'symbuf'
     * buffer. The least significant bit of each element in 'symbuf'is
     * written.
     *
     * @param symbuf The symbols to write
     *
     * @param nsym The number of symbols in symbuf
     * */
    final void writeBits(int[] symbuf, int nsym) {
        int i;
        int bbuf,bpos;
        bbuf = this.bbuf;
        bpos = this.bpos;
        // Write symbol by symbol to bit buffer
        for (i=0; i<nsym; i++) {
            bbuf |= (symbuf[i]&0x01)<<(bpos--);
            if (bpos<0) { // Bit buffer is full, write it
                if (bbuf != 0xFF) { // No bit-stuffing needed
                    if (delFF) { // Output delayed 0xFF if any
                        out.write(0xFF);
                        delFF = false;
                        nb++;
                    }
                    out.write(bbuf);
                    nb++;
                    bpos = 7;
                }
                else { // We need to do bit stuffing on next byte
                    delFF = true;
                    bpos = 6; // One less bit in next byte
                }
                bbuf = 0;
            }
        }
        this.bbuf = bbuf;
        this.bpos = bpos;
    }

    /**
     * Write a bit to the output. The least significant bit of 'bit' is
     * written to the output.
     *
     * @param bit
     * */
    final void writeBit(int bit) {
        bbuf |= (bit&0x01)<<(bpos--);
        if (bpos<0) {
            if (bbuf != 0xFF) { // No bit-stuffing needed
                if (delFF) { // Output delayed 0xFF if any
                    out.write(0xFF);
                    delFF = false;
                    nb++;
                }
                // Output the bit buffer
                out.write(bbuf);
                nb++;
                bpos = 7;
            }
            else { // We need to do bit stuffing on next byte
                delFF = true;
                bpos = 6; // One less bit in next byte
            }
            bbuf = 0;
        }
    }

    /**
     * Writes the contents of the bit buffer and byte aligns the output by
     * filling bits with an alternating sequence of 0's and 1's.
     * */
    void flush() {
        if (delFF) { // There was a bit stuffing
            if (bpos != 6) { // Bit buffer is not empty
                // Output delayed 0xFF
                out.write(0xFF);
                delFF = false;
                nb++;
                // Pad to byte boundary with an alternating sequence of 0's
                // and 1's.
                bbuf |= (PAD_SEQ >>> (6-bpos));
                // Output the bit buffer
                out.write(bbuf);
                nb++;
                bpos = 7;
                bbuf = 0;
            }else if(isPredTerm) {
                out.write(0xFF);
                nb++;
                out.write(0x2A);
                nb++;
                bpos = 7;
                bbuf = 0;
                delFF = false;
            }
        }
        else { // There was no bit stuffing
            if (bpos != 7) { // Bit buffer is not empty
                // Pad to byte boundary with an alternating sequence of 0's and
                // 1's.
                bbuf |= (PAD_SEQ >>> (6-bpos));
                // Output the bit buffer (bbuf can not be 0xFF)
                out.write(bbuf);
                nb++;
                bpos = 7;
                bbuf = 0;
            }
        }
    }

    /**
     * Terminates the bit stream by calling 'flush()' and then 'reset()'.
     * */
    public int terminate() {
        flush();
	int savedNb = nb;
        reset();
	return savedNb;
    }

    /**
     * Resets the bit buffer to empty, without writing anything to the
     * underlying byte output, and resets the byte count. The underlying byte
     * output is NOT reset.
     * */
    void reset() {
        delFF = false;
        bpos = 7;
        bbuf = 0;
        nb = 0;
    }

    /**
     * Returns the length, in bytes, of the output bit stream as written by
     * this object. If the output bit stream does not have an integer number
     * of bytes in length then it is rounded to the next integer.
     *
     * @return The length, in bytes, of the output bit stream.
     * */
    int length() {
        if (delFF) {
            // If bit buffer is empty we just need 'nb' bytes. If not we need
            // the delayed FF and the padded bit buffer.
            return nb + 2;
        }
        else {
            // If the bit buffer is empty, we just need 'nb' bytes. If not, we
            // add length of the padded bit buffer
            return nb + ((bpos==7) ? 0 : 1);
        }
    }

    /** 
     * Set the flag according to whether or not the predictable termination is
     * requested.
     *
     * @param isPredTerm Whether or not predictable termination is requested.
     * */
    void setPredTerm(boolean isPredTerm) {
        this.isPredTerm = isPredTerm;
    }

}
