/*	$Id: MemoryDecoder.c++,v 1.3 2004/08/09 19:29:58 lhoward Exp $ */
/*
 * Copyright (c) 1994-1996 Sam Leffler
 * Copyright (c) 1994-1996 Silicon Graphics, Inc.
 * HylaFAX is a trademark of Silicon Graphics
 *
 * Permission to use, copy, modify, distribute, and sell this software and 
 * its documentation for any purpose is hereby granted without fee, provided
 * that (i) the above copyright notices and this permission notice appear in
 * all copies of the software and related documentation, and (ii) the names of
 * Sam Leffler and Silicon Graphics may not be used in any advertising or
 * publicity relating to the software without the specific, prior written
 * permission of Sam Leffler and Silicon Graphics.
 * 
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
 * 
 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
 * OF THIS SOFTWARE.
 */
/*
 * Phase C data correction and page chopping support.
 */

#include "MemoryDecoder.h"
#include "G3Encoder.h"
#include "StackBuffer.h"

MemoryDecoder::MemoryDecoder(u_char* data, u_long n)
{
    bp = data;
    cc = n;
    endOfData = NULL;
    nblanks = 0;
    runs = NULL;
    rowBuf = NULL;
}

MemoryDecoder::MemoryDecoder(u_char* data, u_int wid, u_long n,
                             u_int order, bool twoDim, bool mmr)
{
    bp         = data;
    width      = wid;
    byteWidth  = howmany(width, 8);
    cc         = n;
    
    fillorder  = order;
    is2D       = twoDim;
    isG4       = mmr;

    runs      = new tiff_runlen_t[2*width];      // run arrays for cur+ref rows
    rowBuf    = new u_char[byteWidth];
    setupDecoder(fillorder, is2D, isG4);
    setRuns(runs, runs+width, width);
}

MemoryDecoder::~MemoryDecoder()
{
    if (rowBuf)
	delete rowBuf;
    if (runs)
	delete runs;
}

int
MemoryDecoder::decodeNextByte()
{
    if (cc == 0)
        raiseRTC();                     // XXX don't need to recognize EOF
    cc--;
    return (*bp++);
}

int
MemoryDecoder::getLastByte()
{
    return (*(endOfData - 1));
}

void
MemoryDecoder::invalidCode(const char* type, int x)
{
    printf("Invalid %s code word, x %d\n", type, x);
}        
void
MemoryDecoder::badPixelCount(const char* type, int got, int expected)  
{
    if (!seenRTC())
	printf("Bad %s pixel count, got %d, expected %d\n",
	    type, got, expected);
}
void
MemoryDecoder::badDecodingState(const char* type, int x)
{
    printf("Panic, bad %s decoding state, x %d\n", type, x);
}

static bool
isBlank(tiff_runlen_t* runs, u_int rowpixels)
{
    u_int x = 0;
    for (;;) {
	if ((x += *runs++) >= rowpixels)
	    break;
	if (runs[0] != 0)
	    return (false);
	if ((x += *runs++) >= rowpixels)
	    break;
    }
    return (true);
}

void
MemoryDecoder::scanPageForBlanks(u_int fillorder, const Class2Params& params)
{
    setupDecoder(fillorder,  params.is2D(), (params.df == DF_2DMMR));
    u_int rowpixels = params.pageWidth();	// NB: assume rowpixels <= 4864
    tiff_runlen_t runs[2*4864];			// run arrays for cur+ref rows
    setRuns(runs, runs+4864, rowpixels);

    if (!RTCraised()) {
	/*
	 * Skip a 1" margin at the top of the page before
	 * scanning for trailing white space.  We do this
	 * to ensure that there is always enough space on
	 * the page to image a tag line and to satisfy a
	 * fax machine that is incapable of imaging to the
	 * full extent of the page.
	 */
	u_int topMargin = 1*98;			// 1" at 98 lpi
	switch (params.vr) {
	    case VR_FINE:
	    case VR_200X200:
		topMargin *= 2;			// 196 lpi =>'s twice as many
		break;
	    case VR_300X300:
		topMargin *= 3;
		break;
	    case VR_R8:
	    case VR_R16:
	    case VR_200X400:
		topMargin *= 4;
		break;
	}
	do {
	    (void) decodeRow(NULL, rowpixels);
	} while (--topMargin);
	/*
	 * Scan the remainder of the page data and calculate
	 * the number of blank lines at the bottom.
	 */
	for (;;) {
	    (void) decodeRow(NULL, rowpixels);
	    if (isBlank(lastRuns(), rowpixels)) {
		endOfData = bp;			// include one blank row
		nblanks = 0;
		do {
		    nblanks++;
		    (void) decodeRow(NULL, rowpixels);
		} while (isBlank(lastRuns(), rowpixels));
	    }
	}
    }
}

