/*
 * SpanDSP - a series of DSP components for telephony
 *
 * t81_t82_arith_coding.c - ITU T.81 and T.82 QM-coder arithmetic encoding
 *                          and decoding
 *
 * Written by Steve Underwood <steveu@coppice.org>
 *
 * Copyright (C) 2009 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>
#if defined(HAVE_STDBOOL_H)
#include <stdbool.h>
#else
#include "spandsp/stdbool.h"
#endif

#include "spandsp/telephony.h"
#include "spandsp/alloc.h"
#include "spandsp/t81_t82_arith_coding.h"

#include "spandsp/private/t81_t82_arith_coding.h"

/* T.82 defines the QM-coder at a level very close to actual code. Therefore
   this file closely mirrors the routine names, variable names, and flow
   described in T.82. QM-Coder is supposed to be the same in some other image
   compression schemes, such as T.81. However, this code has not been checked
   to see if it follows the letter of any spec other than T.82. */

/* Code bytes which must trigger stuffing */
enum
{
    T81_T82_STUFF = 0x00,
    T81_T82_ESC = 0xFF
};

/* This table is from T.82 table 24 - Probability estimation table */
static const struct probability_estimation_s
{
    uint16_t lsz;
    uint8_t nlps;   /* The SWITCH bit is packed into the top of this byte */
    uint8_t nmps;
} prob[113] =
{
    {0x5A1D,   1 + 128,   1},
    {0x2586,  14,         2},
    {0x1114,  16,         3},
    {0x080B,  18,         4},
    {0x03D8,  20,         5},
    {0x01DA,  23,         6},
    {0x00E5,  25,         7},
    {0x006F,  28,         8},
    {0x0036,  30,         9},
    {0x001A,  33,        10},
    {0x000D,  35,        11},
    {0x0006,   9,        12},
    {0x0003,  10,        13},
    {0x0001,  12,        13},
    {0x5A7F,  15 + 128,  15},
    {0x3F25,  36,        16},
    {0x2CF2,  38,        17},
    {0x207C,  39,        18},
    {0x17B9,  40,        19},
    {0x1182,  42,        20},
    {0x0CEF,  43,        21},
    {0x09A1,  45,        22},
    {0x072F,  46,        23},
    {0x055C,  48,        24},
    {0x0406,  49,        25},
    {0x0303,  51,        26},
    {0x0240,  52,        27},
    {0x01B1,  54,        28},
    {0x0144,  56,        29},
    {0x00F5,  57,        30},
    {0x00B7,  59,        31},
    {0x008A,  60,        32},
    {0x0068,  62,        33},
    {0x004E,  63,        34},
    {0x003B,  32,        35},
    {0x002C,  33,         9},
    {0x5AE1,  37 + 128,  37},
    {0x484C,  64,        38},
    {0x3A0D,  65,        39},
    {0x2EF1,  67,        40},
    {0x261F,  68,        41},
    {0x1F33,  69,        42},
    {0x19A8,  70,        43},
    {0x1518,  72,        44},
    {0x1177,  73,        45},
    {0x0E74,  74,        46},
    {0x0BFB,  75,        47},
    {0x09F8,  77,        48},
    {0x0861,  78,        49},
    {0x0706,  79,        50},
    {0x05CD,  48,        51},
    {0x04DE,  50,        52},
    {0x040F,  50,        53},
    {0x0363,  51,        54},
    {0x02D4,  52,        55},
    {0x025C,  53,        56},
    {0x01F8,  54,        57},
    {0x01A4,  55,        58},
    {0x0160,  56,        59},
    {0x0125,  57,        60},
    {0x00F6,  58,        61},
    {0x00CB,  59,        62},
    {0x00AB,  61,        63},
    {0x008F,  61,        32},
    {0x5B12,  65 + 128,  65},
    {0x4D04,  80,        66},
    {0x412C,  81,        67},
    {0x37D8,  82,        68},
    {0x2FE8,  83,        69},
    {0x293C,  84,        70},
    {0x2379,  86,        71},
    {0x1EDF,  87,        72},
    {0x1AA9,  87,        73},
    {0x174E,  72,        74},
    {0x1424,  72,        75},
    {0x119C,  74,        76},
    {0x0F6B,  74,        77},
    {0x0D51,  75,        78},
    {0x0BB6,  77,        79},
    {0x0A40,  77,        48},
    {0x5832,  80 + 128,  81},
    {0x4D1C,  88,        82},
    {0x438E,  89,        83},
    {0x3BDD,  90,        84},
    {0x34EE,  91,        85},
    {0x2EAE,  92,        86},
    {0x299A,  93,        87},
    {0x2516,  86,        71},
    {0x5570,  88 + 128,  89},
    {0x4CA9,  95,        90},
    {0x44D9,  96,        91},
    {0x3E22,  97,        92},
    {0x3824,  99,        93},
    {0x32B4,  99,        94},
    {0x2E17,  93,        86},
    {0x56A8,  95 + 128,  96},
    {0x4F46, 101,        97},
    {0x47E5, 102,        98},
    {0x41CF, 103,        99},
    {0x3C3D, 104,       100},
    {0x375E,  99,        93},
    {0x5231, 105,       102},
    {0x4C0F, 106,       103},
    {0x4639, 107,       104},
    {0x415E, 103,        99},
    {0x5627, 105 + 128, 106},
    {0x50E7, 108,       107},
    {0x4B85, 109,       103},
    {0x5597, 110,       109},
    {0x504F, 111,       107},
    {0x5A10, 110 + 128, 111},
    {0x5522, 112,       109},
    {0x59EB, 112 + 128, 111}
};

