/*
 * SpanDSP - a series of DSP components for telephony
 *
 * t85_decode.c - ITU T.85 JBIG for FAX image decompression
 *
 * Written by Steve Underwood <steveu@coppice.org>
 *
 * Copyright (C) 2009, 2010 Steve Underwood
 *
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 2.1,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*! \file */

#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif

#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if defined(HAVE_STDBOOL_H)
#include <stdbool.h>
#else
#include "spandsp/stdbool.h"
#endif

#include "spandsp/telephony.h"
#include "spandsp/alloc.h"
#include "spandsp/logging.h"
#include "spandsp/async.h"
#include "spandsp/timezone.h"
#include "spandsp/t4_rx.h"
#include "spandsp/t4_tx.h"
#include "spandsp/t81_t82_arith_coding.h"
#include "spandsp/t85.h"

#include "spandsp/private/logging.h"
#include "spandsp/private/t81_t82_arith_coding.h"
#include "spandsp/private/t85.h"

static __inline__ int32_t pack_32(const uint8_t *s)
{
    int32_t value;

    value = (((int32_t) s[0] << 24) | ((int32_t) s[1] << 16) | ((int32_t) s[2] << 8) | (int32_t) s[3]);
    return value;
}
/*- End of function --------------------------------------------------------*/

/* Decode some PSCD bytes, output the decoded rows as they are completed. Return
   the number of bytes which have actually been read. This will be less than len
   if a marker segment was part of the data or if the final byte was 0xFF, meaning
   that this code can not determine whether we have a marker segment. */
