/*
 * SpanDSP - a series of DSP components for telephony
 *
 * t85_tests.c - ITU T.85 FAX image compression and decompression tests
 *
 * 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 General Public License version 2, 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * These tests are based on code from Markus Kuhn's jbigkit. See
 * http://www.cl.cam.ac.uk/~mgk25/
 *
 * jbigkit is GPL2 licenced. This file is also GPL2 licenced, and our
 * T.85 code is LGPL2.1 licenced. There are no licence incompatibilities
 * in this reuse of Markus's work.
 */

/*! \file */

/*! \page t85_tests_page T.85 image compress and decompression tests
\section t85_tests_page_sec_1 What does it do
These tests exercise the image compression and decompression methods defined
in ITU specifications T.85.
*/

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

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>

#define SPANDSP_EXPOSE_INTERNAL_STRUCTURES

#include "spandsp.h"

#define TESTBUF_SIZE        400000
#define TEST_IMAGE_SIZE     (1951*1960/8)

uint8_t testbuf[TESTBUF_SIZE];
uint8_t test_image[TEST_IMAGE_SIZE];

size_t testbuf_len;

int read_row = 0;
int write_row = 0;

int clip_to_row = 0;

static int row_read_handler(void *user_data, uint8_t buf[], size_t len)
{
    memcpy(buf, &test_image[len*read_row], len);
    if (clip_to_row  &&  read_row == clip_to_row)
    {
        clip_to_row = 0;
        return 0;
    }
    read_row++;
    return len;
}
/*- End of function --------------------------------------------------------*/

static int row_write_handler(void *user_data, const uint8_t buf[], size_t len)
{
    uint8_t *bitmap;

    bitmap = (uint8_t *) user_data;
    memcpy(&bitmap[len*write_row], buf, len);
    //printf("Write row %d\n", write_row);
    write_row++;
    return 0;
}
/*- End of function --------------------------------------------------------*/

static int comment_handler(void *user_data, const uint8_t buf[], size_t len)
{
    if (buf)
        printf("Comment (%lu): %s\n", (unsigned long int) len, buf);
    else
        printf("Comment (%lu): ---\n", (unsigned long int) len);
    return 0;
}
/*- End of function --------------------------------------------------------*/

static void create_test_image(uint8_t *pic)
{
    int i;
    int j;
    uint32_t sum;
    uint32_t prsg;
    uint32_t repeat[8];
    uint8_t *p;

    /* Cook up the test image defined in T.82/7.2.1. This image is 1960 x 1951
       pixels, and occupies a single plane (which it has to for T.85). */
    memset(pic, 0, TEST_IMAGE_SIZE);
    p = pic;
    prsg = 1;
    for (i = 0;  i < 1951;  i++)
    {
        for (j = 0;  j < 1960;  j++)
        {
            if (i >= 192)
            {
                if (i < 1023  ||  (j & (3 << 3)) == 0)
                {
                    sum = (prsg & 1)
                        + ((prsg >> 2) & 1)
                        + ((prsg >> 11) & 1)
                        + ((prsg >> 15) & 1);
                    prsg = (prsg << 1) + (sum & 1);
                    if ((prsg & 3) == 0)
                    {
                        *p |= (1 << (7 - (j & 7)));
                        repeat[j & 7] = 1;
                    }
                    else
                    {
                        repeat[j & 7] = 0;
                    }
                }
                else
                {
                    if (repeat[j & 7])
                        *p |= 1 << (7 - (j & 7));
                }
            }
            if ((j & 7) == 7)
                ++p;
        }
    }

    /* Verify the test image has been generated OK, by checking the number of set pixels */
    sum = 0;
    for (i = 0;  i < TEST_IMAGE_SIZE;  i++)
    {
        for (j = 0;  j < 8;  j++)
            sum += ((pic[i] >> j) & 1);
    }
    if (sum != 861965)
    {
        printf("WARNING: Test image has %" PRIu32 " foreground pixels. There should be 861965.\n",
               sum);
    }
}
/*- End of function --------------------------------------------------------*/