#ifdef roundup
#undef roundup
#endif
#define roundup(a,b)    ((((a)+((b)-1))/(b))*(b))

/*
 * TIFF Class F specs say:
 *
 * "As illustrated in FIGURE 1/T.4 in Recommendation T.4 (the Red
 * Book, page 20), facsimile documents begin with an EOL (which in
 * Class F is byte-aligned)..."
 *
 * This is wrong! "Byte-aligned" first EOL means extra zero bits
 * which are not allowed by T.4. Reencode first row to fix this
 * "byte-alignment".
 */
void MemoryDecoder::fixFirstEOL()
{
    fxStackBuffer result;
    G3Encoder enc(result);
    enc.setupEncoder(fillorder, is2D, isG4);
    
    memset(rowBuf, 0, byteWidth*sizeof(u_char)); // clear row to white
    if(!RTCraised()) {
        u_char* start = current();
        (void)decodeRow(rowBuf, width);
        /*
         * syncronize to the next EOL and calculate pointer to it
         * (see detailed explanation of look_ahead in encodeTagLine())
         */
        (void)isNextRow1D();
        u_int look_ahead = roundup(getPendingBits(),8) / 8;
        u_int decoded = current() - look_ahead - start;

        enc.encode(rowBuf, width, 1);
	enc.encoderCleanup();
        u_int encoded = result.getLength();
            
        while( encoded < decoded ){
            result.put((char) 0);
            encoded++;
        }
        if( encoded == decoded ){
            memcpy(start, (const char*)result, encoded);
        }
    }
}

/*
 * TIFF Class F specs say:
 *
 * "Aside from EOL's, TIFF Class F files contain only image data. This
 * means that the Return To Control sequence (RTC) is specifically
 * prohibited..."
 *
 * Nethertheless Ghostscript and possibly other TIFF Class F writers
 * append RTC or single EOL to the last encoded line. Remove them.
 */
u_char* MemoryDecoder::cutExtraRTC()
{
    u_char* start = current();
    
    /*
     * We expect RTC near the end of data and thus
     * do not check all image to save processing time.
     * It's safe because we will resync on the first 
     * encountered EOL.
     *
     * NB: We expect G3Decoder::data==0 and
     * G3Decoder::bit==0 (no data in the accumulator).
     * As we cannot explicitly clear the accumulator
     * (bit and data are private), cutExtraRTC()
     * should be called immediately after
     * MemoryDecoder() constructing.
     */
    const u_long CheckArea = 20;
    if( cc > CheckArea ){
        bp += (cc-CheckArea);
        cc = CheckArea;
    }
        
    endOfData = NULL;
    if(!RTCraised()) {
        /*
         * syncronize to the next EOL and calculate pointer to it
         * (see detailed explanation of look_ahead in encodeTagLine())
         */
        (void)isNextRow1D();
        u_int look_ahead = roundup(getPendingBits(),8) / 8;
        endOfData = current() - look_ahead;
        for (;;) {
            if( decodeRow(NULL, width) ){
                /*
                 * endOfData is now after last good row. Thus we correctly handle
                 * RTC, single EOL in the end, or no RTC/EOL at all
                 */
                endOfData = current();
            }
            if( seenRTC() )
                break;
        }
    }
    return endOfData;
}

u_char* MemoryDecoder::cutExtraEOFB()
{
    /*
     * MMR requires us to decode the entire image...
     */
    endOfData = NULL;
    if(!RTCraised()) {
	endOfData = current();
        for (;;) {
            if( decodeRow(NULL, width) ){
                endOfData = current();
            }
            if( seenRTC() )
                break;
        }
    }
    if (seenRTC() && *(endOfData - 1) == 0x00)
	endOfData--;	// step back over the first byte of EOFB, lastbyte must be non-zero!
    return endOfData;
}