static size_t decode_pscd(t85_decode_state_t *s, const uint8_t data[], size_t len)
{
    uint8_t *hp[3];
    int32_t o;
    int cx;
    int i;
    int pix;
    int slntp;
    int buffered_rows;

    buffered_rows = (s->options & T85_LRLTWO)  ?  2  :  3;
    /* Forward data to the arithmetic decoder */
    s->s.pscd_ptr = data;
    s->s.pscd_end = data + len;

    for (s->interrupt = false;  s->i < s->l0  &&  s->y < s->yd  &&  !s->interrupt;  s->i++, s->y++)
    {
        /* Point to the current image bytes */
        for (i = 0;  i < 3;  i++)
            hp[i] = s->row_buf + s->p[i]*s->bytes_per_row + (s->x >> 3);

        /* Adaptive template changes */
        if (s->x == 0  &&  s->pseudo)
        {
            for (i = 0;  i < s->at_moves;  i++)
            {
                if (s->at_row[i] == s->i)
                    s->tx = s->at_tx[i];
            }
        }

        /* Typical prediction */
        if ((s->options & T85_TPBON)  &&  s->pseudo)
        {
            slntp = t81_t82_arith_decode(&s->s, (s->options & T85_LRLTWO)  ?  TPB2CX  :  TPB3CX);
            if (slntp < 0)
                return s->s.pscd_ptr - data;
            s->lntp = !(slntp ^ s->lntp);
            if (!s->lntp)
            {
                /* This row is 'typical' (i.e. identical to the previous one) */
                if (s->p[1] < 0)
                {
                    /* First row of page or (following SDRST) of stripe */
                    for (i = 0;  i < s->bytes_per_row;  i++)
                        hp[0][i] = 0;
                    s->interrupt = s->row_write_handler(s->row_write_user_data, hp[0], s->bytes_per_row);
                    /* Rotate the ring buffer that holds the last few rows */
                    s->p[2] = s->p[1];
                    s->p[1] = s->p[0];
                    if (++(s->p[0]) >= buffered_rows)
                        s->p[0] = 0;
                }
                else
                {
                    s->interrupt = s->row_write_handler(s->row_write_user_data, hp[1], s->bytes_per_row);
                    /* Duplicate the last row in the ring buffer */
                    s->p[2] = s->p[1];
                }
                continue;
            }
            /* This row is 'not typical' and has to be coded completely */
        }
        s->pseudo = false;

        if (s->x == 0)
        {
            s->row_h[0] = 0;
            s->row_h[1] = (s->p[1] >= 0)  ?  ((int32_t) hp[1][0] << 8)  :  0;
            s->row_h[2] = (s->p[2] >= 0)  ?  ((int32_t) hp[2][0] << 8)  :  0;
        }

        /* Decode row */
        while (s->x < s->xd)
        {
            if ((s->x & 7) == 0)
            {
                if (s->x < (s->bytes_per_row - 1)*8  &&  s->p[1] >= 0)
                {
                    s->row_h[1] |= hp[1][1];
                    if (s->p[2] >= 0)
                        s->row_h[2] |= hp[2][1];
                }
            }
            if ((s->options & T85_LRLTWO))
            {
                /* Two row template */
                do
                {
                    cx = (s->row_h[0] & 0x00F);
                    if (s->tx)
                    {
                        cx |= ((s->row_h[1] >> 9) & 0x3E0);
                        if (s->x >= (uint32_t) s->tx)
                        {
                            if (s->tx < 8)
                            {
                                cx |= ((s->row_h[0] >> (s->tx - 5)) & 0x010);
                            }
                            else
                            {
                                o = (s->x - s->tx) - (s->x & ~7);
                                cx |= (((hp[0][o >> 3] >> (7 - (o & 7))) & 1) << 4);
                            }
                        }
                    }
                    else
                    {
                        cx |= ((s->row_h[1] >> 9) & 0x3F0);
                    }
                    pix = t81_t82_arith_decode(&s->s, cx);
                    if (pix < 0)
                        return s->s.pscd_ptr - data;
                    s->row_h[0] = (s->row_h[0] << 1) | pix;
                    s->row_h[1] <<= 1;
                }
                while ((++s->x & 7)  &&  s->x < s->xd);
            }
            else
            {
                /* Three row template */
                do
                {
                    cx = ((s->row_h[2] >> 7) & 0x380) | (s->row_h[0] & 0x003);
                    if (s->tx)
                    {
                        cx |= ((s->row_h[1] >> 11) & 0x078);
                        if (s->x >= (uint32_t) s->tx)
                        {
                            if (s->tx < 8)
                            {
                                cx |= ((s->row_h[0] >> (s->tx - 3)) & 0x004);
                            }
                            else
                            {
                                o = (s->x - s->tx) - (s->x & ~7);
                                cx |= (((hp[0][o >> 3] >> (7 - (o & 7))) & 1) << 2);
                            }
                        }
                    }
                    else
                    {
                        cx |= ((s->row_h[1] >> 11) & 0x07C);
                    }
                    pix = t81_t82_arith_decode(&s->s, cx);
                    if (pix < 0)
                        return s->s.pscd_ptr - data;
                    s->row_h[0] = (s->row_h[0] << 1) | pix;
                    s->row_h[1] <<= 1;
                    s->row_h[2] <<= 1;
                }
                while ((++s->x & 7)  &&  s->x < s->xd);
            }
            *hp[0]++ = s->row_h[0];
            hp[1]++;
            hp[2]++;
        }
        *(hp[0] - 1) <<= (s->bytes_per_row*8 - s->xd);
        s->interrupt = s->row_write_handler(s->row_write_user_data, &s->row_buf[s->p[0]*s->bytes_per_row], s->bytes_per_row);
        s->x = 0;
        s->pseudo = true;
        /* Shuffle the row buffers */
        s->p[2] = s->p[1];
        s->p[1] = s->p[0];
        if (++(s->p[0]) >= buffered_rows)
            s->p[0] = 0;
    }
    return s->s.pscd_ptr - data;
}
/*- End of function --------------------------------------------------------*/