/* Perform a test cycle, as defined in T.82/7, with one set of parameters. */
static int test_cycle(const char *test_id,
                      const uint8_t *image,
                      uint32_t width,
                      uint32_t height,
                      uint32_t l0,
                      int mx,
                      int options,
                      int optionsx,
                      const uint8_t *comment,
                      size_t correct_length)
{
    t85_encode_state_t *t85_enc;
    t85_decode_state_t *t85_dec;
    long int l;
    size_t image_size;
    int result;
    int len;
    int max_len;
    size_t bytes_per_row;
    size_t cnt_a;
    size_t cnt_b;
    uint8_t *decoded_image;

    printf("%s: TPBON=%d, LRLTWO=%d, Mx=%d, L0=%" PRIu32 "\n",
           test_id,
           (options & T85_TPBON)  ?  1  :  0,
           (options & T85_LRLTWO)  ?  1  :  0,
           mx,
           l0);

    printf("%s.1: Encode\n", test_id);
    bytes_per_row = (width + 7)/8;
    image_size = bytes_per_row*height;

    if ((optionsx & T85_VLENGTH))
    {
        t85_enc = t85_encode_init(NULL, width, height + 10, row_read_handler, NULL);
        clip_to_row = height;
    }
    else
    {
        t85_enc = t85_encode_init(NULL, width, height, row_read_handler, NULL);
        clip_to_row = 0;
    }
    read_row = 0;
    t85_encode_set_options(t85_enc, l0, mx, options);
    /* A comment inserted here should always succeed. The later one, inserted some way
       down the image, will only succeed if a new chunk is started afterwards. */
    if (comment)
        t85_encode_comment(t85_enc, comment, strlen((const char *) comment) + 1);

    testbuf_len = 0;
    max_len = 100;
    while ((len = t85_encode_get(t85_enc, &testbuf[testbuf_len], max_len)) > 0)
    {
        testbuf_len += len;
        max_len = 100;
        if (testbuf_len + 100 > TESTBUF_SIZE)
            max_len = TESTBUF_SIZE - testbuf_len;
        if (comment  &&  testbuf_len == 1000)
            t85_encode_comment(t85_enc, comment, strlen((const char *) comment) + 1);
    }
    printf("Encoded BIE has %lu bytes\n", (unsigned long int) testbuf_len);
    if (correct_length > 0)
    {
        if (testbuf_len != correct_length)
        {
            printf("Incorrect encoded length. Should have been %lu\n", (unsigned long int) correct_length);
            printf("Test failed\n");
            exit(2);
        }
        printf("Test passed\n");
    }

    cnt_a = t85_encode_get_compressed_image_size(t85_enc);
    t85_encode_free(t85_enc);

    printf("%s.2: Decode in one big chunk\n", test_id);
    if ((decoded_image = (uint8_t *) malloc(image_size)) == NULL)
    {
        fprintf(stderr, "Out of memory!\n");
        exit(2);
    }
    t85_dec = t85_decode_init(NULL, row_write_handler, decoded_image);
    if (comment  &&  comment[0] != 'X')
        t85_decode_set_comment_handler(t85_dec, 1000, comment_handler, NULL);
    write_row = 0;
    result = t85_decode_put(t85_dec, testbuf, testbuf_len);
    if (result == T4_DECODE_MORE_DATA)
        result = t85_decode_put(t85_dec, NULL, 0);
    cnt_b = t85_decode_get_compressed_image_size(t85_dec);
    if (cnt_a != cnt_b  ||  cnt_a != testbuf_len*8  ||  result != T4_DECODE_OK)
    {
        printf("Decode result %d\n", result);
        printf("%ld/%ld bits of %ld bits of BIE read. %lu lines decoded.\n",
               (long int) cnt_a,
               (long int) cnt_b,
               (unsigned long int) testbuf_len*8,
               (unsigned long int) t85_dec->y);
        printf("Test failed\n");
        exit(2);
    }
    if (memcmp(decoded_image, image, image_size))
    {
        printf("Image mismatch\n");
        printf("Test failed\n");
        exit(2);
    }
    free(decoded_image);
    t85_decode_free(t85_dec);
    printf("Test passed\n");

    printf("%s.3: Decode byte by byte\n", test_id);
    if ((decoded_image = (uint8_t *) malloc(image_size)) == NULL)
    {
        fprintf(stderr, "Out of memory!\n");
        exit(2);
    }
    t85_dec = t85_decode_init(NULL, row_write_handler, decoded_image);
    if (comment  &&  comment[0] != 'X')
        t85_decode_set_comment_handler(t85_dec, 1000, comment_handler, NULL);
    write_row = 0;
    result = T4_DECODE_MORE_DATA;
    for (l = 0;  l < testbuf_len;  l++)
    {
        result = t85_decode_put(t85_dec, &testbuf[l], 1);
        if (result != T4_DECODE_MORE_DATA)
        {
            l++;
            break;
        }
    }
    if (result == T4_DECODE_MORE_DATA)
        result = t85_decode_put(t85_dec, NULL, 0);
    if (l != testbuf_len  ||  result != T4_DECODE_OK)
    {
        printf("Decode result %d\n", result);
        printf("%ld bytes of %ld bytes of BIE read. %lu lines decoded.\n",
               (long int) l,
               (unsigned long int) testbuf_len,
               (unsigned long int) t85_dec->y);
        printf("Test failed\n");
        exit(2);
    }
    if (memcmp(decoded_image, image, image_size))
    {
        printf("Image mismatch\n");
        printf("Test failed\n");
        exit(2);
    }
    free(decoded_image);
    t85_decode_free(t85_dec);
    printf("Test passed\n");

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

int main(int argc, char **argv)
{
    printf("T.85 JBIG for FAX encoder and decoder tests, from ITU-T T.82\n\n");

    printf("Generating the test image from T.82...\n");
    create_test_image(test_image);

    /* Run through the tests in T.82/7.2, which are applicable to T.85 */
    test_cycle("1", test_image, 1960, 1951, 1951, 0, 0,          0, NULL, 317384);
    test_cycle("2", test_image, 1960, 1951, 1951, 0, T85_LRLTWO, 0, NULL, 317132);
    test_cycle("3", test_image, 1960, 1951,  128, 8, T85_TPBON,  0, NULL, 253653);
    /* Again with a comment added and handled */
    test_cycle("4", test_image, 1960, 1951, 1951, 0, 0,          0, (const uint8_t *) "Comment 4", 317384 + 16);
    test_cycle("5", test_image, 1960, 1951, 1951, 0, T85_LRLTWO, 0, (const uint8_t *) "Comment 5", 317132 + 16);
    test_cycle("6", test_image, 1960, 1951,  128, 8, T85_TPBON,  0, (const uint8_t *) "Comment 6", 253653 + 2*16);
    /* Again with a comment added, but not handled */
    test_cycle("7", test_image, 1960, 1951, 1951, 0, 0,          0, (const uint8_t *) "Xomment 7", 317384 + 16);
    test_cycle("8", test_image, 1960, 1951, 1951, 0, T85_LRLTWO, 0, (const uint8_t *) "Xomment 8", 317132 + 16);
    test_cycle("9", test_image, 1960, 1951,  128, 8, T85_TPBON,  0, (const uint8_t *) "Xomment 9", 253653 + 2*16);
    /* Again with the image variable length and prematurely terminated */
    test_cycle("10", test_image, 1960, 1951, 1951, 0, T85_VLENGTH,              T85_VLENGTH, NULL, 317384 + 8);
    test_cycle("11", test_image, 1960, 1951, 1951, 0, T85_VLENGTH | T85_LRLTWO, T85_VLENGTH, NULL, 317132 + 8);
    test_cycle("12", test_image, 1960, 1951,  128, 8, T85_VLENGTH | T85_TPBON,  T85_VLENGTH, NULL, 253653 + 8);
    /* Again with the image variable length but not prematurely terminated */
    test_cycle("13", test_image, 1960, 1951, 1951, 0, T85_VLENGTH,              0, NULL, 317384);
    test_cycle("14", test_image, 1960, 1951, 1951, 0, T85_VLENGTH | T85_LRLTWO, 0, NULL, 317132);
    test_cycle("15", test_image, 1960, 1951,  128, 8, T85_VLENGTH | T85_TPBON,  0, NULL, 253653);

    printf("Tests passed\n");

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