static __inline__ void output_stuffed_byte(t81_t82_arith_encode_state_t *s, int byte)
{
    s->output_byte_handler(s->user_data, byte);
    if (byte == T81_T82_ESC)
        s->output_byte_handler(s->user_data, T81_T82_STUFF);
    /*endif*/
}
/*- End of function --------------------------------------------------------*/

static __inline__ void byteout(t81_t82_arith_encode_state_t *s)
{
    uint32_t temp;

    /* T.30 figure 26 - BYTEOUT */
    temp = s->c >> 19;
    if (temp > 0xFF)
    {
        if (s->buffer >= 0)
            output_stuffed_byte(s, s->buffer + 1);
        /*endif*/
        while (s->sc)
        {
            s->output_byte_handler(s->user_data, 0x00);
            s->sc--;
        }
        /*endwhile*/
        s->buffer = temp & 0xFF;
    }
    else if (temp == 0xFF)
    {
        s->sc++;
    }
    else
    {
        if (s->buffer >= 0)
            output_stuffed_byte(s, s->buffer);
        /*endif*/
        while (s->sc)
        {
            output_stuffed_byte(s, T81_T82_ESC);
            s->sc--;
        }
        /*endwhile*/
        s->buffer = temp;
    }
    /*endif*/
    s->c &= 0x7FFFF;
    s->ct = 8;
}
/*- End of function --------------------------------------------------------*/