static int finish_sde(t85_decode_state_t *s)
{
    /* Decode final pixels based on trailing zero bytes */
    s->s.nopadding = false;
    if (decode_pscd(s, s->buffer, 2) != 2  &&  s->interrupt)
        return 1;

    /* Prepare decoder for next SDE */
    t81_t82_arith_decode_restart(&s->s, s->buffer[1] == T82_SDNORM);
    s->s.nopadding = s->options & T85_VLENGTH;

    s->x = 0;
    s->i = 0;
    s->pseudo = true;
    s->at_moves = 0;
    if (s->buffer[1] == T82_SDRST)
    {
        s->tx = 0;
        s->lntp = true;
        s->p[0] = 0;
        s->p[1] = -1;
        s->p[2] = -1;
    }

    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(bool) t85_analyse_header(uint32_t *width, uint32_t *length, const uint8_t data[], size_t len)
{
    uint32_t i;
    uint32_t skip;

    if (len < 20)
    {
        *width = 0;
        *length = 0;
        return false;
    }
    *width = pack_32(&data[6]);
    *length = pack_32(&data[10]);
    if ((data[19] & T85_VLENGTH))
    {
        /* There should be an image length sequence terminating the image later on. */
        /* TODO: scan for a true length, instead of this fudge */
        for (i = 20;  i < len - 6;  i++)
        {
            if (data[i] == T82_ESC)
            {
                if (data[i + 1] == T82_COMMENT)
                {
                    skip = pack_32(&data[2]);
                    if ((skip + 6) > (len - i))
                        break;
                    i += (6 + skip - 1);
                }
                else if (data[i + 1] == T82_ATMOVE)
                {
                    i += (8 - 1);
                }
                else if (data[i + 1] == T82_NEWLEN)
                {
                    /* We are only allow to have one of these, so if we find one
                       we should not look any further. */
                    *length = pack_32(&data[i + 2]);
                    break;
                }
            }
        }
    }
    return true;
}
/*- End of function --------------------------------------------------------*/

static int extract_bih(t85_decode_state_t *s)
{
    /* Check that the fixed parameters have the values they are expected to be
       fixed at - see T.85/Table 1 */
    /* DL - Initial layer to be transmitted */
    /* D - Number of differential layers */
    /* Unspecified byte */
    /* MY - Maximum vertical offset allowed for AT pixel */
    /* Order byte */
    if (s->buffer[0] != 0
        ||
        s->buffer[1] != 0
        ||
        s->buffer[3] != 0
        ||
        s->buffer[17] != 0
        ||
#if T85_STRICT_ORDER_BITS
        s->buffer[18] != 0)
#else
        (s->buffer[18] & 0xF0) != 0)
#endif
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. Fixed bytes do not contain expected values.\n");
        s->end_of_data = 2;
        return T4_DECODE_INVALID_DATA;
    }
    /* P - Number of bit planes */
    s->bit_planes = s->buffer[2];
    s->current_bit_plane = 0;
    /* Now look at the stuff which actually counts in a T.85 header. */
    /* XD - Horizontal image size at layer D */
    s->xd = pack_32(&s->buffer[4]);
    /* YD - Vertical image size at layer D */
    s->yd = pack_32(&s->buffer[8]);
    /* L0 - Rows per stripe, at the lowest resolution */
    s->l0 = pack_32(&s->buffer[12]);
    /* MX - Maximum horizontal offset allowed for AT pixel */
    s->mx = s->buffer[16];
    /* Options byte */
    s->options = s->buffer[19];

    if (s->bit_planes < s->min_bit_planes  ||  s->bit_planes > s->max_bit_planes)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. %d bit planes. Should be %d to %d.\n", s->bit_planes, s->min_bit_planes, s->max_bit_planes);
        s->end_of_data = 2;
        return T4_DECODE_INVALID_DATA;
    }
    if (s->xd == 0  ||  (s->max_xd  &&  s->xd > s->max_xd))
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. Width is %" PRIu32 "\n", s->xd);
        s->end_of_data = 2;
        return T4_DECODE_INVALID_DATA;
    }
    if (s->yd == 0)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. Length is %" PRIu32 "\n", s->yd);
        s->end_of_data = 2;
        return T4_DECODE_INVALID_DATA;
    }
    if (s->max_yd)
    {
        if ((s->options & T85_VLENGTH))
        {
            if (s->yd > s->max_yd)
                s->yd = s->max_yd;
        }
        else
        {
            if (s->yd > s->max_yd)
            {
                span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. Length is %" PRIu32 "\n", s->yd);
                s->end_of_data = 2;
                return T4_DECODE_INVALID_DATA;
            }
        }
    }
    if (s->l0 == 0)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. L0 is %" PRIu32 "\n", s->l0);
        s->end_of_data = 2;
        return T4_DECODE_INVALID_DATA;
    }
    if (s->mx > 127)
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. MX is %d\n", s->mx);
        s->end_of_data = 2;
        return T4_DECODE_INVALID_DATA;
    }
    if ((s->options & ~(T85_LRLTWO | T85_VLENGTH | T85_TPBON)))
    {
        span_log(&s->logging, SPAN_LOG_FLOW, "BIH invalid. Options are 0x%X\n", s->options);
        s->end_of_data = 2;
        return T4_DECODE_INVALID_DATA;
    }
    span_log(&s->logging, SPAN_LOG_FLOW, "BIH is OK. Image is %" PRIu32 "x%" PRIu32 " pixels\n", s->xd, s->yd);
    return T4_DECODE_OK;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(void) t85_decode_rx_status(t85_decode_state_t *s, int status)
{
    span_log(&s->logging, SPAN_LOG_FLOW, "Signal status is %s (%d)\n", signal_status_to_str(status), status);
    switch (status)
    {
    case SIG_STATUS_TRAINING_IN_PROGRESS:
    case SIG_STATUS_TRAINING_FAILED:
    case SIG_STATUS_TRAINING_SUCCEEDED:
    case SIG_STATUS_CARRIER_UP:
        /* Ignore these */
        break;
    case SIG_STATUS_CARRIER_DOWN:
    case SIG_STATUS_END_OF_DATA:
        /* Finalise the image */
        t85_decode_put(s, NULL, 0);
        break;
    default:
        span_log(&s->logging, SPAN_LOG_WARNING, "Unexpected rx status - %d!\n", status);
        break;
    }
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t85_decode_put(t85_decode_state_t *s, const uint8_t data[], size_t len)
{
    int ret;
    uint32_t y;
    uint8_t *buf;
    size_t bytes_per_row;
    size_t min_len;
    size_t chunk;
    size_t cnt;
    int i;

    if (len == 0)
    {
        if (s->y >= s->yd)
            return T4_DECODE_OK;
        if (s->end_of_data > 0)
            return T4_DECODE_INVALID_DATA;
        /* This is the end of image condition */
        s->end_of_data = 1;
    }

    s->compressed_image_size += len;
    cnt = 0;

    if (s->bie_len < 20)
    {
        /* Read in the 20-byte BIH */
        i = (s->bie_len + len > 20)  ?  (20 - s->bie_len)  :  len;
        memcpy(&s->buffer[s->bie_len], data, i);
        s->bie_len += i;
        cnt = i;
        if (s->bie_len < 20)
            return T4_DECODE_MORE_DATA;
        if ((ret = extract_bih(s)) != T4_DECODE_OK)
            return ret;
        /* Set up the two/three row buffer */
        bytes_per_row = (s->xd + 7) >> 3;
        min_len = ((s->options & T85_LRLTWO)  ?  2  :  3)*bytes_per_row;
        if (min_len > s->row_buf_len)
        {
            /* We need to expand the 3 row buffer */
            if ((buf = (uint8_t *) span_realloc(s->row_buf, min_len)) == NULL)
                return T4_DECODE_NOMEM;
            s->row_buf = buf;
            s->row_buf_len = min_len;
        }

        t81_t82_arith_decode_init(&s->s);
        s->s.nopadding = s->options & T85_VLENGTH;
        if (s->comment)
        {
            span_free(s->comment);
            s->comment = NULL;
        }
        s->comment_len = 0;
        s->comment_progress = 0;
        s->buf_len = 0;
        s->buf_needed = 2;
        s->x = 0;
        s->y = 0;
        s->i = 0;
        s->pseudo = true;
        s->at_moves = 0;
        s->tx = 0;
        s->lntp = true;
        s->bytes_per_row = bytes_per_row;
        s->p[0] = 0;
        s->p[1] = -1;
        s->p[2] = -1;
    }

    /* BID processing loop */
    while (cnt < len  ||  s->end_of_data == 1)
    {
        if (s->end_of_data == 1)
        {
            s->buf_needed = 2;
            s->options &= ~T85_VLENGTH;
            s->end_of_data = 2;
        }
        if (s->comment_len)
        {
            /* We are in a COMMENT. Absorb its contents */
            chunk = len - cnt;
            if ((s->comment_progress + chunk) >= s->comment_len)
            {
                /* Finished */
                chunk = s->comment_len - s->comment_progress;
                /* If the comment was too long to be passed to the handler, we still
                   call the handler with the buffer set to NULL, so it knows a large
                   comment has occurred. */
                if (s->comment)
                    memcpy(&s->comment[s->comment_progress], &data[cnt], chunk);
                if (s->comment_handler)
                    s->interrupt = s->comment_handler(s->comment_user_data, s->comment, s->comment_len);
                if (s->comment)
                {
                    span_free(s->comment);
                    s->comment = NULL;
                }
                s->comment_len = 0;
                s->comment_progress = 0;
            }
            else
            {
                if (s->comment)
                    memcpy(&s->comment[s->comment_progress], &data[cnt], chunk);
                s->comment_progress += chunk;
            }
            cnt += chunk;
            continue;
        }

        /* Load marker segments into s->buffer for processing */
        if (s->buf_len > 0)
        {
            /* We are in a marker of some kind. Load the first 2 bytes of
               the marker, so we can determine its type, and hence its full
               length. */
            while (s->buf_len < s->buf_needed  &&  cnt < len)
                s->buffer[s->buf_len++] = data[cnt++];
            /* Check we have enough bytes to see the message type */
            if (s->buf_len < s->buf_needed)
                continue;
            switch (s->buffer[1])
            {
            case T82_STUFF:
                /* Forward stuffed 0xFF to arithmetic decoder. This is likely to be
                   the commonest thing for us to hit here. */
                decode_pscd(s, s->buffer, 2);
                s->buf_len = 0;
                if (s->interrupt)
                    return T4_DECODE_INTERRUPT;
                break;
            case T82_ABORT:
                s->buf_len = 0;
                return T4_DECODE_ABORTED;
            case T82_COMMENT:
                s->buf_needed = 6;
                if (s->buf_len < 6)
                    continue;
                s->buf_needed = 2;
                s->buf_len = 0;

                s->comment_len = pack_32(&s->buffer[2]);
                /* Only try to buffer and process the comment's contents if we have
                   a defined callback routine to do something with it. */
                /* If this allocate fails we carry on working just fine, and don't try to
                   process the contents of the comment. That is fairly benign, as
                   the comments are not generally of critical importance, so let's
                   not worry. */
                if (s->comment_handler  &&  s->comment_len > 0  &&  s->comment_len <= s->max_comment_len)
                    s->comment = span_alloc(s->comment_len);
                s->comment_progress = 0;
                continue;
            case T82_ATMOVE:
                s->buf_needed = 8;
                if (s->buf_len < 8)
                    continue;
                s->buf_needed = 2;
                s->buf_len = 0;
                if (s->at_moves >= T85_ATMOVES_MAX)
                {
                    s->end_of_data = 2;
                    return T4_DECODE_INVALID_DATA;
                }
                s->at_row[s->at_moves] = pack_32(&s->buffer[2]);
                s->at_tx[s->at_moves] = s->buffer[6];
                if (s->at_tx[s->at_moves] > s->mx
                    ||
                    (s->at_tx[s->at_moves] > 0  &&  s->at_tx[s->at_moves] < ((s->options & T85_LRLTWO)  ?  5  :  3))
                    ||
                    s->buffer[7] != 0)
                {
                    s->end_of_data = 2;
                    return T4_DECODE_INVALID_DATA;
                }
                s->at_moves++;
                break;
            case T82_NEWLEN:
                s->buf_needed = 6;
                if (s->buf_len < 6)
                    continue;
                s->buf_needed = 2;
                s->buf_len = 0;
                if (!(s->options & T85_VLENGTH))
                {
                    s->end_of_data = 2;
                    return T4_DECODE_INVALID_DATA;
                }
                s->options &= ~T85_VLENGTH;
                y = pack_32(&s->buffer[2]);
                /* An update to the image length is not allowed to stretch it. */
                if (y > s->yd)
                {
                    s->end_of_data = 2;
                    return T4_DECODE_INVALID_DATA;
                }
                s->yd = y;
                break;
            case T82_SDNORM:
            case T82_SDRST:
                if (!(s->options & T85_VLENGTH))
                {
                    /* A plain SDNORM or SDRST with no peek ahead required */
                    s->buf_len = 0;
                    if (finish_sde(s))
                        return T4_DECODE_INTERRUPT;
                    /* Check whether this was the last SDE */
                    if (s->y >= s->yd)
                    {
                        s->compressed_image_size -= (len - cnt);
                        return T4_DECODE_OK;
                    }
                    break;
                }
                /* This is the messy case. We need to peek ahead, as this element
                   might be immediately followed by a T82_NEWLEN which affects the
                   limit of what we decode here. */
                if (s->buf_needed < 3)
                    s->buf_needed = 3;
                if (s->buf_len < 3)
                    continue;
                /* Peek ahead to see whether a NEWLEN marker segment follows */
                if (s->buffer[2] != T82_ESC)
                {
                    /* This is not an escape sequence, so push the single peek-ahead
                       byte back into the buffer. We should always have just grabbed
                       at least one byte, so this should be safe. */
                    s->buf_needed = 2;
                    s->buf_len = 0;
                    cnt--;
                    /* Process the T82_SDNORM or T82_SDRST */
                    if (finish_sde(s))
                        return T4_DECODE_INTERRUPT;
                    /* Check whether this was the last SDE */
                    if (s->y >= s->yd)
                    {
                        s->compressed_image_size -= (len - cnt);
                        return T4_DECODE_OK;
                    }
                    break;
                }
                if (s->buf_needed < 4)
                    s->buf_needed = 4;
                if (s->buf_len < 4)
                    continue;
                if (s->buffer[3] != T82_NEWLEN)
                {
                    s->buf_needed = 2;

                    /* Process the T82_SDNORM or T82_SDRST */
                    if (finish_sde(s))
                        return T4_DECODE_INTERRUPT;
                    /* Check whether this was the last SDE */
                    if (s->y >= s->yd)
                    {
                        s->compressed_image_size -= (len - cnt);
                        return T4_DECODE_OK;
                    }
                    /* Recycle the two peek-ahead marker sequence bytes to
                       be processed later. */
                    s->buffer[0] = s->buffer[2];
                    s->buffer[1] = s->buffer[3];
                    s->buf_len = 2;
                    break;
                }
                if (s->buf_needed < 8)
                    s->buf_needed = 8;
                if (s->buf_len < 8)
                    continue;
                s->buf_needed = 2;
                s->buf_len = 0;
                /* We must have a complete T82_NEWLEN to be here, which we need
                   to process immediately. */
                s->options &= ~T85_VLENGTH;
                y = pack_32(&s->buffer[4]);
                /* An update to the image length is not allowed to stretch it. */
                if (y > s->yd)
                {
                    s->end_of_data = 2;
                    return T4_DECODE_INVALID_DATA;
                }
                /* Things look OK, so accept this new length, and proceed. */
                s->yd = y;
                /* Now process the T82_SDNORM or T82_SDRST */
                if (finish_sde(s))
                    return T4_DECODE_INTERRUPT;
                /* We might be at the end of the image now, but even if we are
                   there should still be a final training T82_SDNORM or T82_SDRST
                   that we should pick up. When we do, we won't wait for further
                   T82_NEWLEN entries, so we should stop crisply on the last byte
                   of the image. */
                break;
            default:
                s->buf_len = 0;
                s->end_of_data = 2;
                return T4_DECODE_INVALID_DATA;
            }
        }
        else if (cnt < len  &&  data[cnt] == T82_ESC)
        {
            s->buffer[s->buf_len++] = data[cnt++];
        }
        else
        {
            /* We have found PSCD bytes */
            cnt += decode_pscd(s, data + cnt, len - cnt);
            if (s->interrupt)
                return T4_DECODE_INTERRUPT;
            /* We should only have stopped processing PSCD if we ran out of data,
               or hit a T82_ESC */
            if (cnt < len  &&  data[cnt] != T82_ESC)
            {
                s->end_of_data = 2;
                return T4_DECODE_INVALID_DATA;
            }
        }
    }
    return T4_DECODE_MORE_DATA;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t85_decode_set_row_write_handler(t85_decode_state_t *s,
                                                   t4_row_write_handler_t handler,
                                                   void *user_data)
{
    s->row_write_handler = handler;
    s->row_write_user_data = user_data;
    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t85_decode_set_comment_handler(t85_decode_state_t *s,
                                                 uint32_t max_comment_len,
                                                 t4_row_write_handler_t handler,
                                                 void *user_data)
{
    s->max_comment_len = max_comment_len;
    s->comment_handler = handler;
    s->comment_user_data = user_data;
    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t85_decode_set_image_size_constraints(t85_decode_state_t *s,
                                                        uint32_t max_xd,
                                                        uint32_t max_yd)
{
    s->max_xd = max_xd;
    s->max_yd = max_yd;
    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(uint32_t) t85_decode_get_image_width(t85_decode_state_t *s)
{
    return s->xd;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(uint32_t) t85_decode_get_image_length(t85_decode_state_t *s)
{
    return s->yd;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t85_decode_get_compressed_image_size(t85_decode_state_t *s)
{
    return s->compressed_image_size*8;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t85_decode_new_plane(t85_decode_state_t *s)
{
    if (s->current_bit_plane >= s->bit_planes - 1)
        return -1;

    s->current_bit_plane++;
    s->tx = 0;
    memset(s->buffer, 0, sizeof(s->buffer));
    s->buf_len = 0;
    s->buf_needed = 0;
    s->at_moves = 0;
    memset(s->at_row, 0, sizeof(s->at_row));
    memset(s->at_tx, 0, sizeof(s->at_tx));
    memset(s->row_h, 0, sizeof(s->row_h));
    s->pseudo = false;
    s->lntp = false;
    s->interrupt = false;
    s->end_of_data = 0;
    if (s->comment)
    {
        span_free(s->comment);
        s->comment = NULL;
    }
    s->comment_len = 0;
    s->comment_progress = 0;
    s->compressed_image_size = 0;

    t81_t82_arith_decode_restart(&s->s, false);
    s->s.nopadding = s->options & T85_VLENGTH;

    s->buf_len = 0;
    s->buf_needed = 2;
    s->x = 0;
    s->y = 0;
    s->i = 0;
    s->pseudo = true;
    s->at_moves = 0;
    s->tx = 0;
    s->lntp = true;
    s->p[0] = 0;
    s->p[1] = -1;
    s->p[2] = -1;
    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(logging_state_t *) t85_decode_get_logging_state(t85_decode_state_t *s)
{
    return &s->logging;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t85_decode_restart(t85_decode_state_t *s)
{
    s->xd = 0;
    s->yd = 0;
    s->l0 = 0;
    s->mx = 0;
    s->bytes_per_row = 0;
    s->tx = 0;
    s->bie_len = 0;
    memset(s->buffer, 0, sizeof(s->buffer));
    s->buf_len = 0;
    s->buf_needed = 0;
    s->at_moves = 0;
    memset(s->at_row, 0, sizeof(s->at_row));
    memset(s->at_tx, 0, sizeof(s->at_tx));
    memset(s->row_h, 0, sizeof(s->row_h));
    s->pseudo = false;
    s->lntp = false;
    s->interrupt = false;
    s->end_of_data = 0;
    if (s->comment)
    {
        span_free(s->comment);
        s->comment = NULL;
    }
    s->comment_len = 0;
    s->comment_progress = 0;
    s->compressed_image_size = 0;

    t81_t82_arith_decode_restart(&s->s, false);

    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(t85_decode_state_t *) t85_decode_init(t85_decode_state_t *s,
                                                   t4_row_write_handler_t handler,
                                                   void *user_data)
{
    if (s == NULL)
    {
        if ((s = (t85_decode_state_t *) span_alloc(sizeof(*s))) == NULL)
            return NULL;
    }
    memset(s, 0, sizeof(*s));
    span_log_init(&s->logging, SPAN_LOG_NONE, NULL);
    span_log_set_protocol(&s->logging, "T.85");

    s->row_write_handler = handler;
    s->row_write_user_data = user_data;

    s->min_bit_planes = 1;
    s->max_bit_planes = 1;

    s->max_xd = 0;
    s->max_yd = 0;

    t81_t82_arith_decode_init(&s->s);
    t85_decode_restart(s);
    return s;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t85_decode_release(t85_decode_state_t *s)
{
    if (s->row_buf)
    {
        span_free(s->row_buf);
        s->row_buf = NULL;
    }
    if (s->comment)
    {
        span_free(s->comment);
        s->comment = NULL;
    }
    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t85_decode_free(t85_decode_state_t *s)
{
    int ret;

    ret = t85_decode_release(s);
    span_free(s);
    return ret;
}
/*- End of function --------------------------------------------------------*/
/*- End of file ------------------------------------------------------------*/