u_char* MemoryDecoder::encodeTagLine(u_long* raster, u_int th, u_int slop)
{
    /*
     * Decode (and discard) the top part of the page where
     * the tag line is to be imaged.  Note that we assume
     * the strip of raw data has enough scanlines in it
     * to satisfy our needs (caller is responsible).
     *
     * ... and then...
     *
     * Encode the result according to the parameters of
     * the outgoing page.  Note that the encoded data is
     * written in the bit order of the page data since
     * it must be merged back with it below.
     */
    fxStackBuffer result;
    G3Encoder enc(result);
    enc.setupEncoder(fillorder, is2D, isG4);

    u_char* start = current();
    decode(NULL, width, th);		// discard decoded data
    if (!isG4) {
	/*
	 * If the source is 2D-encoded and the decoding done
	 * above leaves us at a row that is 2D-encoded, then
	 * our re-encoding below will generate a decoding
	 * error if we don't fix things up.  Thus we discard
	 * up to the next 1D-encoded scanline.  (We could
	 * instead decode the rows and re-encoded them below
	 * but to do that would require decoding above instead
	 * of skipping so that the reference line for the
	 * 2D-encoded rows is available.)
	 */
	u_int n;
	for (n = 0; n < 4 && !isNextRow1D(); n++)
	    decodeRow(NULL, width);
	th += n;			// compensate for discarded rows
	/*
	 * Things get tricky trying to identify the last byte in
	 * the decoded data that we want to replace.  The decoder
	 * must potentially look ahead to see the zeros that
	 * makeup the EOL that marks the end of the data we want
	 * to skip over.  Consequently current() must be
	 * adjusted by the look ahead, a factor of the number of
	 * bits pending in the G3 decoder's bit accumulator.
	 */
	u_int look_ahead = roundup(getPendingBits(),8) / 8;
	u_int decoded = current() - look_ahead - bp;
	enc.encode(raster, width, th);
	enc.encoderCleanup();
	delete raster;
	/*
	 * To properly join the newly encoded data and the previous
	 * data we need to insert two bytes of zero-fill prior to
	 * the start of the old data to ensure 11 bits of zero exist
	 * prior to the EOL code in the first line of data that
	 * follows what we skipped over above.  Note that this
	 * assumes the G3 decoder always stops decoding prior to
	 * an EOL code and that we've adjusted the byte count to the
	 * start of the old data so that the leading bitstring is
	 * some number of zeros followed by a 1.
	 */
	result.put((char) 0);
	result.put((char) 0);
	/*
	 * Copy the encoded raster with the tag line back to
	 * the front of the buffer that was passed in.  The
	 * caller has preallocated a hunk of space for us to
	 * do this and we also reuse space occupied by the
	 * original encoded raster image.  If insufficient space
	 * exists for the newly encoded tag line, then we jam
	 * as much as will fit w/o concern for EOL markers;
	 * this will cause at most one bad row to be received
	 * at the receiver (got a better idea?).
	 */
	u_int encoded = result.getLength();
	if (encoded > slop + decoded)
	    encoded = slop + decoded;
	u_char* dst = bp + (int)(decoded-encoded);
	memcpy(dst, (const unsigned char*) result, encoded);
	return (dst);
    } else {
	u_char* refrow = new u_char[byteWidth*sizeof(u_char)];	// reference row
	memset(refrow, 0, byteWidth*sizeof(u_char));		// clear to white
	enc.encode(raster, width, th, (unsigned char*) refrow);
	/*
	 * refrow does not need to be altered now to match the 
	 * last line of raster because the raster contains MARGIN_BOT
	 * blank lines.
	 */
	delete raster;
	if (!RTCraised()) {
	    for (;;) {
		(void) decodeRow(rowBuf, width);
		if(seenRTC())
		    break;
		enc.encode(rowBuf, width, 1, (unsigned char*) refrow);
		memcpy(refrow, rowBuf, byteWidth*sizeof(u_char));
	    }
	}
	enc.encoderCleanup();
	cc = result.getLength();
	u_char* dst = new u_char[cc];
	memcpy(dst, (const unsigned char*) result, cc);
	return (dst);
    }
}

u_char* MemoryDecoder::convertDataFormat(const Class2Params& params)
{
    /*
     * Convert data to the format specified in params.  The decoder has already
     * been set up, and we don't need to worry about decoder operation here.  
     * These params are for the encoder to use.
     */
    fxStackBuffer result;
    G3Encoder enc(result);
    enc.setupEncoder(fillorder, params.is2D(), (params.df == DF_2DMMR));

    u_char* refrow = new u_char[byteWidth*sizeof(u_char)];	// reference row
    memset(refrow, 0, byteWidth*sizeof(u_char));		// clear to white

    /*
     * For MR we encode a 1-D or 2-D line according to T.4 4.2.1.
     * We understand that "standard" resolution means VR_NORMAL.
     */
    u_short k = 0;

    if (!RTCraised()) {
	for (;;) {
	    (void) decodeRow(rowBuf, width);
	    if(seenRTC())
		break;
	    // encode the line specific to the desired format
	    if (params.df == DF_2DMMR) {
		enc.encode(rowBuf, width, 1, (unsigned char*) refrow);
	    } else if (params.df == DF_2DMR) {
		if (k) {
		    enc.encode(rowBuf, width, 1, (unsigned char*) refrow);	// 2-D
		} else {
		    enc.encode(rowBuf, width, 1);				// 1-D
		    k = (params.vr == VR_NORMAL || params.vr == VR_200X100) ? 2 : 4;
		}
		k--;
	    } else {	// DF_1DMH
		enc.encode(rowBuf, width, 1);
	    }
	    memcpy(refrow, rowBuf, byteWidth*sizeof(u_char));
	}
    }
    enc.encoderCleanup();
    cc = result.getLength();
    u_char* dst = new u_char[cc];
    memcpy(dst, (const unsigned char*) result, cc);
    return (dst);
}