static __inline__ void renorme(t81_t82_arith_encode_state_t *s)
{
    /* T.82 figure 25 - RENORME */
    do
    {
        s->a <<= 1;
        s->c <<= 1;
        s->ct--;
        if (s->ct == 0)
            byteout(s);
        /*endif*/
    }
    while (s->a < 0x8000);
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(void) t81_t82_arith_encode(t81_t82_arith_encode_state_t *s, int cx, int pix)
{
    uint32_t ss;

    /* T.82 figure 22 - ENCODE */
    ss = s->st[cx] & 0x7F;
    if (((pix << 7) ^ s->st[cx]) & 0x80)
    {
        /* T.82 figure 23 - CODELPS */
        s->a -= prob[ss].lsz;
        if (s->a >= prob[ss].lsz)
        {
            s->c += s->a;
            s->a = prob[ss].lsz;
        }
        /*endif*/
        s->st[cx] = (s->st[cx] & 0x80) ^ prob[ss].nlps;
        renorme(s);
    }
    else
    {
        /* T.82 figure 24 - CODEMPS */
        s->a -= prob[ss].lsz;
        if (s->a < 0x8000)
        {
            if (s->a < prob[ss].lsz)
            {
                s->c += s->a;
                s->a = prob[ss].lsz;
            }
            /*endif*/
            s->st[cx] = (s->st[cx] & 0x80) | prob[ss].nmps;
            renorme(s);
        }
        /*endif*/
    }
    /*endif*/
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(void) t81_t82_arith_encode_flush(t81_t82_arith_encode_state_t *s)
{
    uint32_t temp;

    /* T.82 figure 28 - FLUSH */
    /* T.82 figure 29 - CLEARBITS */
    temp = (s->c + s->a - 1) & 0xFFFF0000;
    s->c = (temp < s->c)  ?  (temp + 0x8000)  :  temp;
    /* T.82 figure 30 - FINALWRITES */
    s->c <<= s->ct;
    if ((s->c > 0x7FFFFFF))
    {
        if (s->buffer >= 0)
            output_stuffed_byte(s, s->buffer + 1);
        /*endif*/
        /* Only output 0x00 bytes if something non-0x00 will follow */
        if ((s->c & 0x7FFF800))
        {
            while (s->sc)
            {
                output_stuffed_byte(s, 0x00);
                s->sc--;
            }
            /*endwhile*/
        }
        /*endif*/
    }
    else
    {
        /* The next bit says s->buffer + 1 in T.82, but that makes no sense. It doesn't
           agree with how we code things away from the flush condition, and it gives
           answers which don't seem to match other JBIG coders. */
        if (s->buffer >= 0)
            output_stuffed_byte(s, s->buffer);
        /*endif*/
        while (s->sc)
        {
            output_stuffed_byte(s, 0xFF);
            s->sc--;
        }
        /*endwhile*/
    }
    /*endif*/
    /* Only output final bytes if they are not 0x00 */
    if ((s->c & 0x7FFF800))
    {
        output_stuffed_byte(s, (s->c >> 19) & 0xFF);
        if ((s->c & 0x7F800))
            output_stuffed_byte(s, (s->c >> 11) & 0xFF);
        /*endif*/
    }
    /*endif*/
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t81_t82_arith_encode_restart(t81_t82_arith_encode_state_t *s, int reuse_st)
{
    /* T.82 figure 27 - INITENC */
    if (!reuse_st)
        memset(s->st, 0, sizeof(s->st));
    /*endif*/
    s->c = 0;
    s->a = 0x10000;
    s->sc = 0;
    s->ct = 11;
    s->buffer = -1;
    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(t81_t82_arith_encode_state_t *) t81_t82_arith_encode_init(t81_t82_arith_encode_state_t *s,
                                                                       void (*output_byte_handler)(void *, int),
                                                                       void *user_data)
{
    if (s == NULL)
    {
        if ((s = (t81_t82_arith_encode_state_t *) span_alloc(sizeof(*s))) == NULL)
            return NULL;
        /*endif*/
    }
    /*endif*/
    memset(s, 0, sizeof(*s));
    s->output_byte_handler = output_byte_handler;
    s->user_data = user_data;

    t81_t82_arith_encode_restart(s, false);
    return s;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t81_t82_arith_encode_release(t81_t82_arith_encode_state_t *s)
{
    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t81_t82_arith_encode_free(t81_t82_arith_encode_state_t *s)
{
    int ret;

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

SPAN_DECLARE(int) t81_t82_arith_decode(t81_t82_arith_decode_state_t *s, int cx)
{
    uint32_t ss;
    int pix;

    /* T.82 figure 35 - RENORMD */
    while (s->a < 0x8000  ||  s->startup)
    {
        while (s->ct <= 8  &&  s->ct >= 0)
        {
            /* First we can move a new byte into s->c */
            if (s->pscd_ptr >= s->pscd_end)
                return -1;
            if (s->pscd_ptr[0] == T81_T82_ESC)
            {
                if (s->pscd_ptr + 1 >= s->pscd_end)
                    return -1;
                if (s->pscd_ptr[1] == T81_T82_STUFF)
                {
                    s->c |= (0xFF << (8 - s->ct));
                    s->ct += 8;
                    s->pscd_ptr += 2;
                }
                else
                {
                    /* Start padding with zero bytes */
                    s->ct = -1;
                    if (s->nopadding)
                    {
                        /* Subsequent symbols might depend on zero padding */
                        s->nopadding = false;
                        return -2;
                    }
                }
            }
            else
            {
                s->c |= (int32_t) *(s->pscd_ptr++) << (8 - s->ct);
                s->ct += 8;
            }
            /*endif*/
        }
        /*endwhile*/
        s->a <<= 1;
        s->c <<= 1;
        if (s->ct >= 0)
            s->ct--;
        /*endif*/
        if (s->a == 0x10000)
            s->startup = false;
        /*endif*/
    }
    /*endwhile*/

    /* T.82 figure 32 - DECODE */
    ss = s->st[cx] & 0x7F;
    if ((s->c >> 16) >= (s->a -= prob[ss].lsz))
    {
        /* T.82 figure 33 - LPS_EXCHANGE */
        if (s->a < prob[ss].lsz)
        {
            s->c -= (s->a << 16);
            s->a = prob[ss].lsz;
            pix = s->st[cx] >> 7;
            s->st[cx] = (s->st[cx] & 0x80) | prob[ss].nmps;
        }
        else
        {
            s->c -= (s->a << 16);
            s->a = prob[ss].lsz;
            pix = 1 - (s->st[cx] >> 7);
            s->st[cx] = (s->st[cx]& 0x80) ^ prob[ss].nlps;
        }
        /*endif*/
    }
    else
    {
        if (s->a < 0x8000)
        {
            /* T.82 figure 34 - MPS_EXCHANGE */
            if (s->a < prob[ss].lsz)
            {
                pix = 1 - (s->st[cx] >> 7);
                s->st[cx] = (s->st[cx] & 0x80) ^ prob[ss].nlps;
            }
            else
            {
                pix = s->st[cx] >> 7;
                s->st[cx] = (s->st[cx] & 0x80) | prob[ss].nmps;
            }
            /*endif*/
        }
        else
        {
            pix = s->st[cx] >> 7;
        }
        /*endif*/
    }
    /*endif*/

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

SPAN_DECLARE(int) t81_t82_arith_decode_restart(t81_t82_arith_decode_state_t *s, int reuse_st)
{
    if (!reuse_st)
        memset(s->st, 0, sizeof(s->st));
    /*endif*/
    s->c = 0;
    s->a = 1;
    s->ct = 0;
    s->startup = true;
    s->nopadding = false;
    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(t81_t82_arith_decode_state_t *) t81_t82_arith_decode_init(t81_t82_arith_decode_state_t *s)
{
    if (s == NULL)
    {
        if ((s = (t81_t82_arith_decode_state_t *) span_alloc(sizeof(*s))) == NULL)
            return NULL;
    }
    memset(s, 0, sizeof(*s));

    t81_t82_arith_decode_restart(s, false);
    return s;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t81_t82_arith_decode_release(t81_t82_arith_decode_state_t *s)
{
    return 0;
}
/*- End of function --------------------------------------------------------*/

SPAN_DECLARE(int) t81_t82_arith_decode_free(t81_t82_arith_decode_state_t *s)
{
    int ret;

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